Skip to content

Commit 7862c2f

Browse files
authored
Merge pull request #12713 from rabbitmq/ra-2.16.0
Ra v2.16.0
2 parents 2c3ee0e + 70ade96 commit 7862c2f

14 files changed

+365
-144
lines changed

MODULE.bazel

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -253,8 +253,8 @@ erlang_package.hex_package(
253253
name = "ra",
254254
build_file = "@rabbitmq-server//bazel:BUILD.ra",
255255
pkg = "ra",
256-
sha256 = "bade5b4f30413cd36e754d2eb29a20b3a498695be9dec6eeb567d8c1aa4930ac",
257-
version = "2.15.1",
256+
sha256 = "7cdf7894f1f542aeaa3d9e6f3209aab6efe9a1cdd1d81de9587c3ea23629b0e3",
257+
version = "2.16.0",
258258
)
259259

260260
erlang_package.git_package(

bazel/BUILD.ra

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,17 +55,21 @@ erlang_bytecode(
5555
"src/ra_log_ets.erl",
5656
"src/ra_log_meta.erl",
5757
"src/ra_log_pre_init.erl",
58+
"src/ra_log_read_plan.erl",
5859
"src/ra_log_reader.erl",
5960
"src/ra_log_segment.erl",
6061
"src/ra_log_segment_writer.erl",
6162
"src/ra_log_snapshot.erl",
6263
"src/ra_log_sup.erl",
6364
"src/ra_log_wal.erl",
6465
"src/ra_log_wal_sup.erl",
66+
"src/ra_lol.erl",
6567
"src/ra_machine_ets.erl",
6668
"src/ra_machine_simple.erl",
6769
"src/ra_metrics_ets.erl",
6870
"src/ra_monitors.erl",
71+
"src/ra_mt.erl",
72+
"src/ra_range.erl",
6973
"src/ra_server.erl",
7074
"src/ra_server_proc.erl",
7175
"src/ra_server_sup.erl",

deps/rabbit/src/rabbit_channel.erl

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@
184184
messages_uncommitted,
185185
acks_uncommitted,
186186
pending_raft_commands,
187+
cached_segments,
187188
prefetch_count,
188189
state,
189190
garbage_collection]).
@@ -548,8 +549,6 @@ prioritise_cast(Msg, _Len, _State) ->
548549
case Msg of
549550
{confirm, _MsgSeqNos, _QPid} -> 5;
550551
{reject_publish, _MsgSeqNos, _QPid} -> 5;
551-
{queue_event, _, {confirm, _MsgSeqNos, _QPid}} -> 5;
552-
{queue_event, _, {reject_publish, _MsgSeqNos, _QPid}} -> 5;
553552
_ -> 0
554553
end.
555554

