Switch to using prefetch rather than credit, that way we don't have to freeze the basic.credit methods. This has some minor implications:
* The basic.get hack is no longer possible, so we need to take it out. But everyone seemed to hate it anyway... * We no longer limit the rate of message ingress in no-ack mode. But that's what we do for federated exchanges. * The code gets a bit simpler. Woo.
This commit is contained in:
		
							parent
							
								
									8852e78327
								
							
						
					
					
						commit
						8b29406c32
					
				|  | @ -21,7 +21,7 @@ | |||
| 
 | ||||
| -behaviour(gen_server2). | ||||
| 
 | ||||
| -export([start_link/1, go/0, run/1, pause/1, basic_get/1]). | ||||
| -export([start_link/1, go/0, run/1, pause/1]). | ||||
| 
 | ||||
| -export([init/1, handle_call/3, handle_cast/2, handle_info/2, | ||||
|          terminate/2, code_change/3]). | ||||
|  | @ -29,17 +29,14 @@ | |||
| -import(rabbit_federation_util, [name/1]). | ||||
| 
 | ||||
| -record(not_started, {queue, run, upstream, upstream_params}). | ||||
| -record(state, {queue, run, conn, ch, dconn, dch, credit, ctag, | ||||
|                 upstream, upstream_params, unacked}). | ||||
| 
 | ||||
| -define(CREDIT_MORE_AT_RATIO, 0.25). | ||||
| -record(state, {queue, run, conn, ch, dconn, dch, upstream, upstream_params, | ||||
|                 unacked}). | ||||
| 
 | ||||
| start_link(Args) -> | ||||
|     gen_server2:start_link(?MODULE, Args, [{timeout, infinity}]). | ||||
| 
 | ||||
| run(QName)   -> cast(QName, run). | ||||
| pause(QName) -> cast(QName, pause). | ||||
| basic_get(QName) -> cast(QName, basic_get). | ||||
| go()         -> cast(go). | ||||
| 
 | ||||
