@@ -48,22 +48,48 @@ flowchart LR
48
48
49
49
## Usage
50
50
51
- Pulse dedicated worker pools are generally valuable when workers require
52
- state which depends on the jobs they perform.
53
-
54
- To illustrate, let's consider the scenario of a multitenant system that requires
55
- managing a collection of background tasks for each tenant. In this case,
56
- utilizing a Pulse worker pool proves to be highly beneficial. The system can
57
- create a dedicated worker pool and create one job per tenant, utilizing the
58
- unique tenant identifier as the job key. This approach ensures that only one
59
- worker handles the background task for a specific tenant at any given time. As
60
- new tenants are added or old ones are removed, jobs can be started or stopped
61
- accordingly. Similarly, workers can be added or removed based on performance
62
- requirements.
63
-
64
- Pulse dedicated worker pools are not needed when workers are stateless and can
65
- be scaled horizontally. In such cases, any standard load balancing solution can
66
- be used.
51
+ Job producer:
52
+ ``` go
53
+ rdb := redis.NewClient (&redis.Options {Addr: " localhost:6379" })
54
+ node , err := pool.AddNode (ctx, " example" , rdb, pool.WithClientOnly ())
55
+ if err != nil {
56
+ panic (err)
57
+ }
58
+ if err := node.DispatchJob (ctx, " key" , []byte (" payload" )); err != nil {
59
+ panic (err)
60
+ }
61
+ ```
62
+
63
+ Worker:
64
+ ``` go
65
+ rdb := redis.NewClient (&redis.Options {Addr: " localhost:6379" })
66
+ node , err := pool.AddNode (ctx, " example" , rdb)
67
+ if err != nil {
68
+ panic (err)
69
+ }
70
+ handler := &JobHandler{}
71
+ _ , err := node.AddWorker (context.Background (), handler)
72
+ if err != nil {
73
+ panic (err)
74
+ }
75
+ ```
76
+
77
+ Job handler:
78
+ ``` go
79
+ type JobHandler struct {
80
+ // ...
81
+ }
82
+
83
+ // Pulse calls this method to start a job that was assigned to this worker.
84
+ func (h *JobHandler ) Start (ctx context .Context , key string , payload []byte ) error {
85
+ // ...
86
+ }
87
+
88
+ // Pulse calls this method to stop a job that was assigned to this worker.
89
+ func (h *JobHandler ) Stop (ctx context .Context , key string ) error {
90
+ // ...
91
+ }
92
+ ```
67
93
68
94
### Creating A Pool
69
95
@@ -78,24 +104,28 @@ should be closed when it is no longer needed (see below).
78
104
The options are used to configure the pool node. The following options are
79
105
available:
80
106
107
+ * ` WithClientOnly ` - specifies that this node will only be used to dispatch jobs to
108
+ workers in other nodes, and will not run any workers itself.
81
109
* ` WithLogger ` - sets the logger to be used by the pool node.
82
- * ` WithWorkerTTL ` - sets the worker time-to-live (TTL) in seconds. The TTL
83
- defines the maximum delay between two health-checks before a worker is removed
84
- from the pool. The default value is 10 seconds.
85
- * ` WithPendingJobTTL ` - sets the pending job time-to-live (TTL) in seconds. The
86
- TTL defines the maximum delay between a worker picking up the job and
87
- successfully starting it. The default value is 20 seconds.
110
+ * ` WithWorkerTTL ` - sets the worker time-to-live (TTL). This is the maximum duration
111
+ a worker can go without sending a health check before it's considered inactive
112
+ and removed from the pool. If a worker doesn't report its status within this
113
+ time frame, it will be removed, allowing the pool to reassign its jobs to other
114
+ active workers. The default value is 30 seconds.
88
115
* ` WithWorkerShutdownTTL ` - specifies the maximum time to wait for a worker to
89
- shutdown gracefully. The default value is 2 minutes.
116
+ shutdown gracefully. This is the duration the pool will wait for a worker to
117
+ finish its current job and perform any cleanup operations before forcefully
118
+ terminating it. If the worker doesn't shut down within this time, it will be
119
+ forcefully stopped. The default value is 2 minutes.
90
120
* ` WithMaxQueuedJobs ` - sets the maximum number of jobs that can be queued
91
- before the pool starts rejecting new jobs. The default value is 1000.
92
- * ` WithClientOnly ` - specifies that the pool node should not starts
93
- background goroutines to manage the pool and thus not allow creating workers .
94
- This option is useful when the pool is used only to dispatch jobs to workers
95
- that are created in other nodes.
96
- * ` WithJobSinkBlockDuration ` - sets the max poll duration for new jobs. This
97
- value is mostly used by tests to accelerate the pool shutdown process . The
98
- default value is 5 seconds.
121
+ before the pool starts rejecting new jobs. This limit applies to the entire
122
+ pool across all nodes. When this limit is reached, any attempt to dispatch
123
+ new jobs will result in an error. The default value is 1000 jobs .
124
+ * ` WithAckGracePeriod ` - sets the grace period for job acknowledgment. If a
125
+ worker doesn't acknowledge starting a job within this duration, the job
126
+ becomes available for other workers to claim. This prevents jobs from being
127
+ stuck if a worker fails to start processing them . The default value is 20
128
+ seconds.
99
129
100
130
### Closing A Node
101
131
@@ -168,165 +198,3 @@ a list of jobs to be started and stopped.
168
198
169
199
` Schedule ` makes it possible to maintain a pool of jobs for example in a
170
200
multi-tenant system. See the [ examples] ( ../examples/pool ) for more details.
171
-
172
- ## Data Flows
173
-
174
- The following sections provide additional details on the internal data flows
175
- involved in creating and using a Pulse worker pool. They are provided for
176
- informational purposes only and are not required reading for simply using the
177
- package.
178
-
179
- ### Adding A New Job
180
-
181
- The following diagram illustrates the data flow involved in adding a new job to
182
- a Pulse worker pool:
183
-
184
- * The producer calls ` DispatchJob ` which adds an event to the pool job stream.
185
- * The pool job stream is read by the pool sink running in one of the pool nodes.
186
- The routing node records the event so it can ack it later and routes the event
187
- to the proper worker stream using a consistent hashing algorithm.
188
- * The dedicated worker stream is read by the worker which starts the job by
189
- calling the ` Start ` method on the worker job handler. Once ` Start ` returns
190
- successfully the worker sends an event back to the original pool node.
191
- * Upon getting the event, the pool node acks the job with the
192
- pool job stream and removes it from its pending jobs map.
193
-
194
-
195
- ``` mermaid
196
- %%{ init: { 'flowchart': { 'curve': 'basis' } } }%%
197
- %%{init: {'themeVariables': { 'edgeLabelBackground': '#7A7A7A'}}}%%
198
- flowchart TD
199
- subgraph w[Worker Node]
200
- r[Reader]
201
- u[User code]
202
- end
203
- subgraph rdb[Redis]
204
- js(["Pool Job Stream (shared)"])
205
- ws(["Worker Stream (dedicated)"])
206
- rs(["Routing Node Stream (dedicated)"])
207
- end
208
- subgraph p[Producer Node]
209
- pr[User code]
210
- no[Client Node]
211
- end
212
- subgraph ro[Routing Node]
213
- ps[Pool Sink]
214
- nr[Routing Node Reader]
215
- end
216
- pr --1. DispatchJob--> no
217
- no --2. Add Job--> js
218
- js --3. Job--> ps
219
- ps --4. Add Job--> ws
220
- ws --5. Job--> r
221
- r --6. Start Job--> u
222
- r --7. Add Ack--> rs
223
- rs --7. Ack--> nr
224
- nr --8. Ack Add Job Event--> js
225
-
226
- classDef userCode fill:#9A6D1F, stroke:#D9B871, stroke-width:2px, color:#FFF2CC;
227
- classDef producer fill:#2C5A9A, stroke:#6B96C1, stroke-width:2px, color:#CCE0FF;
228
- classDef redis fill:#25503C, stroke:#5E8E71, stroke-width:2px, color:#D6E9C6;
229
- classDef background fill:#7A7A7A, color:#F2F2F2;
230
-
231
- class pr,u userCode;
232
- class pj,js,ws,rs redis;
233
- class no,ps,r,c,nr producer;
234
- class p,w,rdb,ro background;
235
-
236
- linkStyle 0 stroke:#DDDDDD,color:#DDDDDD,stroke-width:3px;
237
- linkStyle 1 stroke:#DDDDDD,color:#DDDDDD,stroke-width:3px;
238
- linkStyle 2 stroke:#DDDDDD,color:#DDDDDD,stroke-width:3px;
239
- linkStyle 3 stroke:#DDDDDD,color:#DDDDDD,stroke-width:3px;
240
- linkStyle 4 stroke:#DDDDDD,color:#DDDDDD,stroke-width:3px;
241
- linkStyle 5 stroke:#DDDDDD,color:#DDDDDD,stroke-width:3px;
242
- linkStyle 6 stroke:#DDDDDD,color:#DDDDDD,stroke-width:3px;
243
- linkStyle 7 stroke:#DDDDDD,color:#DDDDDD,stroke-width:3px;
244
- ```
245
-
246
- The worker pool uses a job stream so that jobs that do not get acknowledged in time
247
- are automatically re-queued. This is useful in case of worker failure or
248
- network partitioning. The pool sink applies the consistent hashing algorithm
249
- to the job key to determine which worker stream the job should be added to. This
250
- ensures that unhealthy workers are properly ignored when requeuing jobs.
251
-
252
- ### Shutdown and Cleanup
253
-
254
- The following diagram illustrates the data flow involved in shutting down a
255
- Pulse worker pool:
256
-
257
- * The producer calls ` Shutdown ` which adds a shutdown event to the pool stream.
258
- * Upon receving the shutdown event the pool node closes the pool stream to avoid
259
- accepting new jobs and sets a flag in the pool shutdown replicated map.
260
- * The pool nodes get notified and stop accepting new jobs (` DispatchJob `
261
- returns an error if called).
262
- * The pool nodes add a stop event to the worker streams for all the workers
263
- they own.
264
- * Upon receiving the event, the workers remove themselves from the pool
265
- workers replicated map, destroy their stream and exit. Note that any job that
266
- was enqueued before the shutdown event still gets processed.
267
- * Once the workers have stopped, the producer that initiated the
268
- shutdown cleans up the pool resources (jobs sink, jobs stream, replicated maps)
269
- and the pool nodes exit.
270
-
271
- ``` mermaid
272
- %%{ init: { 'flowchart': { 'curve': 'basis' } } }%%
273
- %%{init: {'themeVariables': { 'edgeLabelBackground': '#7A7A7A'}}}%%
274
-
275
- flowchart TD
276
- subgraph pn1[Pool Node 1]
277
- u[User code]
278
- po1[Pool 1]
279
- w1[Worker 1]
280
- end
281
- subgraph pn2[Pool Node 2]
282
- po2[Pool 2]
283
- w2[Worker 2]
284
- end
285
- subgraph rdb[Redis]
286
- sr[(Shutdown <br/> Replicated Map)]
287
- wr[(Worker </br/> Replicated Map)]
288
- ws1(["Worker 1 Stream"])
289
- ws2(["Worker 2 Stream"])
290
- end
291
- u[User code] --1. Shutdown--> po1[Pool 1]
292
- po1 --2. Set Shutdown Flag--> sr[(Shutdown <br/> Replicated Map)]
293
- sr --3. Shutdown Flag--> po1
294
- sr --3. Shutdown Flag--> po2
295
- po1 --4. Add Stop--> ws1
296
- po2 --4. Add Stop--> ws2
297
- ws1 --5. Stop--> w1
298
- ws2 --5. Stop--> w2
299
- w1 --6. Remove Worker--> wr
300
- w2 --6. Remove Worker--> wr
301
- w1 --7. Delete--> ws1
302
- w2 --7. Delete--> ws2
303
- wr --8. Workers Empty--> po1
304
- po1 --9. Delete --> sr
305
- po1 --10. Delete --> wr
306
-
307
- classDef userCode fill:#9A6D1F, stroke:#D9B871, stroke-width:2px, color:#FFF2CC;
308
- classDef producer fill:#2C5A9A, stroke:#6B96C1, stroke-width:2px, color:#CCE0FF;
309
- classDef redis fill:#25503C, stroke:#5E8E71, stroke-width:2px, color:#D6E9C6;
310
- classDef background fill:#7A7A7A, color:#F2F2F2;
311
-
312
- class u userCode;
313
- class wr,sr,ws1,ws2 redis;
314
- class po1,po2,w1,w2 producer;
315
- class rdb,pn1,pn2 background;
316
-
317
- linkStyle 0 stroke:#DDDDDD,color:#DDDDDD,stroke-width:3px;
318
- linkStyle 1 stroke:#DDDDDD,color:#DDDDDD,stroke-width:3px;
319
- linkStyle 2 stroke:#DDDDDD,color:#DDDDDD,stroke-width:3px;
320
- linkStyle 3 stroke:#DDDDDD,color:#DDDDDD,stroke-width:3px;
321
- linkStyle 4 stroke:#DDDDDD,color:#DDDDDD,stroke-width:3px;
322
- linkStyle 5 stroke:#DDDDDD,color:#DDDDDD,stroke-width:3px;
323
- linkStyle 6 stroke:#DDDDDD,color:#DDDDDD,stroke-width:3px;
324
- linkStyle 7 stroke:#DDDDDD,color:#DDDDDD,stroke-width:3px;
325
- linkStyle 8 stroke:#DDDDDD,color:#DDDDDD,stroke-width:3px;
326
- linkStyle 9 stroke:#DDDDDD,color:#DDDDDD,stroke-width:3px;
327
- linkStyle 10 stroke:#FF8888,color:#FF8888,stroke-width:3px;
328
- linkStyle 11 stroke:#FF8888,color:#FF8888,stroke-width:3px;
329
- linkStyle 12 stroke:#DDDDDD,color:#DDDDDD,stroke-width:3px;
330
- linkStyle 13 stroke:#FF8888,color:#FF8888,stroke-width:3px;
331
- linkStyle 14 stroke:#FF8888,color:#FF8888,stroke-width:3px;
332
- ```
0 commit comments