|
91 | 91 | flying_ets, |
92 | 92 | %% set of dying clients |
93 | 93 | dying_clients, |
| 94 | + %% index of file positions for client death messages |
| 95 | + dying_client_index, |
94 | 96 | %% map of references of all registered clients |
95 | 97 | %% to callbacks |
96 | 98 | clients, |
|
131 | 133 | msg_store |
132 | 134 | }). |
133 | 135 |
|
| 136 | +-record(dying_client, |
| 137 | + { client_ref, |
| 138 | + file, |
| 139 | + offset |
| 140 | + }). |
| 141 | + |
134 | 142 | %%---------------------------------------------------------------------------- |
135 | 143 |
|
136 | 144 | -export_type([gc_state/0, file_num/0]). |
|
416 | 424 | %% performance with many healthy clients and few, if any, dying |
417 | 425 | %% clients, which is the typical case. |
418 | 426 | %% |
| 427 | +%% Client termination messages are stored in a separate ets index to |
| 428 | +%% avoid filling primary message store index and message files with |
| 429 | +%% client termination messages. |
| 430 | +%% |
419 | 431 | %% When the msg_store has a backlog (i.e. it has unprocessed messages |
420 | 432 | %% in its mailbox / gen_server priority queue), a further optimisation |
421 | 433 | %% opportunity arises: we can eliminate pairs of 'write' and 'remove' |
@@ -687,7 +699,9 @@ client_update_flying(Diff, MsgId, #client_msstate { flying_ets = FlyingEts, |
687 | 699 | end. |
688 | 700 |
|
689 | 701 | clear_client(CRef, State = #msstate { cref_to_msg_ids = CTM, |
690 | | - dying_clients = DyingClients }) -> |
| 702 | + dying_clients = DyingClients, |
| 703 | + dying_client_index = DyingIndex }) -> |
| 704 | + ets:delete(DyingIndex, CRef), |
691 | 705 | State #msstate { cref_to_msg_ids = dict:erase(CRef, CTM), |
692 | 706 | dying_clients = sets:del_element(CRef, DyingClients) }. |
693 | 707 |
|
@@ -741,6 +755,8 @@ init([Server, BaseDir, ClientRefs, StartupFunState]) -> |
741 | 755 | [ordered_set, public]), |
742 | 756 | CurFileCacheEts = ets:new(rabbit_msg_store_cur_file, [set, public]), |
743 | 757 | FlyingEts = ets:new(rabbit_msg_store_flying, [set, public]), |
| 758 | + DyingIndex = ets:new(rabbit_msg_store_dying_client_index, |
| 759 | + [set, public, {keypos, #dying_client.client_ref}]), |
744 | 760 |
|
745 | 761 | {ok, FileSizeLimit} = application:get_env(msg_store_file_size_limit), |
746 | 762 |
|
@@ -772,6 +788,7 @@ init([Server, BaseDir, ClientRefs, StartupFunState]) -> |
772 | 788 | cur_file_cache_ets = CurFileCacheEts, |
773 | 789 | flying_ets = FlyingEts, |
774 | 790 | dying_clients = sets:new(), |
| 791 | + dying_client_index = DyingIndex, |
775 | 792 | clients = Clients, |
776 | 793 | successfully_recovered = CleanShutdown, |
777 | 794 | file_size_limit = FileSizeLimit, |
@@ -848,15 +865,21 @@ handle_call({contains, MsgId}, From, State) -> |
848 | 865 | noreply(State1). |
849 | 866 |
|
850 | 867 | handle_cast({client_dying, CRef}, |
851 | | - State = #msstate { dying_clients = DyingClients }) -> |
| 868 | + State = #msstate { dying_clients = DyingClients, |
| 869 | + dying_client_index = DyingIndex, |
| 870 | + current_file_handle = CurHdl, |
| 871 | + current_file = CurFile }) -> |
852 | 872 | DyingClients1 = sets:add_element(CRef, DyingClients), |
853 | | - noreply(write_message(CRef, <<>>, |
854 | | - State #msstate { dying_clients = DyingClients1 })); |
| 873 | + {ok, CurOffset} = file_handle_cache:current_virtual_offset(CurHdl), |
| 874 | + true = ets:insert_new(DyingIndex, #dying_client{client_ref = CRef, |
| 875 | + file = CurFile, |
| 876 | + offset = CurOffset}), |
| 877 | + noreply(State #msstate { dying_clients = DyingClients1 }); |
855 | 878 |
|
856 | 879 | handle_cast({client_delete, CRef}, |
857 | 880 | State = #msstate { clients = Clients }) -> |
858 | 881 | State1 = State #msstate { clients = dict:erase(CRef, Clients) }, |
859 | | - noreply(remove_message(CRef, CRef, clear_client(CRef, State1))); |
| 882 | + noreply(clear_client(CRef, State1)); |
860 | 883 |
|
861 | 884 | handle_cast({write, CRef, MsgId, Flow}, |
862 | 885 | State = #msstate { cur_file_cache_ets = CurFileCacheEts, |
@@ -1334,16 +1357,17 @@ blind_confirm(CRef, MsgIds, ActionTaken, State) -> |
1334 | 1357 | %% msg and thus should be ignored. Note that this (correctly) returns |
1335 | 1358 | %% false when testing to remove the death msg itself. |
1336 | 1359 | should_mask_action(CRef, MsgId, |
1337 | | - State = #msstate { dying_clients = DyingClients }) -> |
| 1360 | + State = #msstate { dying_clients = DyingClients, |
| 1361 | + dying_client_index = DyingIndex }) -> |
1338 | 1362 | case {sets:is_element(CRef, DyingClients), index_lookup(MsgId, State)} of |
1339 | 1363 | {false, Location} -> |
1340 | 1364 | {false, Location}; |
1341 | 1365 | {true, not_found} -> |
1342 | 1366 | {true, not_found}; |
1343 | 1367 | {true, #msg_location { file = File, offset = Offset, |
1344 | 1368 | ref_count = RefCount } = Location} -> |
1345 | | - #msg_location { file = DeathFile, offset = DeathOffset } = |
1346 | | - index_lookup(CRef, State), |
| 1369 | + [#dying_client { file = DeathFile, offset = DeathOffset }] = |
| 1370 | + ets:lookup(DyingIndex, CRef), |
1347 | 1371 | {case {{DeathFile, DeathOffset} < {File, Offset}, RefCount} of |
1348 | 1372 | {true, _} -> true; |
1349 | 1373 | {false, 0} -> false_if_increment; |
|
0 commit comments