| %%---------------------------------------------------------------------------- | ||||
|  | @ -97,12 +94,12 @@ handle_cast(go, State = #not_started{}) -> | |||
| handle_cast(go, State) -> | ||||
|     {noreply, State}; | ||||
| 
 | ||||
| handle_cast(run, State = #state{upstream = #upstream{prefetch_count = Prefetch}, | ||||
|                                 ch = Ch, run = false, ctag = CTag}) -> | ||||
|     amqp_channel:cast(Ch, #'basic.credit'{consumer_tag = CTag, | ||||
|                                           credit       = Prefetch, | ||||
|                                           drain        = false}), | ||||
|     {noreply, State#state{run = true, credit = Prefetch}}; | ||||
| handle_cast(run, State = #state{upstream        = Upstream, | ||||
|                                 upstream_params = UParams, | ||||
|                                 ch              = Ch, | ||||
|                                 run             = false}) -> | ||||
|     consume(Ch, Upstream, UParams#upstream_params.x_or_q), | ||||
|     {noreply, State#state{run = true}}; | ||||
| 
 | ||||
| handle_cast(run, State = #not_started{}) -> | ||||
|     {noreply, State#not_started{run = true}}; | ||||
|  | @ -118,22 +115,9 @@ handle_cast(pause, State = #state{run = false}) -> | |||
| handle_cast(pause, State = #not_started{}) -> | ||||
|     {noreply, State#not_started{run = false}}; | ||||
| 
 | ||||
| handle_cast(pause, State = #state{ch = Ch, ctag = CTag}) -> | ||||
|     amqp_channel:cast(Ch, #'basic.credit'{consumer_tag = CTag, | ||||
|                                           credit       = 0, | ||||
|                                           drain        = false}), | ||||
|     {noreply, State#state{run = false, credit = 0}}; | ||||
| 
 | ||||
| handle_cast(basic_get, State = #not_started{}) -> | ||||
|     {noreply, State}; | ||||
| 
 | ||||
| handle_cast(basic_get, State = #state{ch = Ch, credit = Credit, ctag = CTag}) -> | ||||
|     Credit1 = Credit + 1, | ||||
|     amqp_channel:cast( | ||||
|       Ch, #'basic.credit'{consumer_tag = CTag, | ||||
|                           credit       = Credit1, | ||||
|                           drain        = false}), | ||||
|     {noreply, State#state{credit = Credit1}}; | ||||
| handle_cast(pause, State = #state{ch = Ch}) -> | ||||
|     cancel(Ch), | ||||
|     {noreply, State#state{run = false}}; | ||||
| 
 | ||||
| handle_cast(Msg, State) -> | ||||
|     {stop, {unexpected_cast, Msg}, State}. | ||||
|  | @ -155,13 +139,9 @@ handle_info({#'basic.deliver'{redelivered = Redelivered} = DeliverMethod, Msg}, | |||
|             State = #state{queue           = #amqqueue{name = QName}, | ||||
|                            upstream        = Upstream, | ||||
|                            upstream_params = UParams, | ||||
|                            run             = Run, | ||||
|                            credit          = Credit, | ||||
|                            ctag            = CTag, | ||||
|                            ch              = Ch, | ||||
|                            dch             = DCh, | ||||
|                            unacked         = Unacked}) -> | ||||
|     #upstream{prefetch_count = Prefetch} = Upstream, | ||||
|     PublishMethod = #'basic.publish'{exchange    = <<"">>, | ||||
|                                      routing_key = QName#resource.name}, | ||||
|     HeadersFun = fun (H) -> update_headers(UParams, Redelivered, H) end, | ||||
|  | @ -169,25 +149,8 @@ handle_info({#'basic.deliver'{redelivered = Redelivered} = DeliverMethod, Msg}, | |||
|     Unacked1 = rabbit_federation_link_util:forward( | ||||
|                  Upstream, DeliverMethod, Ch, DCh, PublishMethod, | ||||
|                  HeadersFun, ForwardFun, Msg, Unacked), | ||||
|     %% TODO we could also hook this up to internal credit | ||||
|     %% TODO actually we could reject when 'stopped' | ||||
|     CreditMoreAt = trunc(Prefetch * ?CREDIT_MORE_AT_RATIO), | ||||
|     Credit1 = case Run of | ||||
|                   false -> Credit - 1; | ||||
|                   true  -> case Credit of | ||||
|                                I when I < CreditMoreAt -> | ||||
|                                    More = Prefetch - CreditMoreAt, | ||||
|                                    amqp_channel:cast( | ||||
|                                      Ch, #'basic.credit'{consumer_tag = CTag, | ||||
|                                                          credit       = More, | ||||
|                                                          drain        = false}), | ||||
|                                    I - 1 + More; | ||||
|                                I -> | ||||
|                                    I - 1 | ||||
|                            end | ||||
|               end, | ||||
|     {noreply, State#state{credit  = Credit1, | ||||
|                           unacked = Unacked1}}; | ||||
|     {noreply, State#state{unacked = Unacked1}}; | ||||
| 
 | ||||
| handle_info(#'basic.cancel'{}, | ||||
|             State = #state{queue           = #amqqueue{name = QName}, | ||||
|  | @ -230,41 +193,33 @@ code_change(_OldVsn, State, _Extra) -> | |||
| %%---------------------------------------------------------------------------- | ||||
| 
 | ||||
| go(S0 = #not_started{run             = Run, | ||||
|                      upstream        = Upstream, | ||||
|                      upstream        = Upstream = #upstream{ | ||||
|                                          prefetch_count = Prefetch}, | ||||
|                      upstream_params = UParams, | ||||
|                      queue           = Queue = #amqqueue{name = QName}}) -> | ||||
|     #upstream_params{x_or_q = UQueue = #amqqueue{ | ||||
|                                 durable     = Durable, | ||||
|                                 auto_delete = AutoDelete, | ||||
|                                 arguments   = QArgs}} = UParams, | ||||
|     Credit = case Run of | ||||
|                  true  -> Upstream#upstream.prefetch_count; | ||||
|                  false -> 0 | ||||
|              end, | ||||
|     CArgs = [{<<"x-priority">>, long,  -1}, | ||||
|              {<<"x-credit">>,   table, [{<<"credit">>, long, Credit}, | ||||
|                                         {<<"drain">>,  bool, false}]}], | ||||
|                                 arguments   = Args}} = UParams, | ||||
|     Unacked = rabbit_federation_link_util:unacked_new(), | ||||
|     NoAck = Upstream#upstream.ack_mode =:= 'no-ack', | ||||
|     rabbit_federation_link_util:start_conn_ch( | ||||
|       fun (Conn, Ch, DConn, DCh) -> | ||||
|               amqp_channel:call(Ch, #'queue.declare'{queue       = name(UQueue), | ||||
|                                                      durable     = Durable, | ||||
|                                                      auto_delete = AutoDelete, | ||||
|                                                      arguments   = QArgs}), | ||||
|               #'basic.consume_ok'{consumer_tag = CTag} = | ||||
|                   amqp_channel:call( | ||||
|                     Ch, #'basic.consume'{queue     = name(UQueue), | ||||
|                                          no_ack    = NoAck, | ||||
|                                          arguments = CArgs}), | ||||
|                                                      arguments   = Args}), | ||||
|               amqp_channel:call(Ch, #'basic.qos'{prefetch_count = Prefetch}), | ||||
| 
 | ||||
|               case Run of | ||||
|                   true  -> consume(Ch, Upstream, UQueue); | ||||
|                   false -> ok | ||||
|               end, | ||||
|               {noreply, #state{queue           = Queue, | ||||
|                                run             = Run, | ||||
|                                conn            = Conn, | ||||
|                                ch              = Ch, | ||||
|                                dconn           = DConn, | ||||
|                                dch             = DCh, | ||||
|                                credit          = Credit, | ||||
|                                ctag            = CTag, | ||||
|                                upstream        = Upstream, | ||||
|                                upstream_params = UParams, | ||||
|                                unacked         = Unacked}} | ||||
|  | @ -306,3 +261,16 @@ visit_match({table, T}, Info) -> | |||
|               end, [<<"uri">>, <<"virtual_host">>, <<"queue">>]); | ||||
| visit_match(_ ,_) -> | ||||
|     false. | ||||
| 
 | ||||
| consume(Ch, Upstream, UQueue) -> | ||||
|     NoAck = Upstream#upstream.ack_mode =:= 'no-ack', | ||||
|     amqp_channel:cast( | ||||
|       Ch, #'basic.consume'{queue        = name(UQueue), | ||||
|                            no_ack       = NoAck, | ||||
|                            nowait       = true, | ||||
|                            consumer_tag = <<"consumer">>, | ||||
|                            arguments    = [{<<"x-priority">>, long, -1}]}). | ||||
| 
 | ||||
| cancel(Ch) -> | ||||
|     amqp_channel:cast(Ch, #'basic.cancel'{nowait       = true, | ||||
|                                           consumer_tag = <<"consumer">>}). | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue