9
9
10
10
-behaviour (gen_server2 ).
11
11
12
- -export ([start_link /1 , combine / 3 , delete / 2 , no_readers /2 , stop /1 ]).
12
+ -export ([start_link /1 , compact / 2 , truncate / 4 , delete /2 , stop /1 ]).
13
13
14
14
-export ([set_maximum_since_use /2 ]).
15
15
16
16
-export ([init /1 , handle_call /3 , handle_cast /2 , handle_info /2 ,
17
17
terminate /2 , code_change /3 , prioritise_cast /3 ]).
18
18
19
19
-record (state ,
20
- { pending_no_readers ,
21
- on_action ,
20
+ { pending ,
21
+ timer_ref ,
22
22
msg_store_state
23
23
}).
24
24
@@ -33,22 +33,21 @@ start_link(MsgStoreState) ->
33
33
gen_server2 :start_link (? MODULE , [MsgStoreState ],
34
34
[{timeout , infinity }]).
35
35
36
- -spec combine (pid (), rabbit_msg_store :file_num (),
37
- rabbit_msg_store :file_num ()) -> 'ok' .
36
+ -spec compact (pid (), rabbit_msg_store :file_num ()) -> 'ok' .
38
37
39
- combine (Server , Source , Destination ) ->
40
- gen_server2 :cast (Server , {combine , Source , Destination }).
38
+ compact (Server , File ) ->
39
+ gen_server2 :cast (Server , {compact , File }).
40
+
41
+ -spec truncate (pid (), rabbit_msg_store :file_num (), non_neg_integer (), integer ()) -> 'ok' .
42
+
43
+ truncate (Server , File , TruncateSize , ThresholdTimestamp ) ->
44
+ gen_server2 :cast (Server , {truncate , File , TruncateSize , ThresholdTimestamp }).
41
45
42
46
-spec delete (pid (), rabbit_msg_store :file_num ()) -> 'ok' .
43
47
44
48
delete (Server , File ) ->
45
49
gen_server2 :cast (Server , {delete , File }).
46
50
47
- -spec no_readers (pid (), rabbit_msg_store :file_num ()) -> 'ok' .
48
-
49
- no_readers (Server , File ) ->
50
- gen_server2 :cast (Server , {no_readers , File }).
51
-
52
51
-spec stop (pid ()) -> 'ok' .
53
52
54
53
stop (Server ) ->
@@ -64,8 +63,7 @@ set_maximum_since_use(Pid, Age) ->
64
63
init ([MsgStoreState ]) ->
65
64
ok = file_handle_cache :register_callback (? MODULE , set_maximum_since_use ,
66
65
[self ()]),
67
- {ok , # state { pending_no_readers = #{},
68
- on_action = [],
66
+ {ok , # state { pending = #{},
69
67
msg_store_state = MsgStoreState }, hibernate ,
70
68
{backoff , ? HIBERNATE_AFTER_MIN , ? HIBERNATE_AFTER_MIN , ? DESIRED_HIBERNATE }}.
71
69
@@ -75,28 +73,44 @@ prioritise_cast(_Msg, _Len, _State) -> 0.
75
73
handle_call (stop , _From , State ) ->
76
74
{stop , normal , ok , State }.
77
75
78
- handle_cast ({combine , Source , Destination }, State ) ->
79
- {noreply , attempt_action (combine , [Source , Destination ], State ), hibernate };
80
-
81
- handle_cast ({delete , File }, State ) ->
82
- {noreply , attempt_action (delete , [File ], State ), hibernate };
83
-
84
- handle_cast ({no_readers , File },
85
- State = # state { pending_no_readers = Pending }) ->
86
- {noreply , case maps :find (File , Pending ) of
87
- error ->
88
- State ;
89
- {ok , {Action , Files }} ->
90
- Pending1 = maps :remove (File , Pending ),
91
- attempt_action (
92
- Action , Files ,
93
- State # state { pending_no_readers = Pending1 })
94
- end , hibernate };
76
+ handle_cast ({compact , File }, State ) ->
77
+ % % Since we don't compact files that have a valid size of 0,
78
+ % % we cannot have a delete queued at the same time as we are
79
+ % % asked to compact. We can always compact.
80
+ {noreply , attempt_action (compact , [File ], State ), hibernate };
81
+
82
+ handle_cast ({truncate , File , TruncateSize , ThresholdTimestamp }, State = # state {pending = Pending }) ->
83
+ case Pending of
84
+ % % No need to truncate if we are going to delete.
85
+ #{File := {delete , _ }} ->
86
+ {noreply , State , hibernate };
87
+ % % Attempt to truncate otherwise. If a truncate was already
88
+ % % scheduled we drop it in favor of the new truncate.
89
+ _ ->
90
+ State1 = State # state {pending = maps :remove (File , Pending )},
91
+ {noreply , attempt_action (truncate , [File , TruncateSize , ThresholdTimestamp ], State1 ), hibernate }
92
+ end ;
93
+
94
+ handle_cast ({delete , File }, State = # state {pending = Pending }) ->
95
+ % % We drop any pending action because deletion takes precedence over truncation.
96
+ State1 = State # state {pending = maps :remove (File , Pending )},
97
+ {noreply , attempt_action (delete , [File ], State1 ), hibernate };
95
98
96
99
handle_cast ({set_maximum_since_use , Age }, State ) ->
97
100
ok = file_handle_cache :set_maximum_since_use (Age ),
98
101
{noreply , State , hibernate }.
99
102
103
+ % % Run all pending actions.
104
+ handle_info ({timeout , TimerRef , do_pending },
105
+ State = # state { pending = Pending ,
106
+ timer_ref = TimerRef }) ->
107
+ State1 = State # state { pending = #{},
108
+ timer_ref = undefined },
109
+ State2 = maps :fold (fun (_File , {Action , Args }, StateFold ) ->
110
+ attempt_action (Action , Args , StateFold )
111
+ end , State1 , Pending ),
112
+ {noreply , State2 , hibernate };
113
+
100
114
handle_info (Info , State ) ->
101
115
{stop , {unhandled_info , Info }, State }.
102
116
@@ -106,20 +120,27 @@ terminate(_Reason, State) ->
106
120
code_change (_OldVsn , State , _Extra ) ->
107
121
{ok , State }.
108
122
109
- attempt_action (Action , Files ,
110
- State = # state { pending_no_readers = Pending ,
111
- on_action = Thunks ,
123
+ attempt_action (Action , Args ,
124
+ State = # state { pending = Pending ,
112
125
msg_store_state = MsgStoreState }) ->
113
- case do_action (Action , Files , MsgStoreState ) of
114
- { ok , OkThunk } ->
115
- State # state { on_action = lists : filter ( fun ( Thunk ) -> not Thunk () end ,
116
- [ OkThunk | Thunks ])};
117
- { defer , [File | _ ]} ->
118
- Pending1 = maps :put (File , {Action , Files }, Pending ),
119
- State # state { pending_no_readers = Pending1 }
126
+ case do_action (Action , Args , MsgStoreState ) of
127
+ ok ->
128
+ State ;
129
+ defer ->
130
+ [File | _ ] = Args ,
131
+ Pending1 = maps :put (File , {Action , Args }, Pending ),
132
+ ensure_pending_timer ( State # state { pending = Pending1 })
120
133
end .
121
134
122
- do_action (combine , [Source , Destination ], MsgStoreState ) ->
123
- rabbit_msg_store :combine_files (Source , Destination , MsgStoreState );
135
+ do_action (compact , [File ], MsgStoreState ) ->
136
+ rabbit_msg_store :compact_file (File , MsgStoreState );
137
+ do_action (truncate , [File , Size , ThresholdTimestamp ], MsgStoreState ) ->
138
+ rabbit_msg_store :truncate_file (File , Size , ThresholdTimestamp , MsgStoreState );
124
139
do_action (delete , [File ], MsgStoreState ) ->
125
140
rabbit_msg_store :delete_file (File , MsgStoreState ).
141
+
142
+ ensure_pending_timer (State = # state {timer_ref = undefined }) ->
143
+ TimerRef = erlang :start_timer (5000 , self (), do_pending ),
144
+ State # state {timer_ref = TimerRef };
145
+ ensure_pending_timer (State ) ->
146
+ State .
0 commit comments