]>
Commit | Line | Data |
---|---|---|
f67539c2 TL |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | #pragma once | |
4 | ||
5 | #include <string> | |
6 | ||
7 | #include <boost/statechart/custom_reaction.hpp> | |
8 | #include <boost/statechart/deferral.hpp> | |
9 | #include <boost/statechart/event.hpp> | |
10 | #include <boost/statechart/event_base.hpp> | |
11 | #include <boost/statechart/in_state_reaction.hpp> | |
12 | #include <boost/statechart/simple_state.hpp> | |
13 | #include <boost/statechart/state.hpp> | |
14 | #include <boost/statechart/state_machine.hpp> | |
15 | #include <boost/statechart/transition.hpp> | |
16 | ||
17 | #include "common/version.h" | |
18 | #include "include/Context.h" | |
1e59de90 | 19 | #include "osd/scrubber_common.h" |
f67539c2 TL |
20 | |
21 | #include "scrub_machine_lstnr.h" | |
f67539c2 | 22 | |
1e59de90 TL |
23 | /// a wrapper that sets the FSM state description used by the |
24 | /// PgScrubber | |
25 | /// \todo consider using the full NamedState as in Peering | |
26 | struct NamedSimply { | |
27 | explicit NamedSimply(ScrubMachineListener* scrubber, const char* name); | |
28 | }; | |
f67539c2 TL |
29 | |
30 | class PG; // holding a pointer to that one - just for testing | |
31 | class PgScrubber; | |
1e59de90 | 32 | |
f67539c2 TL |
33 | namespace Scrub { |
34 | ||
35 | namespace sc = ::boost::statechart; | |
36 | namespace mpl = ::boost::mpl; | |
37 | ||
38 | // | |
39 | // EVENTS | |
40 | // | |
41 | ||
42 | void on_event_creation(std::string_view nm); | |
43 | void on_event_discard(std::string_view nm); | |
44 | ||
20effc67 TL |
45 | #define MEV(E) \ |
46 | struct E : sc::event<E> { \ | |
47 | inline static int actv{0}; \ | |
48 | E() \ | |
49 | { \ | |
50 | if (!actv++) \ | |
51 | on_event_creation(#E); \ | |
52 | } \ | |
53 | ~E() \ | |
54 | { \ | |
55 | if (!--actv) \ | |
56 | on_event_discard(#E); \ | |
57 | } \ | |
58 | void print(std::ostream* out) const { *out << #E; } \ | |
59 | std::string_view print() const { return #E; } \ | |
f67539c2 TL |
60 | }; |
61 | ||
1e59de90 TL |
62 | /// all replicas have granted our reserve request |
63 | MEV(RemotesReserved) | |
20effc67 | 64 | |
1e59de90 TL |
65 | /// a reservation request has failed |
66 | MEV(ReservationFailure) | |
f67539c2 | 67 | |
1e59de90 TL |
68 | /// initiate a new scrubbing session (relevant if we are a Primary) |
69 | MEV(StartScrub) | |
20effc67 | 70 | |
1e59de90 TL |
71 | /// initiate a new scrubbing session. Only triggered at Recovery completion |
72 | MEV(AfterRepairScrub) | |
20effc67 | 73 | |
1e59de90 TL |
74 | /// triggered when the PG unblocked an object that was marked for scrubbing. |
75 | /// Via the PGScrubUnblocked op | |
76 | MEV(Unblocked) | |
20effc67 | 77 | |
f67539c2 | 78 | MEV(InternalSchedScrub) |
20effc67 | 79 | |
f67539c2 | 80 | MEV(SelectedChunkFree) |
20effc67 | 81 | |
f67539c2 | 82 | MEV(ChunkIsBusy) |
20effc67 | 83 | |
1e59de90 TL |
84 | /// Update to active_pushes. 'active_pushes' represents recovery that |
85 | /// is in-flight to the local ObjectStore | |
86 | MEV(ActivePushesUpd) | |
20effc67 | 87 | |
1e59de90 TL |
88 | /// (Primary only) all updates are committed |
89 | MEV(UpdatesApplied) | |
20effc67 | 90 | |
1e59de90 TL |
91 | /// the internal counterpart of UpdatesApplied |
92 | MEV(InternalAllUpdates) | |
20effc67 | 93 | |
1e59de90 TL |
94 | /// got a map from a replica |
95 | MEV(GotReplicas) | |
f67539c2 | 96 | |
1e59de90 TL |
97 | /// internal - BuildMap preempted. Required, as detected within the ctor |
98 | MEV(IntBmPreempted) | |
20effc67 | 99 | |
f67539c2 TL |
100 | MEV(InternalError) |
101 | ||
102 | MEV(IntLocalMapDone) | |
103 | ||
1e59de90 TL |
104 | /// external. called upon success of a MODIFY op. See |
105 | /// scrub_snapshot_metadata() | |
106 | MEV(DigestUpdate) | |
20effc67 | 107 | |
1e59de90 TL |
108 | /// initiating replica scrub |
109 | MEV(StartReplica) | |
20effc67 | 110 | |
1e59de90 TL |
111 | /// 'start replica' when there are no pending updates |
112 | MEV(StartReplicaNoWait) | |
f67539c2 TL |
113 | |
114 | MEV(SchedReplica) | |
20effc67 | 115 | |
1e59de90 TL |
116 | /// Update to active_pushes. 'active_pushes' represents recovery |
117 | /// that is in-flight to the local ObjectStore | |
118 | MEV(ReplicaPushesUpd) | |
f67539c2 | 119 | |
1e59de90 TL |
120 | /// guarantee that the FSM is in the quiescent state (i.e. NotActive) |
121 | MEV(FullReset) | |
f67539c2 | 122 | |
1e59de90 TL |
123 | /// finished handling this chunk. Go get the next one |
124 | MEV(NextChunk) | |
20effc67 | 125 | |
1e59de90 TL |
126 | /// all chunks handled |
127 | MEV(ScrubFinished) | |
20effc67 | 128 | |
1e59de90 TL |
129 | // |
130 | // STATES | |
131 | // | |
f67539c2 TL |
132 | |
133 | struct NotActive; ///< the quiescent state. No active scrubbing. | |
134 | struct ReservingReplicas; ///< securing scrub resources from replicas' OSDs | |
135 | struct ActiveScrubbing; ///< the active state for a Primary. A sub-machine. | |
1e59de90 TL |
136 | struct ReplicaWaitUpdates; ///< an active state for a replica. Waiting for all |
137 | ///< active operations to finish. | |
f67539c2 TL |
138 | struct ActiveReplica; ///< an active state for a replica. |
139 | ||
140 | ||
141 | class ScrubMachine : public sc::state_machine<ScrubMachine, NotActive> { | |
142 | public: | |
143 | friend class PgScrubber; | |
144 | ||
145 | public: | |
146 | explicit ScrubMachine(PG* pg, ScrubMachineListener* pg_scrub); | |
147 | ~ScrubMachine(); | |
148 | ||
f67539c2 TL |
149 | spg_t m_pg_id; |
150 | ScrubMachineListener* m_scrbr; | |
20effc67 | 151 | std::ostream& gen_prefix(std::ostream& out) const; |
f67539c2 | 152 | |
f67539c2 TL |
153 | void assert_not_active() const; |
154 | [[nodiscard]] bool is_reserving() const; | |
20effc67 | 155 | [[nodiscard]] bool is_accepting_updates() const; |
f67539c2 TL |
156 | }; |
157 | ||
158 | /** | |
159 | * The Scrubber's base (quiescent) state. | |
160 | * Scrubbing is triggered by one of the following events: | |
1e59de90 TL |
161 | * |
162 | * - (standard scenario for a Primary): 'StartScrub'. Initiates the OSDs | |
163 | * resources reservation process. Will be issued by PG::scrub(), following a | |
f67539c2 | 164 | * queued "PGScrub" op. |
20effc67 | 165 | * |
1e59de90 TL |
166 | * - a special end-of-recovery Primary scrub event ('AfterRepairScrub'). |
167 | * | |
168 | * - (for a replica) 'StartReplica' or 'StartReplicaNoWait', triggered by | |
169 | * an incoming MOSDRepScrub message. | |
170 | * | |
171 | * note (20.8.21): originally, AfterRepairScrub was triggering a scrub without | |
172 | * waiting for replica resources to be acquired. But once replicas started | |
173 | * using the resource-request to identify and tag the scrub session, this | |
174 | * bypass cannot be supported anymore. | |
f67539c2 | 175 | */ |
1e59de90 | 176 | struct NotActive : sc::state<NotActive, ScrubMachine>, NamedSimply { |
f67539c2 TL |
177 | explicit NotActive(my_context ctx); |
178 | ||
1e59de90 TL |
179 | using reactions = |
180 | mpl::list<sc::custom_reaction<StartScrub>, | |
181 | // a scrubbing that was initiated at recovery completion: | |
182 | sc::custom_reaction<AfterRepairScrub>, | |
183 | sc::transition<StartReplica, ReplicaWaitUpdates>, | |
184 | sc::transition<StartReplicaNoWait, ActiveReplica>>; | |
20effc67 | 185 | sc::result react(const StartScrub&); |
39ae355f | 186 | sc::result react(const AfterRepairScrub&); |
f67539c2 TL |
187 | }; |
188 | ||
1e59de90 TL |
189 | struct ReservingReplicas : sc::state<ReservingReplicas, ScrubMachine>, |
190 | NamedSimply { | |
f67539c2 TL |
191 | |
192 | explicit ReservingReplicas(my_context ctx); | |
20effc67 | 193 | ~ReservingReplicas(); |
f67539c2 TL |
194 | using reactions = mpl::list<sc::custom_reaction<FullReset>, |
195 | // all replicas granted our resources request | |
196 | sc::transition<RemotesReserved, ActiveScrubbing>, | |
197 | sc::custom_reaction<ReservationFailure>>; | |
198 | ||
199 | sc::result react(const FullReset&); | |
200 | ||
201 | /// at least one replica denied us the scrub resources we've requested | |
202 | sc::result react(const ReservationFailure&); | |
203 | }; | |
204 | ||
205 | ||
206 | // the "active" sub-states | |
207 | ||
1e59de90 TL |
208 | /// the objects range is blocked |
209 | struct RangeBlocked; | |
210 | ||
211 | /// either delaying the scrub by some time and requeuing, or just requeue | |
212 | struct PendingTimer; | |
213 | ||
214 | /// select a chunk to scrub, and verify its availability | |
215 | struct NewChunk; | |
216 | ||
f67539c2 TL |
217 | struct WaitPushes; |
218 | struct WaitLastUpdate; | |
219 | struct BuildMap; | |
1e59de90 TL |
220 | |
221 | /// a problem during BuildMap. Wait for all replicas to report, then restart. | |
222 | struct DrainReplMaps; | |
223 | ||
224 | /// wait for all replicas to report | |
225 | struct WaitReplicas; | |
226 | ||
20effc67 | 227 | struct WaitDigestUpdate; |
f67539c2 | 228 | |
1e59de90 TL |
229 | struct ActiveScrubbing |
230 | : sc::state<ActiveScrubbing, ScrubMachine, PendingTimer>, NamedSimply { | |
f67539c2 TL |
231 | |
232 | explicit ActiveScrubbing(my_context ctx); | |
233 | ~ActiveScrubbing(); | |
234 | ||
1e59de90 TL |
235 | using reactions = mpl::list<sc::custom_reaction<InternalError>, |
236 | sc::custom_reaction<FullReset>>; | |
f67539c2 | 237 | |
f67539c2 TL |
238 | sc::result react(const FullReset&); |
239 | sc::result react(const InternalError&); | |
240 | }; | |
241 | ||
1e59de90 | 242 | struct RangeBlocked : sc::state<RangeBlocked, ActiveScrubbing>, NamedSimply { |
f67539c2 TL |
243 | explicit RangeBlocked(my_context ctx); |
244 | using reactions = mpl::list<sc::transition<Unblocked, PendingTimer>>; | |
20effc67 TL |
245 | |
246 | Scrub::BlockedRangeWarning m_timeout; | |
f67539c2 TL |
247 | }; |
248 | ||
1e59de90 | 249 | struct PendingTimer : sc::state<PendingTimer, ActiveScrubbing>, NamedSimply { |
f67539c2 TL |
250 | |
251 | explicit PendingTimer(my_context ctx); | |
252 | ||
253 | using reactions = mpl::list<sc::transition<InternalSchedScrub, NewChunk>>; | |
254 | }; | |
255 | ||
1e59de90 | 256 | struct NewChunk : sc::state<NewChunk, ActiveScrubbing>, NamedSimply { |
f67539c2 TL |
257 | |
258 | explicit NewChunk(my_context ctx); | |
259 | ||
260 | using reactions = mpl::list<sc::transition<ChunkIsBusy, RangeBlocked>, | |
261 | sc::custom_reaction<SelectedChunkFree>>; | |
262 | ||
263 | sc::result react(const SelectedChunkFree&); | |
264 | }; | |
265 | ||
266 | /** | |
267 | * initiate the update process for this chunk | |
268 | * | |
269 | * Wait fo 'active_pushes' to clear. | |
1e59de90 TL |
270 | * 'active_pushes' represents recovery that is in-flight to the local |
271 | * Objectstore, hence scrub waits until the correct data is readable | |
272 | * (in-flight data to the Objectstore is not readable until written to | |
273 | * disk, termed 'applied' here) | |
f67539c2 | 274 | */ |
1e59de90 | 275 | struct WaitPushes : sc::state<WaitPushes, ActiveScrubbing>, NamedSimply { |
f67539c2 TL |
276 | |
277 | explicit WaitPushes(my_context ctx); | |
278 | ||
279 | using reactions = mpl::list<sc::custom_reaction<ActivePushesUpd>>; | |
280 | ||
281 | sc::result react(const ActivePushesUpd&); | |
282 | }; | |
283 | ||
1e59de90 TL |
284 | struct WaitLastUpdate : sc::state<WaitLastUpdate, ActiveScrubbing>, |
285 | NamedSimply { | |
f67539c2 TL |
286 | |
287 | explicit WaitLastUpdate(my_context ctx); | |
288 | ||
289 | void on_new_updates(const UpdatesApplied&); | |
290 | ||
1e59de90 TL |
291 | using reactions = |
292 | mpl::list<sc::custom_reaction<InternalAllUpdates>, | |
293 | sc::in_state_reaction<UpdatesApplied, | |
294 | WaitLastUpdate, | |
295 | &WaitLastUpdate::on_new_updates>>; | |
f67539c2 TL |
296 | |
297 | sc::result react(const InternalAllUpdates&); | |
298 | }; | |
299 | ||
1e59de90 | 300 | struct BuildMap : sc::state<BuildMap, ActiveScrubbing>, NamedSimply { |
f67539c2 TL |
301 | explicit BuildMap(my_context ctx); |
302 | ||
303 | // possible error scenarios: | |
304 | // - an error reported by the backend will trigger an 'InternalError' event, | |
305 | // handled by our parent state; | |
306 | // - if preempted, we switch to DrainReplMaps, where we will wait for all | |
307 | // replicas to send their maps before acknowledging the preemption; | |
1e59de90 TL |
308 | // - an interval change will be handled by the relevant 'send-event' |
309 | // functions, and will translated into a 'FullReset' event. | |
310 | using reactions = mpl::list<sc::transition<IntBmPreempted, DrainReplMaps>, | |
311 | // looping, waiting for the backend to finish: | |
312 | sc::transition<InternalSchedScrub, BuildMap>, | |
313 | sc::custom_reaction<IntLocalMapDone>>; | |
f67539c2 TL |
314 | |
315 | sc::result react(const IntLocalMapDone&); | |
316 | }; | |
317 | ||
318 | /* | |
319 | * "drain" scrub-maps responses from replicas | |
320 | */ | |
1e59de90 | 321 | struct DrainReplMaps : sc::state<DrainReplMaps, ActiveScrubbing>, NamedSimply { |
f67539c2 TL |
322 | explicit DrainReplMaps(my_context ctx); |
323 | ||
324 | using reactions = | |
1e59de90 TL |
325 | // all replicas are accounted for: |
326 | mpl::list<sc::custom_reaction<GotReplicas>>; | |
f67539c2 TL |
327 | |
328 | sc::result react(const GotReplicas&); | |
329 | }; | |
330 | ||
1e59de90 | 331 | struct WaitReplicas : sc::state<WaitReplicas, ActiveScrubbing>, NamedSimply { |
f67539c2 TL |
332 | explicit WaitReplicas(my_context ctx); |
333 | ||
1e59de90 TL |
334 | using reactions = mpl::list< |
335 | // all replicas are accounted for: | |
336 | sc::custom_reaction<GotReplicas>, | |
337 | sc::custom_reaction<DigestUpdate>>; | |
f67539c2 TL |
338 | |
339 | sc::result react(const GotReplicas&); | |
33c7a0ef | 340 | sc::result react(const DigestUpdate&); |
20effc67 | 341 | bool all_maps_already_called{false}; // see comment in react code |
f67539c2 TL |
342 | }; |
343 | ||
1e59de90 TL |
344 | struct WaitDigestUpdate : sc::state<WaitDigestUpdate, ActiveScrubbing>, |
345 | NamedSimply { | |
f67539c2 TL |
346 | explicit WaitDigestUpdate(my_context ctx); |
347 | ||
20effc67 | 348 | using reactions = mpl::list<sc::custom_reaction<DigestUpdate>, |
1e59de90 TL |
349 | sc::custom_reaction<ScrubFinished>, |
350 | sc::transition<NextChunk, PendingTimer>>; | |
f67539c2 | 351 | sc::result react(const DigestUpdate&); |
20effc67 | 352 | sc::result react(const ScrubFinished&); |
f67539c2 TL |
353 | }; |
354 | ||
1e59de90 | 355 | // ----------------------------- the "replica active" states |
f67539c2 TL |
356 | |
357 | /* | |
358 | * Waiting for 'active_pushes' to complete | |
359 | * | |
360 | * When in this state: | |
361 | * - the details of the Primary's request were internalized by PgScrubber; | |
362 | * - 'active' scrubbing is set | |
363 | */ | |
1e59de90 TL |
364 | struct ReplicaWaitUpdates : sc::state<ReplicaWaitUpdates, ScrubMachine>, |
365 | NamedSimply { | |
f67539c2 | 366 | explicit ReplicaWaitUpdates(my_context ctx); |
1e59de90 TL |
367 | using reactions = mpl::list<sc::custom_reaction<ReplicaPushesUpd>, |
368 | sc::custom_reaction<FullReset>>; | |
f67539c2 TL |
369 | |
370 | sc::result react(const ReplicaPushesUpd&); | |
371 | sc::result react(const FullReset&); | |
372 | }; | |
373 | ||
374 | ||
1e59de90 | 375 | struct ActiveReplica : sc::state<ActiveReplica, ScrubMachine>, NamedSimply { |
f67539c2 | 376 | explicit ActiveReplica(my_context ctx); |
20effc67 | 377 | using reactions = mpl::list<sc::custom_reaction<SchedReplica>, |
1e59de90 | 378 | sc::custom_reaction<FullReset>>; |
f67539c2 TL |
379 | |
380 | sc::result react(const SchedReplica&); | |
381 | sc::result react(const FullReset&); | |
382 | }; | |
383 | ||
384 | } // namespace Scrub |