@@ -122,7 +122,7 @@ type Queue struct {
122122 location string
123123 hostname string
124124 autogenMsgDomain string
125- wheel * TimeWheel
125+ wheel * TimeWheel [ queueSlot ]
126126
127127 dsnPipeline module.DeliveryTarget
128128
@@ -211,6 +211,9 @@ func (q *Queue) Configure(inlineArgs []string, cfg *config.Map) error {
211211 cfg .Bool ("debug" , true , false , & q .Log .Debug )
212212 cfg .Int ("max_tries" , false , false , 20 , & q .maxTries )
213213 cfg .Int ("max_parallelism" , false , false , 16 , & q .maxParallelism )
214+ cfg .Duration ("post_init_delay" , false , false , q .postInitDelay , & q .postInitDelay )
215+ cfg .Duration ("initial_retry_time" , false , false , q .initialRetryTime , & q .initialRetryTime )
216+ cfg .Float ("retry_time_scale" , false , false , q .retryTimeScale , & q .retryTimeScale )
214217 cfg .String ("location" , false , false , q .location , & q .location )
215218 cfg .Custom ("target" , false , true , nil , modconfig .DeliveryDirective , & q .Target )
216219 cfg .String ("hostname" , true , true , "" , & q .hostname )
@@ -249,7 +252,7 @@ func (q *Queue) Start() error {
249252}
250253
251254func (q * Queue ) start (maxParallelism int ) error {
252- q .wheel = NewTimeWheel (q .dispatch )
255+ q .wheel = NewTimeWheel [ queueSlot ] (q .dispatch )
253256 q .deliverySemaphore = make (chan struct {}, maxParallelism )
254257
255258 if err := q .readDiskQueue (); err != nil {
@@ -261,13 +264,18 @@ func (q *Queue) start(maxParallelism int) error {
261264 return nil
262265}
263266
264- func (q * Queue ) Stop () error {
267+ func (q * Queue ) EarlyStop () error {
268+ // We must ensure queue state is consistent on disk before we proceed
269+ // with configuration reload.
265270 q .wheel .Close ()
266271 q .deliveryWg .Wait ()
267-
268272 return nil
269273}
270274
275+ func (q * Queue ) Stop () error {
276+ return q .EarlyStop ()
277+ }
278+
271279// discardBroken changes the name of metadata file to have .meta_broken
272280// extension.
273281//
@@ -283,8 +291,8 @@ func (q *Queue) discardBroken(id string) {
283291 }
284292}
285293
286- func (q * Queue ) dispatch (value TimeSlot ) {
287- slot := value .Value .( queueSlot )
294+ func (q * Queue ) dispatch (ctx context. Context , value TimeSlot [ queueSlot ] ) {
295+ slot := value .Value
288296
289297 q .Log .Debugln ("starting delivery for" , slot .ID )
290298
@@ -329,7 +337,7 @@ func (q *Queue) dispatch(value TimeSlot) {
329337 body = slot .Body
330338 }
331339
332- q .tryDelivery (meta , hdr , body )
340+ q .tryDelivery (ctx , meta , hdr , body )
333341 }()
334342}
335343
@@ -373,10 +381,10 @@ func toSMTPErr(err error) *smtp.SMTPError {
373381 return res
374382}
375383
376- func (q * Queue ) tryDelivery (meta * QueueMetadata , header textproto.Header , body buffer.Buffer ) {
384+ func (q * Queue ) tryDelivery (ctx context. Context , meta * QueueMetadata , header textproto.Header , body buffer.Buffer ) {
377385 dl := target .DeliveryLogger (q .Log , meta .MsgMeta )
378386
379- partialErr := q .deliver (meta , header , body )
387+ partialErr := q .deliver (ctx , meta , header , body )
380388 dl .Debugf ("errors: %v" , partialErr .Errs )
381389
382390 // While iterating the list of recipients we also pick the smallest tries count
@@ -460,7 +468,7 @@ func (q *Queue) tryDelivery(meta *QueueMetadata, header textproto.Header, body b
460468 })
461469}
462470
463- func (q * Queue ) deliver (meta * QueueMetadata , header textproto.Header , body buffer.Buffer ) partialError {
471+ func (q * Queue ) deliver (ctx context. Context , meta * QueueMetadata , header textproto.Header , body buffer.Buffer ) partialError {
464472 dl := target .DeliveryLogger (q .Log , meta .MsgMeta )
465473 perr := partialError {
466474 Errs : map [string ]error {},
@@ -471,7 +479,7 @@ func (q *Queue) deliver(meta *QueueMetadata, header textproto.Header, body buffe
471479 msgMeta .ID = msgMeta .ID + "-" + strconv .FormatInt (time .Now ().Unix (), 16 )
472480 dl .Debugf ("using message ID = %s" , msgMeta .ID )
473481
474- msgCtx , msgTask := trace .NewTask (context . Background () , "Queue delivery" )
482+ msgCtx , msgTask := trace .NewTask (ctx , "Queue delivery" )
475483 defer msgTask .End ()
476484
477485 mailCtx , mailTask := trace .NewTask (msgCtx , "MAIL FROM" )
@@ -486,6 +494,15 @@ func (q *Queue) deliver(meta *QueueMetadata, header textproto.Header, body buffe
486494 }
487495 dl .Debugf ("target.StartDelivery OK" )
488496
497+ // Check in case delivery implementation is actually
498+ // context-unaware.
499+ if err := mailCtx .Err (); err != nil {
500+ for _ , rcpt := range meta .To {
501+ perr .Errs [rcpt ] = err
502+ }
503+ return perr
504+ }
505+
489506 var acceptedRcpts []string
490507 for _ , rcpt := range meta .To {
491508 rcptCtx , rcptTask := trace .NewTask (msgCtx , "RCPT TO" )
@@ -497,6 +514,15 @@ func (q *Queue) deliver(meta *QueueMetadata, header textproto.Header, body buffe
497514 acceptedRcpts = append (acceptedRcpts , rcpt )
498515 }
499516 rcptTask .End ()
517+
518+ // Check in case delivery implementation is actually
519+ // context-unaware.
520+ if err := mailCtx .Err (); err != nil {
521+ for _ , rcpt := range meta .To {
522+ perr .Errs [rcpt ] = err
523+ }
524+ return perr
525+ }
500526 }
501527
502528 if len (acceptedRcpts ) == 0 {
@@ -513,6 +539,10 @@ func (q *Queue) deliver(meta *QueueMetadata, header textproto.Header, body buffe
513539 }
514540 }
515541
542+ // At this point, it is too late to abort delivery. We should complete
543+ // it or fail it consistently.
544+ msgCtx = context .WithoutCancel (msgCtx )
545+
516546 bodyCtx , bodyTask := trace .NewTask (msgCtx , "DATA" )
517547 defer bodyTask .End ()
518548
0 commit comments