@@ -2407,45 +2407,79 @@ reduce_memory_use(State = #vqstate {
2407
2407
out = AvgEgress ,
2408
2408
ack_in = AvgAckIngress ,
2409
2409
ack_out = AvgAckEgress } }) ->
2410
- State1 = # vqstate { q2 = Q2 , q3 = Q3 } =
2410
+ {CreditDiscBound , _ } = rabbit_misc :get_env (rabbit ,
2411
+ msg_store_credit_disc_bound ,
2412
+ ? CREDIT_DISC_BOUND ),
2413
+ {NeedResumeA2B , State1 } = {_ , # vqstate { q2 = Q2 , q3 = Q3 }} =
2411
2414
case chunk_size (RamMsgCount + gb_trees :size (RPA ), TargetRamCount ) of
2412
- 0 -> State ;
2415
+ 0 -> { false , State } ;
2413
2416
% % Reduce memory of pending acks and alphas. The order is
2414
2417
% % determined based on which is growing faster. Whichever
2415
2418
% % comes second may very well get a quota of 0 if the
2416
2419
% % first manages to push out the max number of messages.
2417
- S1 -> Funs = case ((AvgAckIngress - AvgAckEgress ) >
2420
+ A2BChunk ->
2421
+ % % In case there are few messages to be sent to a message store
2422
+ % % and many messages to be embedded to the queue index,
2423
+ % % we should limit the number of messages to be flushed
2424
+ % % to avoid blocking the process.
2425
+ A2BChunkActual = case A2BChunk > CreditDiscBound * 2 of
2426
+ true -> CreditDiscBound * 2 ;
2427
+ false -> A2BChunk
2428
+ end ,
2429
+ Funs = case ((AvgAckIngress - AvgAckEgress ) >
2418
2430
(AvgIngress - AvgEgress )) of
2419
2431
true -> [fun limit_ram_acks /2 ,
2420
2432
fun push_alphas_to_betas /2 ];
2421
2433
false -> [fun push_alphas_to_betas /2 ,
2422
2434
fun limit_ram_acks /2 ]
2423
2435
end ,
2424
- { _ , State2 } = lists :foldl (fun (ReduceFun , {QuotaN , StateN }) ->
2436
+ { Quota , State2 } = lists :foldl (fun (ReduceFun , {QuotaN , StateN }) ->
2425
2437
ReduceFun (QuotaN , StateN )
2426
- end , {S1 , State }, Funs ),
2427
- State2
2438
+ end , {A2BChunkActual , State }, Funs ),
2439
+ {( Quota == 0 ) andalso ( A2BChunk > A2BChunkActual ), State2 }
2428
2440
end ,
2429
-
2430
- State3 =
2441
+ Permitted = permitted_beta_count ( State1 ),
2442
+ { NeedResumeB2D , State3 } =
2431
2443
% % If there are more messages with their queue position held in RAM,
2432
2444
% % a.k.a. betas, in Q2 & Q3 than IoBatchSize,
2433
2445
% % write their queue position to disk, a.k.a. push_betas_to_deltas
2434
2446
case chunk_size (? QUEUE :len (Q2 ) + ? QUEUE :len (Q3 ),
2435
- permitted_beta_count (State1 )) of
2436
- S2 when S2 >= IoBatchSize ->
2437
- % % There is an implicit, but subtle, upper bound here. We
2438
- % % may shuffle a lot of messages from Q2/3 into delta, but
2439
- % % the number of these that require any disk operation,
2440
- % % namely index writing, i.e. messages that are genuine
2441
- % % betas and not gammas, is bounded by the credit_flow
2442
- % % limiting of the alpha->beta conversion above.
2443
- push_betas_to_deltas (S2 , State1 );
2447
+ Permitted ) of
2448
+ B2DChunk when B2DChunk >= IoBatchSize ->
2449
+ % % Same as for alphas to betas. Limit a number of messages
2450
+ % % to be flushed to disk at once to avoid blocking the process.
2451
+ B2DChunkActual = case B2DChunk > CreditDiscBound * 2 of
2452
+ true -> CreditDiscBound * 2 ;
2453
+ false -> B2DChunk
2454
+ end ,
2455
+ StateBD = push_betas_to_deltas (B2DChunkActual , State1 ),
2456
+ {B2DChunk > B2DChunkActual , StateBD };
2444
2457
_ ->
2445
- State1
2458
+ { false , State1 }
2446
2459
end ,
2447
- % % See rabbitmq-server-290 for the reasons behind this GC call.
2448
- garbage_collect (),
2460
+ % % We can be blocked by the credit flow, or limited by a batch size,
2461
+ % % or finished with flushing.
2462
+ % % If blocked by the credit flow - the credit grant will resume processing,
2463
+ % % if limited by a batch - the batch continuation message should be sent.
2464
+ % % The continuation message will be prioritised over publishes,
2465
+ % % but not cinsumptions, so the queue can make progess.
2466
+ Blocked = credit_flow :blocked (),
2467
+ case {Blocked , NeedResumeA2B orelse NeedResumeB2D } of
2468
+ % % Credit bump will continue paging
2469
+ {true , _ } -> ok ;
2470
+ % % Finished with paging
2471
+ {false , false } -> ok ;
2472
+ % % Planning next batch
2473
+ {false , true } ->
2474
+ % % We don't want to use self-credit-flow, because it's harder to
2475
+ % % reason about. So the process sends a (prioritised) message to
2476
+ % % itself and sets a waiting_bump value to keep the message box clean
2477
+ case get (waiting_bump ) of
2478
+ true -> ok ;
2479
+ _ -> self () ! bump_reduce_memory_use ,
2480
+ put (waiting_bump , true )
2481
+ end
2482
+ end ,
2449
2483
State3 ;
2450
2484
% % When using lazy queues, there are no alphas, so we don't need to
2451
2485
% % call push_alphas_to_betas/2.
0 commit comments