0
# Cluster Singleton
1
2
The Cluster Singleton pattern ensures that exactly one instance of a particular actor is running somewhere in the cluster at any point in time. This is useful for central coordination, resource management, or any scenario where you need exactly-one semantics in a distributed system.
3
4
## Capabilities
5
6
### ClusterSingletonManager
7
8
Manages a singleton actor instance, ensuring it runs on exactly one node with automatic failover.
9
10
```scala { .api }
11
/**
12
* Manages singleton actor instance among all cluster nodes or a group
13
* of nodes tagged with a specific role. At most one singleton instance
14
* is running at any point in time.
15
*
16
* @param singletonProps Props of the singleton actor instance
17
* @param terminationMessage Message sent to singleton during hand-over
18
* @param settings Configuration settings
19
*/
20
class ClusterSingletonManager(
21
singletonProps: Props,
22
terminationMessage: Any,
23
settings: ClusterSingletonManagerSettings
24
) extends Actor
25
26
object ClusterSingletonManager {
27
/**
28
* Scala API: Factory method for ClusterSingletonManager Props
29
*/
30
def props(
31
singletonProps: Props,
32
terminationMessage: Any,
33
settings: ClusterSingletonManagerSettings
34
): Props
35
}
36
```
37
38
**Usage Example:**
39
40
```scala
41
import akka.actor.{ActorSystem, Props, PoisonPill}
42
import akka.cluster.singleton.{ClusterSingletonManager, ClusterSingletonManagerSettings}
43
44
implicit val system: ActorSystem = ActorSystem("cluster-system")
45
46
// Create singleton manager
47
val singletonManager = system.actorOf(
48
ClusterSingletonManager.props(
49
singletonProps = Props[DatabaseCoordinator](),
50
terminationMessage = PoisonPill,
51
settings = ClusterSingletonManagerSettings(system)
52
.withRole("backend")
53
.withSingletonName("db-coordinator")
54
),
55
name = "database-singleton"
56
)
57
```
58
59
### ClusterSingletonProxy
60
61
Provides a proxy to communicate with the singleton actor, handling location transparency and buffering.
62
63
```scala { .api }
64
/**
65
* Proxy that forwards messages to the singleton actor.
66
* Can be started on any node to provide location-transparent access.
67
* Buffers messages when singleton is unavailable during hand-over.
68
*
69
* @param singletonManagerPath Logical path to the singleton manager
70
* @param settings Configuration settings
71
*/
72
class ClusterSingletonProxy(
73
singletonManagerPath: String,
74
settings: ClusterSingletonProxySettings
75
) extends Actor
76
77
object ClusterSingletonProxy {
78
/**
79
* Scala API: Factory method for ClusterSingletonProxy Props
80
*
81
* @param singletonManagerPath The logical path of the singleton manager
82
* @param settings Configuration settings
83
*/
84
def props(
85
singletonManagerPath: String,
86
settings: ClusterSingletonProxySettings
87
): Props
88
}
89
```
90
91
**Usage Example:**
92
93
```scala
94
import akka.cluster.singleton.{ClusterSingletonProxy, ClusterSingletonProxySettings}
95
96
// Create proxy to communicate with singleton
97
val singletonProxy = system.actorOf(
98
ClusterSingletonProxy.props(
99
singletonManagerPath = "/user/database-singleton",
100
settings = ClusterSingletonProxySettings(system)
101
.withRole("backend")
102
.withSingletonName("db-coordinator")
103
.withBufferSize(1000)
104
),
105
name = "database-proxy"
106
)
107
108
// Send messages to singleton via proxy
109
singletonProxy ! "process-batch"
110
singletonProxy ! GetStatus
111
```
112
113
### ClusterSingletonManagerSettings
114
115
Configuration for the singleton manager behavior.
116
117
```scala { .api }
118
/**
119
* Configuration settings for ClusterSingletonManager
120
*
121
* @param singletonName The actor name of the child singleton actor
122
* @param role Singleton among nodes tagged with specified role. None means all nodes
123
* @param removalMargin Margin until singleton is created in surviving partition after split
124
* @param handOverRetryInterval Retry interval for hand-over requests
125
* @param leaseSettings Optional lease configuration for singleton coordination
126
*/
127
final class ClusterSingletonManagerSettings(
128
val singletonName: String,
129
val role: Option[String],
130
val removalMargin: FiniteDuration,
131
val handOverRetryInterval: FiniteDuration,
132
val leaseSettings: Option[LeaseUsageSettings]
133
) extends NoSerializationVerificationNeeded
134
135
object ClusterSingletonManagerSettings {
136
/**
137
* Create settings from the default configuration akka.cluster.singleton
138
*/
139
def apply(system: ActorSystem): ClusterSingletonManagerSettings
140
141
/**
142
* Create settings from a configuration with the same layout as
143
* the default configuration akka.cluster.singleton
144
*/
145
def apply(config: Config): ClusterSingletonManagerSettings
146
147
/**
148
* Java API: Create settings from the default configuration
149
*/
150
def create(system: ActorSystem): ClusterSingletonManagerSettings
151
152
/**
153
* Java API: Create settings from configuration
154
*/
155
def create(config: Config): ClusterSingletonManagerSettings
156
}
157
```
158
159
**Settings Methods:**
160
161
```scala { .api }
162
// Configuration methods for ClusterSingletonManagerSettings
163
def withSingletonName(name: String): ClusterSingletonManagerSettings
164
def withRole(role: String): ClusterSingletonManagerSettings
165
def withRole(role: Option[String]): ClusterSingletonManagerSettings
166
def withRemovalMargin(removalMargin: FiniteDuration): ClusterSingletonManagerSettings
167
def withHandOverRetryInterval(retryInterval: FiniteDuration): ClusterSingletonManagerSettings
168
def withLeaseSettings(leaseSettings: LeaseUsageSettings): ClusterSingletonManagerSettings
169
```
170
171
### ClusterSingletonProxySettings
172
173
Configuration for the singleton proxy behavior.
174
175
```scala { .api }
176
/**
177
* Configuration settings for ClusterSingletonProxy
178
*
179
* @param singletonName The actor name of the singleton actor started by ClusterSingletonManager
180
* @param role The role of cluster nodes where singleton can be deployed
181
* @param dataCenter The data center of cluster nodes where singleton is running
182
* @param singletonIdentificationInterval Interval at which proxy tries to resolve singleton
183
* @param bufferSize Number of messages to buffer when singleton location unknown
184
*/
185
final class ClusterSingletonProxySettings(
186
val singletonName: String,
187
val role: Option[String],
188
val dataCenter: Option[DataCenter],
189
val singletonIdentificationInterval: FiniteDuration,
190
val bufferSize: Int
191
) extends NoSerializationVerificationNeeded
192
193
object ClusterSingletonProxySettings {
194
/**
195
* Create settings from the default configuration akka.cluster.singleton-proxy
196
*/
197
def apply(system: ActorSystem): ClusterSingletonProxySettings
198
199
/**
200
* Create settings from configuration
201
*/
202
def apply(config: Config): ClusterSingletonProxySettings
203
204
/**
205
* Java API: Create settings from default configuration
206
*/
207
def create(system: ActorSystem): ClusterSingletonProxySettings
208
209
/**
210
* Java API: Create settings from configuration
211
*/
212
def create(config: Config): ClusterSingletonProxySettings
213
}
214
```
215
216
**Settings Methods:**
217
218
```scala { .api }
219
// Configuration methods for ClusterSingletonProxySettings
220
def withSingletonName(name: String): ClusterSingletonProxySettings
221
def withRole(role: String): ClusterSingletonProxySettings
222
def withRole(role: Option[String]): ClusterSingletonProxySettings
223
def withDataCenter(dataCenter: DataCenter): ClusterSingletonProxySettings
224
def withDataCenter(dataCenter: Option[DataCenter]): ClusterSingletonProxySettings
225
def withSingletonIdentificationInterval(interval: FiniteDuration): ClusterSingletonProxySettings
226
def withBufferSize(bufferSize: Int): ClusterSingletonProxySettings
227
```
228
229
### Exception Types
230
231
```scala { .api }
232
/**
233
* Thrown when a consistent state can't be determined within the defined retry limits.
234
* Parent supervisor should typically restart the actor.
235
*/
236
class ClusterSingletonManagerIsStuck(message: String) extends AkkaException
237
```
238
239
## Common Usage Patterns
240
241
### Database Coordinator Singleton
242
243
```scala
244
// Singleton that coordinates database operations
245
class DatabaseCoordinator extends Actor {
246
def receive = {
247
case "process-batch" =>
248
// Process batch operations
249
sender() ! "batch-processed"
250
case GetStatus =>
251
sender() ! DatabaseStatus("active", processedBatches = 42)
252
}
253
}
254
255
// Setup singleton manager and proxy
256
val manager = system.actorOf(
257
ClusterSingletonManager.props(
258
Props[DatabaseCoordinator](),
259
terminationMessage = "shutdown",
260
ClusterSingletonManagerSettings(system).withRole("backend")
261
),
262
"db-coordinator-singleton"
263
)
264
265
val proxy = system.actorOf(
266
ClusterSingletonProxy.props(
267
"/user/db-coordinator-singleton",
268
ClusterSingletonProxySettings(system).withRole("backend")
269
),
270
"db-coordinator-proxy"
271
)
272
```
273
274
### Leader Election Pattern
275
276
```scala
277
// Use singleton for leader election
278
class ClusterLeader extends Actor {
279
override def preStart(): Unit = {
280
super.preStart()
281
log.info("I am the leader!")
282
// Initialize leader-specific operations
283
}
284
285
def receive = {
286
case "election-status" => sender() ! "leader"
287
case LeaderTask(work) =>
288
// Only the leader processes these tasks
289
processLeaderWork(work)
290
}
291
}
292
```
293
294
## Types
295
296
```scala { .api }
297
// Required imports for singleton functionality
298
import akka.actor.{Actor, ActorRef, ActorSystem, Props}
299
import akka.cluster.ClusterSettings.DataCenter
300
import akka.coordination.lease.LeaseUsageSettings
301
import com.typesafe.config.Config
302
import scala.concurrent.duration.FiniteDuration
303
```