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