@@ -69,6 +69,8 @@ type LauncherOptions struct {
69
69
// in progress at one time.
70
70
MaxInProgress uint `toml:"max_in_progress" commented:"true" comment:"maximum number of workers within the application at one time"`
71
71
72
+ TaskLimit int `toml:"task_limit" commented:"true" comment:"hourly task rate limit, 0 disables limit"`
73
+
72
74
// WorkerKillTime is how long the Launcher will
73
75
// wait for a forced-shutdown worker to cleanup.
74
76
WorkerKillTime time.Duration `toml:"worker_kill_time" commented:"true" comment:"how long the application will wait for a task to finish before shutting down when being forced to shut down"`
@@ -145,9 +147,8 @@ func NewLauncherFromBus(newWkr NewWorker, c bus.Consumer, p bus.Producer, opt *L
145
147
opt .DoneTopic = defaultDoneTopic
146
148
}
147
149
148
- lgr := log .New (os .Stderr , "" , log .LstdFlags )
149
150
if opt .Logger != nil {
150
- lgr = opt . Logger
151
+ opt . Logger = log . New ( os . Stderr , "" , log . LstdFlags )
151
152
}
152
153
153
154
// make sure maxInProgress is at least 1
@@ -171,20 +172,11 @@ func NewLauncherFromBus(newWkr NewWorker, c bus.Consumer, p bus.Producer, opt *L
171
172
// lifetime max remaining (0; no lifetime max)
172
173
remaining := opt .LifetimeWorkers
173
174
174
- // doneCncl (done cancel function)
175
- // - is called internally by the Launcher to signal that the Launcher
176
- // has COMPLETED shutting down.
177
- //
178
- // doneCtx (done context)
179
- // - is for communicating externally that the Launcher is DONE and
180
- // has shutdown gracefully.
181
- doneCtx , doneCncl := context .WithCancel (context .Background ())
182
-
183
175
// stop context and cancel func: shutdown Launcher/workers
184
176
//
185
177
// stopCtx - Launcher will listen on stopCtx.Done() for external forced shutdown.
186
178
// stopCncl - used externally of Launcher to initiate forced Launcher shutdown.
187
- stopCtx , stopCncl := context .WithCancel (context .Background ())
179
+ // stopCtx, stopCncl := context.WithCancel(context.Background())
188
180
189
181
// last context and cancel func - for indicating the last task
190
182
// is in progress.
@@ -194,7 +186,7 @@ func NewLauncherFromBus(newWkr NewWorker, c bus.Consumer, p bus.Producer, opt *L
194
186
//
195
187
// lastCncl - for sending a signal indicating the last task has
196
188
// been received and is currently being processed.
197
- lastCtx , lastCncl := context .WithCancel (context .Background ())
189
+ // lastCtx, lastCncl := context.WithCancel(context.Background())
198
190
199
191
// make sure logger is not nil
200
192
if opt .Logger == nil {
@@ -213,10 +205,10 @@ func NewLauncherFromBus(newWkr NewWorker, c bus.Consumer, p bus.Producer, opt *L
213
205
// warn if typeHandling is set and
214
206
// a task type is not provided.
215
207
if typeHandling != "" && opt .TaskType == "" {
216
- lgr .Printf ("NO WORKERS WILL BE LAUNCHED! task type handling is set to '%v' but no task type is provided" , typeHandling )
208
+ opt . Logger .Printf ("NO WORKERS WILL BE LAUNCHED! task type handling is set to '%v' but no task type is provided" , typeHandling )
217
209
}
218
210
219
- l := & Launcher {
211
+ return & Launcher {
220
212
initTime : time .Now (),
221
213
isInitialized : true ,
222
214
consumer : c ,
@@ -226,19 +218,15 @@ func NewLauncherFromBus(newWkr NewWorker, c bus.Consumer, p bus.Producer, opt *L
226
218
lgr : opt .Logger ,
227
219
taskType : opt .TaskType ,
228
220
typeHandling : typeHandling ,
229
- doneCtx : doneCtx ,
230
- doneCncl : doneCncl ,
231
- stopCtx : stopCtx ,
232
- stopCncl : stopCncl ,
233
- lastCtx : lastCtx ,
234
- lastCncl : lastCncl ,
221
+ // stopCtx: stopCtx,
222
+ // stopCncl: stopCncl,
223
+ // lastCtx: lastCtx,
224
+ // lastCncl: lastCncl,
235
225
maxInProgress : maxInProgress ,
236
226
remaining : remaining ,
237
227
slots : slots ,
238
228
closeTimeout : workerTimeout ,
239
229
}
240
-
241
- return l
242
230
}
243
231
244
232
// Launcher handles the heavy lifting of worker lifecycle, general
@@ -267,18 +255,14 @@ type Launcher struct {
267
255
taskType string // registered task type; used for identifying the worker and handling task types that do not match.
268
256
typeHandling string // how to handle unmatching task types: one of "reject", "ignore"
269
257
270
- // communicating Launcher has finished shutting down
271
- doneCtx context.Context // Launcher context (highest level context)
272
- doneCncl context.CancelFunc // Launcher cancel func (calling it indicates the Launcher has cleanly closed up)
273
-
274
258
// forcing workers/Launcher to shut down
275
259
// all worker contexts inherit from stopCtx.
276
- stopCtx context.Context // for listening to Launcher shutdown signal. Initiates shutdown process.
277
- stopCncl context.CancelFunc // for telling the Launcher to shutdown. Initiates shutdown process. Shutdown is complete when doneCtx.Done() is closed
260
+ // stopCtx context.Context // for listening to Launcher shutdown signal. Initiates shutdown process.
261
+ // stopCncl context.CancelFunc // for telling the Launcher to shutdown. Initiates shutdown process. Shutdown is complete when doneCtx.Done() is closed
278
262
279
263
// indicate the last task is in progress
280
- lastCtx context.Context // main loop will listen on lastCtx.Done() to know if the last task is in progress
281
- lastCncl context.CancelFunc // called to indicate the last task is in progress
264
+ // lastCtx context.Context // main loop will listen on lastCtx.Done() to know if the last task is in progress
265
+ // lastCncl context.CancelFunc // called to indicate the last task is in progress
282
266
283
267
// closeTimeout tells the Launcher how long to wait
284
268
// when forcing a task to close.
@@ -350,8 +334,8 @@ func (l *Launcher) Stats() LauncherStats {
350
334
finishedTasks := createdTasks - activeTasks
351
335
resp := LauncherStats {
352
336
RunTime : time .Now ().Sub (l .initTime ).String (),
353
- TasksConsumed : l .tasksConsumed ,
354
- TasksRunning : l .tasksRunning ,
337
+ TasksConsumed : atomic . LoadInt64 ( & l .tasksConsumed ) ,
338
+ TasksRunning : atomic . LoadInt64 ( & l .tasksRunning ) ,
355
339
Producer : l .producer .Info (),
356
340
Consumer : l .consumer .Info (),
357
341
}
@@ -362,8 +346,14 @@ func (l *Launcher) Stats() LauncherStats {
362
346
return resp
363
347
}
364
348
365
- // DoTasks will start the task loop and immediately
366
- // begin working on tasks if any are available.
349
+ // Deprecated: Use Start() instead.
350
+ func (l * Launcher ) DoTasks () (doneCtx context.Context , stopCncl context.CancelFunc ) {
351
+ ctx , cancel := context .WithCancel (context .Background ())
352
+ go l .Start (ctx )
353
+ return ctx , cancel
354
+ }
355
+
356
+ // Start is a BLOCKING task loop and immediately begin working on tasks if any are available.
367
357
//
368
358
// The Launcher assumes the producer and consumer
369
359
// are fully initialized when the Launcher is created.
@@ -375,29 +365,23 @@ func (l *Launcher) Stats() LauncherStats {
375
365
// will not do anything. If called more than once will
376
366
// return a copy of the same context and cancel function
377
367
// received the first time.
378
- func (l * Launcher ) DoTasks () ( doneCtx context.Context , stopCncl context. CancelFunc ) {
368
+ func (l * Launcher ) Start ( ctx context.Context ) {
379
369
if ! l .isInitialized {
380
370
panic ("launcher not initialized" )
381
371
}
382
-
383
372
if l .isDoing {
384
- return l . doneCtx , l . stopCncl
373
+ return
385
374
}
386
- go l .do ()
387
-
388
375
l .isDoing = true
389
- return l .doneCtx , l .stopCncl
390
- }
391
-
392
- // do is the main task loop.
393
- func (l * Launcher ) do () {
394
- defer l .doneCncl ()
376
+ stopCtx , stopCancel := context .WithCancel (ctx )
377
+ // lastCtx, lastCancel := context.WithCancel(ctx)
378
+ defer stopCancel ()
395
379
for {
396
380
select {
397
- case <- l .stopCtx .Done ():
398
- goto Shutdown
399
- case <- l .lastCtx .Done ():
381
+ case <- stopCtx .Done ():
400
382
goto Shutdown
383
+ // case <-lastCtx.Done():
384
+ // goto Shutdown
401
385
case <- l .slots :
402
386
l .wg .Add (1 )
403
387
l .mu .Lock ()
@@ -408,14 +392,14 @@ func (l *Launcher) do() {
408
392
if l .remaining == 0 {
409
393
// the message about to be requested will
410
394
// be the last one.
411
- l . lastCncl ()
395
+ stopCancel ()
412
396
}
413
397
}
414
398
l .mu .Unlock ()
415
399
416
400
// next() needs to be non-blocking so
417
- // the application can shut down when asked to.
418
- go l .next ()
401
+ // the application can shut down when asked to
402
+ go l .next (stopCtx , stopCancel )
419
403
}
420
404
}
421
405
@@ -440,29 +424,29 @@ Shutdown:
440
424
}
441
425
442
426
// next handles getting and processing the next task.
443
- func (l * Launcher ) next () {
427
+ func (l * Launcher ) next (ctx context. Context , cancel context. CancelFunc ) {
444
428
tskB , done , err := l .consumer .Msg ()
445
429
if done {
446
- l . lastCncl ()
430
+ cancel ()
447
431
}
448
432
if err != nil {
449
433
l .log (err .Error ())
450
- l .giveBackSlot ()
434
+ l .giveBackSlot (ctx )
451
435
452
436
return
453
437
}
454
438
455
439
// handle a zero byte message
456
440
if len (tskB ) == 0 {
457
- l .giveBackSlot ()
441
+ l .giveBackSlot (ctx )
458
442
459
443
return
460
444
}
461
445
462
446
tsk , err := NewFromBytes (tskB )
463
- if err != nil {
464
- l .log (err . Error () )
465
- l .giveBackSlot ()
447
+ if err != nil || tsk == nil {
448
+ l .log (err )
449
+ l .giveBackSlot (ctx )
466
450
467
451
return
468
452
}
@@ -472,7 +456,7 @@ func (l *Launcher) next() {
472
456
go func () {
473
457
atomic .AddInt64 (& l .tasksConsumed , 1 )
474
458
atomic .AddInt64 (& l .tasksRunning , 1 )
475
- l .doLaunch (tsk )
459
+ l .doLaunch (tsk , ctx )
476
460
atomic .AddInt64 (& l .tasksRunning , - 1 )
477
461
}()
478
462
}
@@ -481,15 +465,15 @@ func (l *Launcher) next() {
481
465
// doLaunch will safely handle the wait
482
466
// group and cleanly close down a worker
483
467
// and report back on the task result.
484
- func (l * Launcher ) doLaunch (tsk * Task ) {
485
- defer l .giveBackSlot ()
468
+ func (l * Launcher ) doLaunch (tsk * Task , ctx context. Context ) {
469
+ defer l .giveBackSlot (ctx )
486
470
487
471
var wCtx context.Context
488
472
var cncl context.CancelFunc
489
473
if l .completeTimeout > time .Duration (0 ) {
490
- wCtx , cncl = context .WithTimeout (l . stopCtx , l .completeTimeout )
474
+ wCtx , cncl = context .WithTimeout (ctx , l .completeTimeout )
491
475
} else {
492
- wCtx , cncl = context .WithCancel (l . stopCtx )
476
+ wCtx , cncl = context .WithCancel (ctx )
493
477
}
494
478
defer cncl () // clean up worker context
495
479
@@ -565,16 +549,16 @@ func (l *Launcher) sendTsk(tsk *Task) {
565
549
//
566
550
// will not give back the slot if the application is
567
551
// shutting down or processing the last task.
568
- func (l * Launcher ) giveBackSlot () {
569
- if l . stopCtx . Err () == nil && l . lastCtx .Err () == nil {
552
+ func (l * Launcher ) giveBackSlot (ctx context. Context ) {
553
+ if ctx .Err () == nil {
570
554
//atomic.AddInt64(&l.tasksRunning, -1)
571
555
l .slots <- 1
572
556
}
573
557
l .wg .Done ()
574
558
}
575
559
576
560
// log is the central point of operational logging.
577
- func (l * Launcher ) log (msg string ) {
561
+ func (l * Launcher ) log (msg interface {} ) {
578
562
l .lgr .Println (msg )
579
563
}
580
564
@@ -587,9 +571,5 @@ func (l *Launcher) log(msg string) {
587
571
func (l * Launcher ) Err () error {
588
572
l .mu .Lock ()
589
573
defer l .mu .Unlock ()
590
- if l .doneCtx .Err () != nil {
591
- return l .closeErr
592
- }
593
-
594
- return nil
574
+ return l .closeErr
595
575
}
0 commit comments