@@ -2403,24 +2403,20 @@ return_one(#{system_time := Ts} = Meta, MsgId,
24032403 end .
24042404
24052405should_delay (DeliveryFailed , DelayedRetry , Ts , Header , Anns ) ->
2406- % % First check for explicit x-opt-delivery-time annotation.
2407- % % This takes precedence over delayed_retry configuration.
2408- DeferralToken = case Anns of
2409- #{<<" x-opt-deferral-token" >> := Token }
2410- when is_binary (Token ) ->
2411- Token ;
2412- _ ->
2413- undefined
2414- end ,
24152406 case Anns of
24162407 #{<<" x-opt-delivery-time" >> := DeliveryTime }
24172408 when is_integer (DeliveryTime ),
24182409 DeliveryTime > Ts ->
2410+ % % Deferral tokens are only honoured when the client explicitly
2411+ % % sets a delivery time; the delayed-retry path never creates a
2412+ % % deferred entry so that tokens remain a purely client-driven
2413+ % % mechanism.
2414+ DeferralToken = maps :get (<<" x-opt-deferral-token" >>, Anns , undefined ),
24192415 {true , DeliveryTime , DeferralToken };
24202416 _ ->
24212417 case should_delay0 (DeliveryFailed , DelayedRetry , Ts , Header ) of
24222418 {true , ReadyAt } ->
2423- {true , ReadyAt , DeferralToken };
2419+ {true , ReadyAt , undefined };
24242420 false ->
24252421 false
24262422 end
@@ -2616,8 +2612,7 @@ take_next_delayed(Ts, #delayed{next = {ReadyAt, Idx, Msg},
26162612 {? TUPLE (NextReadyAt , NextIdx ), V } = gb_trees :smallest (Tree ),
26172613 {NextReadyAt , NextIdx , V }
26182614 end ,
2619- % % Remove any deferral token that maps to this key
2620- Deferred = maps :filter (fun (_Token , K ) -> K =/= Key end , Deferred0 ),
2615+ Deferred = remove_deferred_key (Key , Deferred0 ),
26212616 Delayed = # delayed {tree = Tree , next = Next , deferred = Deferred },
26222617 {Msg , Delayed };
26232618take_next_delayed (_Ts , # delayed {}) ->
@@ -2660,12 +2655,22 @@ take_delayed_for_retry(N, Ts, #delayed{tree = Tree0,
26602655 ? TUPLE (ReadyAt , Idx ) = NextKey ,
26612656 {ReadyAt , Idx , NextMsg }
26622657 end ,
2663- % % Remove any deferral token that maps to this key
2664- Deferred = maps :filter (fun (_Token , K ) -> K =/= Key end , Deferred0 ),
2658+ Deferred = remove_deferred_key (Key , Deferred0 ),
26652659 Delayed = # delayed {tree = Tree , next = Next , deferred = Deferred },
26662660 take_delayed_for_retry (N - 1 , Ts , Delayed , [Msg | Acc ])
26672661 end .
26682662
2663+ % % Drop a single tree key from every token's key list, dropping the token
2664+ % % entirely once its last key is removed.
2665+ remove_deferred_key (Key , Deferred0 ) ->
2666+ maps :filtermap (
2667+ fun (_Token , Keys ) ->
2668+ case lists :delete (Key , Keys ) of
2669+ [] -> false ;
2670+ Remaining -> {true , Remaining }
2671+ end
2672+ end , Deferred0 ).
2673+
26692674take_deferred (Tokens , Delayed ) ->
26702675 take_deferred (Tokens , Delayed , [], []).
26712676
@@ -2675,20 +2680,30 @@ take_deferred([Token | Rest], #delayed{tree = Tree0,
26752680 deferred = Deferred0 } = Delayed0 ,
26762681 MsgsAcc , NotFoundAcc ) ->
26772682 case maps :take (Token , Deferred0 ) of
2678- {Key , Deferred1 } ->
2679- case gb_trees :lookup (Key , Tree0 ) of
2680- {value , Msg } ->
2681- Tree = gb_trees :delete (Key , Tree0 ),
2682- Next = update_delayed_next (Tree ),
2683- Delayed = Delayed0 # delayed {tree = Tree ,
2684- next = Next ,
2685- deferred = Deferred1 },
2686- take_deferred (Rest , Delayed , [Msg | MsgsAcc ], NotFoundAcc );
2687- none ->
2688- % % Key in deferred map but not in tree - inconsistent,
2689- % % treat as not found and clean up
2690- Delayed = Delayed0 # delayed {deferred = Deferred1 },
2691- take_deferred (Rest , Delayed , MsgsAcc , [Token | NotFoundAcc ])
2683+ {Keys , Deferred1 } ->
2684+ % % Keys were prepended as they were parked, so reverse to
2685+ % % resolve them oldest first.
2686+ {Tree , MsgsAcc1 , Found } =
2687+ lists :foldl (
2688+ fun (Key , {TreeAcc , Acc , FoundAcc }) ->
2689+ case gb_trees :lookup (Key , TreeAcc ) of
2690+ {value , Msg } ->
2691+ {gb_trees :delete (Key , TreeAcc ), [Msg | Acc ], true };
2692+ none ->
2693+ % % Key in deferred map but not in tree -
2694+ % % inconsistent, skip and clean up
2695+ {TreeAcc , Acc , FoundAcc }
2696+ end
2697+ end , {Tree0 , MsgsAcc , false }, lists :reverse (Keys )),
2698+ Next = update_delayed_next (Tree ),
2699+ Delayed = Delayed0 # delayed {tree = Tree ,
2700+ next = Next ,
2701+ deferred = Deferred1 },
2702+ case Found of
2703+ true ->
2704+ take_deferred (Rest , Delayed , MsgsAcc1 , NotFoundAcc );
2705+ false ->
2706+ take_deferred (Rest , Delayed , MsgsAcc1 , [Token | NotFoundAcc ])
26922707 end ;
26932708 error ->
26942709 take_deferred (Rest , Delayed0 , MsgsAcc , [Token | NotFoundAcc ])
@@ -2765,7 +2780,9 @@ delayed_in(ReadyAt, Idx, Msg, DeferralToken, #delayed{tree = Tree0,
27652780 undefined ->
27662781 Deferred0 ;
27672782 _ ->
2768- Deferred0 #{DeferralToken => Key }
2783+ maps :update_with (DeferralToken ,
2784+ fun (Keys ) -> [Key | Keys ] end ,
2785+ [Key ], Deferred0 )
27692786 end ,
27702787 # delayed {tree = Tree , next = Next , deferred = Deferred }.
27712788
0 commit comments