@@ -639,10 +638,14 @@ handle_cast(terminate, State = #ch{cfg = #conf{writer_pid = WriterPid}}) ->
639638
ok = rabbit_writer:flush(WriterPid),
640639
{stop, normal, State};
641640

642-
handle_cast({command, #'basic.consume_ok'{consumer_tag = CTag} = Msg}, State) ->
641+
handle_cast({command, #'basic.consume_ok'{consumer_tag = CTag} = Msg},
642+
#ch{consumer_mapping = CMap} = State)
643+
when is_map_key(CTag, CMap) ->
643644
ok = send(Msg, State),
644645
noreply(consumer_monitor(CTag, State));
645-
646+
handle_cast({command, #'basic.consume_ok'{}}, State) ->
647+
%% a consumer was not found so just ignore this
648+
noreply(State);
646649
handle_cast({command, Msg}, State) ->
647650
ok = send(Msg, State),
648651
noreply(State);
@@ -2259,6 +2262,8 @@ i(acks_uncommitted, #ch{tx = {_Msgs, Acks}}) -> ack_len(Acks);
22592262
i(acks_uncommitted, #ch{}) -> 0;
22602263
i(pending_raft_commands, #ch{queue_states = QS}) ->
22612264
pending_raft_commands(QS);
2265+
i(cached_segments, #ch{queue_states = QS}) ->
2266+
cached_segments(QS);
22622267
i(state, #ch{cfg = #conf{state = running}}) -> credit_flow:state();
22632268
i(state, #ch{cfg = #conf{state = State}}) -> State;
22642269
i(prefetch_count, #ch{cfg = #conf{consumer_prefetch = C}}) -> C;
@@ -2287,6 +2292,17 @@ pending_raft_commands(QStates) ->
22872292
end,
22882293
rabbit_queue_type:fold_state(Fun, 0, QStates).
22892294

2295+
cached_segments(QStates) ->
2296+
Fun = fun(_, V, Acc) ->
2297+
case rabbit_queue_type:state_info(V) of
2298+
#{cached_segments := P} ->
2299+
Acc + P;
2300+
_ ->
2301+
Acc
2302+
end
2303+
end,
2304+
rabbit_queue_type:fold_state(Fun, 0, QStates).
2305+
22902306
name(#ch{cfg = #conf{conn_name = ConnName, channel = Channel}}) ->
22912307
list_to_binary(rabbit_misc:format("~ts (~tp)", [ConnName, Channel])).
22922308

deps/rabbit/src/rabbit_fifo.erl

Lines changed: 42 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,10 @@
8989
make_purge/0,
9090
make_purge_nodes/1,
9191
make_update_config/1,
92-
make_garbage_collection/0
92+
make_garbage_collection/0,
93+
94+
exec_read/3
95+
9396
]).
9497

9598
-ifdef(TEST).
@@ -2076,30 +2079,27 @@ delivery_effect(ConsumerKey, [{MsgId, ?MSG(Idx, Header)}],
20762079
{CTag, CPid} = consumer_id(ConsumerKey, State),
20772080
{send_msg, CPid, {delivery, CTag, [{MsgId, {Header, RawMsg}}]},
20782081
?DELIVERY_SEND_MSG_OPTS};
2079-
delivery_effect(ConsumerKey, Msgs,
2080-
#?STATE{cfg = #cfg{resource = QR}} = State) ->
2082+
delivery_effect(ConsumerKey, Msgs, #?STATE{} = State) ->
20812083
{CTag, CPid} = consumer_id(ConsumerKey, State),
2082-
{RaftIdxs, Num} = lists:foldr(fun ({_, ?MSG(I, _)}, {Acc, N}) ->
2083-
{[I | Acc], N+1}
2084-
end, {[], 0}, Msgs),
2085-
{log, RaftIdxs,
2086-
fun (Commands)
2087-
when length(Commands) < Num ->
2088-
%% the mandatory length/1 guard is a bit :(
2089-
rabbit_log:info("~ts: requested read consumer tag '~ts' of ~b "
2090-
"indexes ~w but only ~b were returned. "
2091-
"This is most likely a stale read request "
2092-
"and can be ignored",
2093-
[rabbit_misc:rs(QR), CTag, Num, RaftIdxs,
2094-
length(Commands)]),
2095-
[];
2096-
(Commands) ->
2097-
DelMsgs = lists:zipwith(
2098-
fun (Cmd, {MsgId, ?MSG(_Idx, Header)}) ->
2099-
{MsgId, {Header, get_msg(Cmd)}}
2100-
end, Commands, Msgs),
2101-
[{send_msg, CPid, {delivery, CTag, DelMsgs},
2102-
?DELIVERY_SEND_MSG_OPTS}]
2084+
{RaftIdxs, _Num} = lists:foldr(fun ({_, ?MSG(I, _)}, {Acc, N}) ->
2085+
{[I | Acc], N+1}
2086+
end, {[], 0}, Msgs),
2087+
{log_ext, RaftIdxs,
2088+
fun (ReadPlan) ->
2089+
case node(CPid) == node() of
2090+
true ->
2091+
[{send_msg, CPid, {delivery, CTag, ReadPlan, Msgs},
2092+
?DELIVERY_SEND_MSG_OPTS}];
2093+
false ->
2094+
%% if we got there we need to read the data on this node
2095+
%% and send it to the consumer pid as it isn't availble
2096+
%% locally
2097+
{DelMsgs, Flru} = exec_read(undefined, ReadPlan, Msgs),
2098+
%% we need to evict all cached items here
2099+
_ = ra_flru:evict_all(Flru),
2100+
[{send_msg, CPid, {delivery, CTag, DelMsgs},
2101+
?DELIVERY_SEND_MSG_OPTS}]
2102+
end
21032103
end,
21042104
{local, node(CPid)}}.
21052105

@@ -3014,3 +3014,21 @@ incr_msg(Msg0, DelFailed, Anns) ->
30143014
false ->
30153015
Msg2
30163016
end.
3017+
3018+
exec_read(Flru0, ReadPlan, Msgs) ->
3019+
try ra_log_read_plan:execute(ReadPlan, Flru0) of
3020+
{Entries, Flru} ->
3021+
%% return a list in original order
3022+
{lists:map(fun ({MsgId, ?MSG(Idx, Header)}) ->
3023+
Cmd = maps:get(Idx, Entries),
3024+
{MsgId, {Header, get_msg(Cmd)}}
3025+
end, Msgs), Flru}
3026+
catch exit:{missing_key, _}
3027+
when Flru0 =/= undefined ->
3028+
%% this segment has most likely been appended to but the
3029+
%% cached index doesn't know about new items and need to be
3030+
%% re-generated
3031+
_ = ra_flru:evict_all(Flru0),
3032+
%% retry without segment cache
3033+
exec_read(undefined, ReadPlan, Msgs)
3034+
end.

deps/rabbit/src/rabbit_fifo_client.erl

Lines changed: 98 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
purge/1,
3131
update_machine_state/2,
3232
pending_size/1,
33+
num_cached_segments/1,
3334
stat/1,
3435
stat/2,
3536
query_single_active_consumer/1,
@@ -40,8 +41,12 @@
4041
-define(TIMER_TIME, 10000).
4142
-define(COMMAND_TIMEOUT, 30000).
4243
-define(UNLIMITED_PREFETCH_COUNT, 2000). %% something large for ra
44+
%% controls the timer for closing cached segments
45+
-define(CACHE_SEG_TIMEOUT, 5000).
4346

4447
-type seq() :: non_neg_integer().
48+
-type milliseconds() :: non_neg_integer().
49+
4550

4651
-record(consumer, {key :: rabbit_fifo:consumer_key(),
4752
% status = up :: up | cancelled,
@@ -69,7 +74,11 @@
6974
pending = #{} :: #{seq() =>
7075
{term(), rabbit_fifo:command()}},
7176
consumers = #{} :: #{rabbit_types:ctag() => #consumer{}},
72-
timer_state :: term()
77+
timer_state :: term(),
78+
cached_segments :: undefined |
79+
{undefined | reference(),
80+
LastSeenMs :: milliseconds(),
81+
ra_flru:state()}
7382
}).
7483

7584
-opaque state() :: #state{}.
@@ -132,9 +141,15 @@ enqueue(QName, Correlation, Msg,
132141
%% it is safe to reject the message as we never attempted
133142
%% to send it
134143
{reject_publish, State0};
144+
{error, {shutdown, delete}} ->
145+
rabbit_log:debug("~ts: QQ ~ts tried to register enqueuer during delete shutdown",
146+
[?MODULE, rabbit_misc:rs(QName)]),
147+
{reject_publish, State0};
135148
{timeout, _} ->
136149
{reject_publish, State0};
137150
Err ->
151+
rabbit_log:debug("~ts: QQ ~ts error when registering enqueuer ~p",
152+
[?MODULE, rabbit_misc:rs(QName), Err]),
138153
exit(Err)
139154
end;
140155
enqueue(_QName, _Correlation, _Msg,
@@ -167,7 +182,7 @@ enqueue(QName, Correlation, Msg,
167182
%% @param QueueName Name of the queue.
168183
%% @param Msg an arbitrary erlang term representing the message.
169184
%% @param State the current {@module} state.
170-
%% @returns
185+
%% @return's
171186
%% `{ok, State, Actions}' if the command was successfully sent.
172187
%% {@module} assigns a sequence number to every raft command it issues. The
173188
%% SequenceNumber can be correlated to the applied sequence numbers returned
@@ -510,6 +525,15 @@ purge(Server) ->
510525
pending_size(#state{pending = Pend}) ->
511526
maps:size(Pend).
512527

528+
-spec num_cached_segments(state()) -> non_neg_integer().
529+
num_cached_segments(#state{cached_segments = CachedSegments}) ->
530+
case CachedSegments of
531+
undefined ->
532+
0;
533+
{_, _, Cached} ->
534+
ra_flru:size(Cached)
535+
end.
536+
513537
-spec stat(ra:server_id()) ->
514538
{ok, non_neg_integer(), non_neg_integer()}
515539
| {error | timeout, term()}.
@@ -657,7 +681,8 @@ handle_ra_event(QName, Leader, {applied, Seqs},
657681
_ ->
658682
{ok, State2, Actions}
659683
end;
660-
handle_ra_event(QName, From, {machine, {delivery, _ConsumerTag, _} = Del}, State0) ->
684+
handle_ra_event(QName, From, {machine, Del}, State0)
685+
when element(1, Del) == delivery ->
661686
handle_delivery(QName, From, Del, State0);
662687
handle_ra_event(_QName, _From, {machine, Action}, State)
663688
when element(1, Action) =:= credit_reply orelse
@@ -667,28 +692,31 @@ handle_ra_event(_QName, _, {machine, {queue_status, Status}},
667692
#state{} = State) ->
668693
%% just set the queue status
669694
{ok, State#state{queue_status = Status}, []};
670-
handle_ra_event(_QName, Leader, {machine, leader_change},
695+
handle_ra_event(QName, Leader, {machine, leader_change},
671696
#state{leader = OldLeader,
672697
pending = Pending} = State0) ->
673698
%% we need to update leader
674699
%% and resend any pending commands
675-
rabbit_log:debug("~ts: Detected QQ leader change from ~w to ~w, "
700+
rabbit_log:debug("~ts: ~s Detected QQ leader change from ~w to ~w, "
676701
"resending ~b pending commands",
677-
[?MODULE, OldLeader, Leader, maps:size(Pending)]),
702+
[rabbit_misc:rs(QName), ?MODULE, OldLeader,
703+
Leader, maps:size(Pending)]),
678704
State = resend_all_pending(State0#state{leader = Leader}),
679705
{ok, State, []};
680706
handle_ra_event(_QName, _From, {rejected, {not_leader, Leader, _Seq}},
681707
#state{leader = Leader} = State) ->
682708
{ok, State, []};
683-
handle_ra_event(_QName, _From, {rejected, {not_leader, Leader, _Seq}},
709+
handle_ra_event(QName, _From, {rejected, {not_leader, Leader, _Seq}},
684710
#state{leader = OldLeader,
685711
pending = Pending} = State0) ->
686-
rabbit_log:debug("~ts: Detected QQ leader change (rejection) from ~w to ~w, "
712+
rabbit_log:debug("~ts: ~s Detected QQ leader change (rejection) from ~w to ~w, "
687713
"resending ~b pending commands",
688-
[?MODULE, OldLeader, Leader, maps:size(Pending)]),
714+
[rabbit_misc:rs(QName), ?MODULE, OldLeader,
715+
Leader, maps:size(Pending)]),
689716
State = resend_all_pending(State0#state{leader = Leader}),
690717
{ok, cancel_timer(State), []};
691-
handle_ra_event(_QName, _From, {rejected, {not_leader, _UndefinedMaybe, _Seq}}, State0) ->
718+
handle_ra_event(_QName, _From,
719+
{rejected, {not_leader, _UndefinedMaybe, _Seq}}, State0) ->
692720
% TODO: how should these be handled? re-sent on timer or try random
693721
{ok, State0, []};
694722
handle_ra_event(QName, _, timeout, #state{cfg = #cfg{servers = Servers}} = State0) ->
@@ -700,6 +728,30 @@ handle_ra_event(QName, _, timeout, #state{cfg = #cfg{servers = Servers}} = State
700728
State = resend_all_pending(State0#state{leader = Leader}),
701729
{ok, State, []}
702730
end;
731+
handle_ra_event(QName, Leader, close_cached_segments,
732+
#state{cached_segments = CachedSegments} = State) ->
733+
{ok,
734+
case CachedSegments of
735+
undefined ->
736+
%% timer didn't get cancelled so just ignore this
737+
State;
738+
{_TRef, Last, Cache} ->
739+
case now_ms() > Last + ?CACHE_SEG_TIMEOUT of
740+
true ->
741+
rabbit_log:debug("~ts: closing_cached_segments",
742+
[rabbit_misc:rs(QName)]),
743+
%% its been long enough, evict all
744+
_ = ra_flru:evict_all(Cache),
745+
State#state{cached_segments = undefined};
746+
false ->
747+
%% set another timer
748+
Ref = erlang:send_after(?CACHE_SEG_TIMEOUT, self(),
749+
{'$gen_cast',
750+
{queue_event, QName,
751+
{Leader, close_cached_segments}}}),
752+
State#state{cached_segments = {Ref, Last, Cache}}
753+
end
754+
end, []};
703755
handle_ra_event(_QName, _Leader, {machine, eol}, State) ->
704756
{eol, [{unblock, cluster_name(State)}]}.
705757

@@ -863,7 +915,39 @@ handle_delivery(_QName, _Leader, {delivery, Tag, [_ | _] = IdMsgs},
863915
%% we should return all messages.
864916
MsgIntIds = [Id || {Id, _} <- IdMsgs],
865917
{State1, Deliveries} = return(Tag, MsgIntIds, State0),
866-
{ok, State1, Deliveries}.
918+
{ok, State1, Deliveries};
919+
handle_delivery(QName, Leader, {delivery, Tag, ReadPlan, Msgs},
920+
#state{cached_segments = CachedSegments} = State) ->
921+
{TRef, Cached0} = case CachedSegments of
922+
undefined ->
923+
{undefined, undefined};
924+
{R, _, C} ->
925+
{R, C}
926+
end,
927+
{MsgIds, Cached1} = rabbit_fifo:exec_read(Cached0, ReadPlan, Msgs),
928+
%% if there are cached segments after a read and there
929+
%% is no current timer set, set a timer
930+
%% send a message to evict cache after some time
931+
Cached = case ra_flru:size(Cached1) > 0 of
932+
true when TRef == undefined ->
933+
Ref = erlang:send_after(?CACHE_SEG_TIMEOUT, self(),
934+
{'$gen_cast',
935+
{queue_event, QName,
936+
{Leader, close_cached_segments}}}),
937+
{Ref, now_ms(), Cached1};
938+
true ->
939+
{TRef, now_ms(), Cached1};
940+
false when is_reference(TRef) ->
941+
%% the time is (potentially) alive and may as well be
942+
%% cancelled here
943+
_ = erlang:cancel_timer(TRef, [{async, true},
944+
{info, false}]),
945+
undefined;
946+
false ->
947+
undefined
948+
end,
949+
handle_delivery(QName, Leader, {delivery, Tag, MsgIds},
950+
State#state{cached_segments = Cached}).
867951

868952
transform_msgs(QName, QRef, Msgs) ->
869953
lists:map(
@@ -1032,3 +1116,6 @@ send_pending(Cid, #state{unsent_commands = Unsent} = State0) ->
10321116
normal, S0)
10331117
end, State0, Commands),
10341118
State1#state{unsent_commands = maps:remove(Cid, Unsent)}.
1119+
1120+
now_ms() ->
1121+
erlang:system_time(millisecond).

deps/rabbit/src/rabbit_fifo_dlx.erl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,8 @@ discard(Msgs, Reason, undefined, State) ->
154154
[Reason, rabbit_quorum_queue, disabled, length(Msgs)]}]};
155155
discard(Msgs0, Reason, {at_most_once, {Mod, Fun, Args}}, State) ->
156156
Idxs = [I || ?MSG(I, _) <- Msgs0],
157+
%% TODO: this could be turned into a log_ext effect instead to avoid
158+
%% reading from disk inside the qq process
157159
Effect = {log, Idxs,
158160
fun (Log) ->
159161
Lookup = maps:from_list(lists:zip(Idxs, Log)),

deps/rabbit/src/rabbit_queue_type_util.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ run_checks([C | Checks], Q) ->
7272
Err
7373
end.
7474

75-
-spec erpc_call(node(), module(), atom(), list(), non_neg_integer()) ->
75+
-spec erpc_call(node(), module(), atom(), list(), non_neg_integer() | infinity) ->
7676
term() | {error, term()}.
7777
erpc_call(Node, M, F, A, _Timeout)
7878
when Node =:= node() ->

0 commit comments

Comments
 (0)