]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | /* | |
4 | * Ceph - scalable distributed file system | |
5 | * | |
6 | * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net> | |
7 | * | |
8 | * This is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU Lesser General Public | |
10 | * License version 2.1, as published by the Free Software | |
11 | * Foundation. See file COPYING. | |
12 | * | |
13 | */ | |
14 | ||
15 | #ifndef CEPH_PAXOSSERVICE_H | |
16 | #define CEPH_PAXOSSERVICE_H | |
17 | ||
18 | #include "include/Context.h" | |
19 | #include "Paxos.h" | |
20 | #include "Monitor.h" | |
21 | #include "MonitorDBStore.h" | |
22 | ||
23 | class Monitor; | |
24 | class Paxos; | |
25 | ||
26 | /** | |
27 | * A Paxos Service is an abstraction that easily allows one to obtain an | |
28 | * association between a Monitor and a Paxos class, in order to implement any | |
29 | * service. | |
30 | */ | |
31 | class PaxosService { | |
32 | /** | |
33 | * @defgroup PaxosService_h_class Paxos Service | |
34 | * @{ | |
35 | */ | |
36 | public: | |
37 | /** | |
38 | * The Monitor to which this class is associated with | |
39 | */ | |
40 | Monitor *mon; | |
41 | /** | |
42 | * The Paxos instance to which this class is associated with | |
43 | */ | |
44 | Paxos *paxos; | |
45 | /** | |
46 | * Our name. This will be associated with the class implementing us, and will | |
47 | * be used mainly for store-related operations. | |
48 | */ | |
49 | string service_name; | |
50 | /** | |
51 | * If we are or have queued anything for proposal, this variable will be true | |
52 | * until our proposal has been finished. | |
53 | */ | |
54 | bool proposing; | |
55 | ||
31f18b77 FG |
56 | bool need_immediate_propose = false; |
57 | ||
58 | protected: | |
7c673cae FG |
59 | /** |
60 | * Services implementing us used to depend on the Paxos version, back when | |
61 | * each service would have a Paxos instance for itself. However, now we only | |
62 | * have a single Paxos instance, shared by all the services. Each service now | |
63 | * must keep its own version, if so they wish. This variable should be used | |
64 | * for that purpose. | |
65 | */ | |
66 | version_t service_version; | |
67 | ||
68 | private: | |
69 | /** | |
70 | * Event callback responsible for proposing our pending value once a timer | |
71 | * runs out and fires. | |
72 | */ | |
73 | Context *proposal_timer; | |
74 | /** | |
75 | * If the implementation class has anything pending to be proposed to Paxos, | |
76 | * then have_pending should be true; otherwise, false. | |
77 | */ | |
78 | bool have_pending; | |
79 | ||
224ce89b WB |
80 | /** |
81 | * health checks for this service | |
82 | * | |
83 | * Child must populate this during encode_pending() by calling encode_health(). | |
84 | */ | |
85 | health_check_map_t health_checks; | |
86 | public: | |
87 | const health_check_map_t& get_health_checks() { | |
88 | return health_checks; | |
89 | } | |
7c673cae | 90 | |
224ce89b | 91 | protected: |
7c673cae FG |
92 | /** |
93 | * format of our state in leveldb, 0 for default | |
94 | */ | |
95 | version_t format_version; | |
96 | ||
7c673cae FG |
97 | /** |
98 | * @defgroup PaxosService_h_callbacks Callback classes | |
99 | * @{ | |
100 | */ | |
101 | /** | |
102 | * Retry dispatching a given service message | |
103 | * | |
104 | * This callback class is used when we had to wait for some condition to | |
105 | * become true while we were dispatching it. | |
106 | * | |
107 | * For instance, if the message's version isn't readable, according to Paxos, | |
108 | * then we must wait for it to become readable. So, we just queue an | |
109 | * instance of this class onto the Paxos::wait_for_readable function, and | |
110 | * we will retry the whole dispatch again once the callback is fired. | |
111 | */ | |
112 | class C_RetryMessage : public C_MonOp { | |
113 | PaxosService *svc; | |
114 | public: | |
115 | C_RetryMessage(PaxosService *s, MonOpRequestRef op_) : | |
116 | C_MonOp(op_), svc(s) { } | |
117 | void _finish(int r) override { | |
118 | if (r == -EAGAIN || r >= 0) | |
119 | svc->dispatch(op); | |
120 | else if (r == -ECANCELED) | |
121 | return; | |
122 | else | |
123 | assert(0 == "bad C_RetryMessage return value"); | |
124 | } | |
125 | }; | |
126 | ||
127 | /** | |
128 | * @} | |
129 | */ | |
130 | ||
131 | public: | |
132 | /** | |
133 | * @param mn A Monitor instance | |
134 | * @param p A Paxos instance | |
135 | * @param name Our service's name. | |
136 | */ | |
137 | PaxosService(Monitor *mn, Paxos *p, string name) | |
138 | : mon(mn), paxos(p), service_name(name), | |
139 | proposing(false), | |
140 | service_version(0), proposal_timer(0), have_pending(false), | |
141 | format_version(0), | |
142 | last_committed_name("last_committed"), | |
143 | first_committed_name("first_committed"), | |
144 | full_prefix_name("full"), full_latest_name("latest"), | |
145 | cached_first_committed(0), cached_last_committed(0) | |
146 | { | |
147 | } | |
148 | ||
149 | virtual ~PaxosService() {} | |
150 | ||
151 | /** | |
152 | * Get the service's name. | |
153 | * | |
154 | * @returns The service's name. | |
155 | */ | |
156 | string get_service_name() { return service_name; } | |
157 | ||
158 | /** | |
159 | * Get the store prefixes we utilize | |
160 | */ | |
161 | virtual void get_store_prefixes(set<string>& s) { | |
162 | s.insert(service_name); | |
163 | } | |
164 | ||
165 | // i implement and you ignore | |
166 | /** | |
167 | * Informs this instance that it should consider itself restarted. | |
168 | * | |
169 | * This means that we will cancel our proposal_timer event, if any exists. | |
170 | */ | |
171 | void restart(); | |
172 | /** | |
173 | * Informs this instance that an election has finished. | |
174 | * | |
175 | * This means that we will invoke a PaxosService::discard_pending while | |
176 | * setting have_pending to false (basically, ignore our pending state) and | |
177 | * we will then make sure we obtain a new state. | |
178 | * | |
179 | * Our state shall be updated by PaxosService::_active if the Paxos is | |
180 | * active; otherwise, we will wait for it to become active by adding a | |
181 | * PaxosService::C_Active callback to it. | |
182 | */ | |
183 | void election_finished(); | |
184 | /** | |
185 | * Informs this instance that it is supposed to shutdown. | |
186 | * | |
187 | * Basically, it will instruct Paxos to cancel all events/callbacks and then | |
188 | * will cancel the proposal_timer event if any exists. | |
189 | */ | |
190 | void shutdown(); | |
191 | ||
192 | private: | |
193 | /** | |
194 | * Update our state by updating it from Paxos, and then creating a new | |
195 | * pending state if need be. | |
196 | * | |
197 | * @remarks We only create a pending state we our Monitor is the Leader. | |
198 | * | |
199 | * @pre Paxos is active | |
31f18b77 | 200 | * @post have_pending is true if our Monitor is the Leader and Paxos is |
7c673cae FG |
201 | * active |
202 | */ | |
203 | void _active(); | |
204 | ||
205 | public: | |
206 | /** | |
207 | * Propose a new value through Paxos. | |
208 | * | |
209 | * This function should be called by the classes implementing | |
210 | * PaxosService, in order to propose a new value through Paxos. | |
211 | * | |
212 | * @pre The implementation class implements the encode_pending function. | |
213 | * @pre have_pending is true | |
214 | * @pre Our monitor is the Leader | |
215 | * @pre Paxos is active | |
216 | * @post Cancel the proposal timer, if any | |
217 | * @post have_pending is false | |
218 | * @post propose pending value through Paxos | |
219 | * | |
220 | * @note This function depends on the implementation of encode_pending on | |
221 | * the class that is implementing PaxosService | |
222 | */ | |
223 | void propose_pending(); | |
224 | ||
225 | /** | |
226 | * Let others request us to propose. | |
227 | * | |
228 | * At the moment, this is just a wrapper to propose_pending() with an | |
229 | * extra check for is_writeable(), but it's a good practice to dissociate | |
230 | * requests for proposals from direct usage of propose_pending() for | |
231 | * future use -- we might want to perform additional checks or put a | |
232 | * request on hold, for instance. | |
233 | */ | |
234 | void request_proposal() { | |
235 | assert(is_writeable()); | |
236 | ||
237 | propose_pending(); | |
238 | } | |
239 | /** | |
240 | * Request service @p other to perform a proposal. | |
241 | * | |
242 | * We could simply use the function above, requesting @p other directly, | |
243 | * but we might eventually want to do something to the request -- say, | |
244 | * set a flag stating we're waiting on a cross-proposal to be finished. | |
245 | */ | |
246 | void request_proposal(PaxosService *other) { | |
247 | assert(other != NULL); | |
248 | assert(other->is_writeable()); | |
249 | ||
250 | other->request_proposal(); | |
251 | } | |
252 | ||
253 | /** | |
254 | * Dispatch a message by passing it to several different functions that are | |
255 | * either implemented directly by this service, or that should be implemented | |
256 | * by the class implementing this service. | |
257 | * | |
258 | * @param m A message | |
259 | * @returns 'true' on successful dispatch; 'false' otherwise. | |
260 | */ | |
261 | bool dispatch(MonOpRequestRef op); | |
262 | ||
263 | void refresh(bool *need_bootstrap); | |
264 | void post_refresh(); | |
265 | ||
266 | /** | |
267 | * @defgroup PaxosService_h_override_funcs Functions that should be | |
268 | * overridden. | |
269 | * | |
270 | * These functions should be overridden at will by the class implementing | |
271 | * this service. | |
272 | * @{ | |
273 | */ | |
274 | /** | |
275 | * Create the initial state for your system. | |
276 | * | |
277 | * In some of ours the state is actually set up elsewhere so this does | |
278 | * nothing. | |
279 | */ | |
280 | virtual void create_initial() = 0; | |
281 | ||
282 | /** | |
283 | * Query the Paxos system for the latest state and apply it if it's newer | |
284 | * than the current Monitor state. | |
285 | */ | |
286 | virtual void update_from_paxos(bool *need_bootstrap) = 0; | |
287 | ||
288 | /** | |
289 | * Hook called after all services have refreshed their state from paxos | |
290 | * | |
291 | * This is useful for doing any update work that depends on other | |
292 | * service's having up-to-date state. | |
293 | */ | |
294 | virtual void post_paxos_update() {} | |
295 | ||
296 | /** | |
297 | * Init on startup | |
298 | * | |
299 | * This is called on mon startup, after all of the PaxosService instances' | |
300 | * update_from_paxos() methods have been called | |
301 | */ | |
302 | virtual void init() {} | |
303 | ||
304 | /** | |
305 | * Create the pending state. | |
306 | * | |
307 | * @invariant This function is only called on a Leader. | |
308 | * @remarks This created state is then modified by incoming messages. | |
309 | * @remarks Called at startup and after every Paxos ratification round. | |
310 | */ | |
311 | virtual void create_pending() = 0; | |
312 | ||
313 | /** | |
314 | * Encode the pending state into a bufferlist for ratification and | |
315 | * transmission as the next state. | |
316 | * | |
317 | * @invariant This function is only called on a Leader. | |
318 | * | |
319 | * @param t The transaction to hold all changes. | |
320 | */ | |
321 | virtual void encode_pending(MonitorDBStore::TransactionRef t) = 0; | |
322 | ||
323 | /** | |
324 | * Discard the pending state | |
325 | * | |
326 | * @invariant This function is only called on a Leader. | |
327 | * | |
328 | * @remarks This function is NOT overridden in any of our code, but it is | |
329 | * called in PaxosService::election_finished if have_pending is | |
330 | * true. | |
331 | */ | |
332 | virtual void discard_pending() { } | |
333 | ||
334 | /** | |
335 | * Look at the query; if the query can be handled without changing state, | |
336 | * do so. | |
337 | * | |
338 | * @param m A query message | |
339 | * @returns 'true' if the query was handled (e.g., was a read that got | |
340 | * answered, was a state change that has no effect); 'false' | |
341 | * otherwise. | |
342 | */ | |
343 | virtual bool preprocess_query(MonOpRequestRef op) = 0; | |
344 | ||
345 | /** | |
346 | * Apply the message to the pending state. | |
347 | * | |
348 | * @invariant This function is only called on a Leader. | |
349 | * | |
350 | * @param m An update message | |
351 | * @returns 'true' if the update message was handled (e.g., a command that | |
352 | * went through); 'false' otherwise. | |
353 | */ | |
354 | virtual bool prepare_update(MonOpRequestRef op) = 0; | |
355 | /** | |
356 | * @} | |
357 | */ | |
358 | ||
359 | /** | |
360 | * Determine if the Paxos system should vote on pending, and if so how long | |
361 | * it should wait to vote. | |
362 | * | |
363 | * @param[out] delay The wait time, used so we can limit the update traffic | |
364 | * spamming. | |
365 | * @returns 'true' if the Paxos system should propose; 'false' otherwise. | |
366 | */ | |
367 | virtual bool should_propose(double &delay); | |
368 | ||
31f18b77 FG |
369 | /** |
370 | * force an immediate propose. | |
371 | * | |
372 | * This is meant to be called from prepare_update(op). | |
373 | */ | |
374 | void force_immediate_propose() { | |
375 | need_immediate_propose = true; | |
376 | } | |
377 | ||
7c673cae FG |
378 | /** |
379 | * @defgroup PaxosService_h_courtesy Courtesy functions | |
380 | * | |
381 | * Courtesy functions, in case the class implementing this service has | |
382 | * anything it wants/needs to do at these times. | |
383 | * @{ | |
384 | */ | |
385 | /** | |
386 | * This is called when the Paxos state goes to active. | |
387 | * | |
388 | * On the peon, this is after each election. | |
389 | * On the leader, this is after each election, *and* after each completed | |
390 | * proposal. | |
391 | * | |
392 | * @note This function may get called twice in certain recovery cases. | |
393 | */ | |
394 | virtual void on_active() { } | |
395 | ||
396 | /** | |
397 | * This is called when we are shutting down | |
398 | */ | |
399 | virtual void on_shutdown() {} | |
400 | ||
401 | /** | |
402 | * this is called when activating on the leader | |
403 | * | |
404 | * it should conditionally upgrade the on-disk format by proposing a transaction | |
405 | */ | |
406 | virtual void upgrade_format() { } | |
407 | ||
408 | /** | |
409 | * this is called when we detect the store has just upgraded underneath us | |
410 | */ | |
411 | virtual void on_upgrade() {} | |
412 | ||
413 | /** | |
414 | * Called when the Paxos system enters a Leader election. | |
415 | * | |
416 | * @remarks It's a courtesy method, in case the class implementing this | |
417 | * service has anything it wants/needs to do at that time. | |
418 | */ | |
419 | virtual void on_restart() { } | |
420 | /** | |
421 | * @} | |
422 | */ | |
423 | ||
424 | /** | |
425 | * Tick. | |
426 | */ | |
427 | virtual void tick() {} | |
428 | ||
429 | /** | |
430 | * Get health information | |
431 | * | |
432 | * @param summary list of summary strings and associated severity | |
433 | * @param detail optional list of detailed problem reports; may be NULL | |
434 | */ | |
435 | virtual void get_health(list<pair<health_status_t,string> >& summary, | |
436 | list<pair<health_status_t,string> > *detail, | |
437 | CephContext *cct) const { } | |
438 | ||
224ce89b WB |
439 | void encode_health(const health_check_map_t& next, |
440 | MonitorDBStore::TransactionRef t) { | |
441 | bufferlist bl; | |
442 | ::encode(next, bl); | |
443 | t->put("health", service_name, bl); | |
444 | mon->log_health(next, health_checks, t); | |
445 | } | |
446 | void load_health(); | |
447 | ||
7c673cae FG |
448 | private: |
449 | /** | |
450 | * @defgroup PaxosService_h_store_keys Set of keys that are usually used on | |
451 | * all the services implementing this | |
452 | * class, and, being almost the only keys | |
453 | * used, should be standardized to avoid | |
454 | * mistakes. | |
455 | * @{ | |
456 | */ | |
457 | const string last_committed_name; | |
458 | const string first_committed_name; | |
459 | const string full_prefix_name; | |
460 | const string full_latest_name; | |
461 | /** | |
462 | * @} | |
463 | */ | |
464 | ||
465 | /** | |
466 | * @defgroup PaxosService_h_version_cache Variables holding cached values | |
467 | * for the most used versions (first | |
468 | * and last committed); we only have | |
469 | * to read them when the store is | |
470 | * updated, so in-between updates we | |
471 | * may very well use cached versions | |
472 | * and avoid the overhead. | |
473 | * @{ | |
474 | */ | |
475 | version_t cached_first_committed; | |
476 | version_t cached_last_committed; | |
477 | /** | |
478 | * @} | |
479 | */ | |
480 | ||
481 | /** | |
482 | * Callback list to be used whenever we are running a proposal through | |
483 | * Paxos. These callbacks will be awaken whenever the said proposal | |
484 | * finishes. | |
485 | */ | |
486 | list<Context*> waiting_for_finished_proposal; | |
487 | ||
488 | public: | |
489 | ||
490 | /** | |
491 | * Check if we are proposing a value through Paxos | |
492 | * | |
493 | * @returns true if we are proposing; false otherwise. | |
494 | */ | |
495 | bool is_proposing() { | |
496 | return proposing; | |
497 | } | |
498 | ||
499 | /** | |
500 | * Check if we are in the Paxos ACTIVE state. | |
501 | * | |
502 | * @note This function is a wrapper for Paxos::is_active | |
503 | * | |
504 | * @returns true if in state ACTIVE; false otherwise. | |
505 | */ | |
506 | bool is_active() { | |
507 | return | |
508 | !is_proposing() && | |
509 | (paxos->is_active() || paxos->is_updating() || paxos->is_writing()); | |
510 | } | |
511 | ||
512 | /** | |
513 | * Check if we are readable. | |
514 | * | |
515 | * This mirrors on the paxos check, except that we also verify that | |
516 | * | |
517 | * - the client hasn't seen the future relative to this PaxosService | |
518 | * - this service isn't proposing. | |
519 | * - we have committed our initial state (last_committed > 0) | |
520 | * | |
521 | * @param ver The version we want to check if is readable | |
522 | * @returns true if it is readable; false otherwise | |
523 | */ | |
524 | bool is_readable(version_t ver = 0) { | |
525 | if (ver > get_last_committed() || | |
526 | !paxos->is_readable(0) || | |
527 | get_last_committed() == 0) | |
528 | return false; | |
529 | return true; | |
530 | } | |
531 | ||
532 | /** | |
533 | * Check if we are writeable. | |
534 | * | |
535 | * We consider to be writeable iff: | |
536 | * | |
537 | * - we are not proposing a new version; | |
538 | * - we are ready to be written to -- i.e., we have a pending value. | |
539 | * - paxos is (active or updating or writing or refresh) | |
540 | * | |
541 | * @returns true if writeable; false otherwise | |
542 | */ | |
543 | bool is_writeable() { | |
544 | return is_write_ready(); | |
545 | } | |
546 | ||
547 | /** | |
548 | * Check if we are ready to be written to. This means we must have a | |
549 | * pending value and be active. | |
550 | * | |
551 | * @returns true if we are ready to be written to; false otherwise. | |
552 | */ | |
553 | bool is_write_ready() { | |
554 | return is_active() && have_pending; | |
555 | } | |
556 | ||
557 | /** | |
558 | * Wait for a proposal to finish. | |
559 | * | |
560 | * Add a callback to be awaken whenever our current proposal finishes being | |
561 | * proposed through Paxos. | |
562 | * | |
563 | * @param c The callback to be awaken once the proposal is finished. | |
564 | */ | |
565 | void wait_for_finished_proposal(MonOpRequestRef op, Context *c) { | |
566 | if (op) | |
567 | op->mark_event_string(service_name + ":wait_for_finished_proposal"); | |
568 | waiting_for_finished_proposal.push_back(c); | |
569 | } | |
570 | void wait_for_finished_proposal_ctx(Context *c) { | |
571 | MonOpRequestRef o; | |
572 | wait_for_finished_proposal(o, c); | |
573 | } | |
574 | ||
575 | /** | |
576 | * Wait for us to become active | |
577 | * | |
578 | * @param c The callback to be awaken once we become active. | |
579 | */ | |
580 | void wait_for_active(MonOpRequestRef op, Context *c) { | |
581 | if (op) | |
582 | op->mark_event_string(service_name + ":wait_for_active"); | |
583 | ||
584 | if (!is_proposing()) { | |
585 | paxos->wait_for_active(op, c); | |
586 | return; | |
587 | } | |
588 | wait_for_finished_proposal(op, c); | |
589 | } | |
590 | void wait_for_active_ctx(Context *c) { | |
591 | MonOpRequestRef o; | |
592 | wait_for_active(o, c); | |
593 | } | |
594 | ||
595 | /** | |
596 | * Wait for us to become readable | |
597 | * | |
598 | * @param c The callback to be awaken once we become active. | |
599 | * @param ver The version we want to wait on. | |
600 | */ | |
601 | void wait_for_readable(MonOpRequestRef op, Context *c, version_t ver = 0) { | |
602 | /* This is somewhat of a hack. We only do check if a version is readable on | |
603 | * PaxosService::dispatch(), but, nonetheless, we must make sure that if that | |
604 | * is why we are not readable, then we must wait on PaxosService and not on | |
605 | * Paxos; otherwise, we may assert on Paxos::wait_for_readable() if it | |
606 | * happens to be readable at that specific point in time. | |
607 | */ | |
608 | if (op) | |
609 | op->mark_event_string(service_name + ":wait_for_readable"); | |
610 | ||
611 | if (is_proposing() || | |
612 | ver > get_last_committed() || | |
613 | get_last_committed() == 0) | |
614 | wait_for_finished_proposal(op, c); | |
615 | else { | |
616 | if (op) | |
617 | op->mark_event_string(service_name + ":wait_for_readable/paxos"); | |
618 | ||
619 | paxos->wait_for_readable(op, c); | |
620 | } | |
621 | } | |
622 | ||
623 | void wait_for_readable_ctx(Context *c, version_t ver = 0) { | |
624 | MonOpRequestRef o; // will initialize the shared_ptr to NULL | |
625 | wait_for_readable(o, c, ver); | |
626 | } | |
627 | ||
628 | /** | |
629 | * Wait for us to become writeable | |
630 | * | |
631 | * @param c The callback to be awaken once we become writeable. | |
632 | */ | |
633 | void wait_for_writeable(MonOpRequestRef op, Context *c) { | |
634 | if (op) | |
635 | op->mark_event_string(service_name + ":wait_for_writeable"); | |
636 | ||
637 | if (is_proposing()) | |
638 | wait_for_finished_proposal(op, c); | |
639 | else if (!is_write_ready()) | |
640 | wait_for_active(op, c); | |
641 | else | |
642 | paxos->wait_for_writeable(op, c); | |
643 | } | |
644 | void wait_for_writeable_ctx(Context *c) { | |
645 | MonOpRequestRef o; | |
646 | wait_for_writeable(o, c); | |
647 | } | |
648 | ||
649 | ||
650 | /** | |
651 | * @defgroup PaxosService_h_Trim Functions for trimming states | |
652 | * @{ | |
653 | */ | |
654 | /** | |
655 | * trim service states if appropriate | |
656 | * | |
657 | * Called at same interval as tick() | |
658 | */ | |
659 | void maybe_trim(); | |
660 | ||
661 | /** | |
662 | * Auxiliary function to trim our state from version @p from to version | |
663 | * @p to, not including; i.e., the interval [from, to[ | |
664 | * | |
665 | * @param t The transaction to which we will add the trim operations. | |
666 | * @param from the lower limit of the interval to be trimmed | |
667 | * @param to the upper limit of the interval to be trimmed (not including) | |
668 | */ | |
669 | void trim(MonitorDBStore::TransactionRef t, version_t from, version_t to); | |
670 | ||
671 | /** | |
672 | * encode service-specific extra bits into trim transaction | |
673 | * | |
674 | * @param tx transaction | |
675 | * @param first new first_committed value | |
676 | */ | |
677 | virtual void encode_trim_extra(MonitorDBStore::TransactionRef tx, | |
678 | version_t first) {} | |
679 | ||
680 | /** | |
681 | * Get the version we should trim to. | |
682 | * | |
683 | * Should be overloaded by service if it wants to trim states. | |
684 | * | |
685 | * @returns the version we should trim to; if we return zero, it should be | |
686 | * assumed that there's no version to trim to. | |
687 | */ | |
688 | virtual version_t get_trim_to() { | |
689 | return 0; | |
690 | } | |
691 | ||
692 | /** | |
693 | * @} | |
694 | */ | |
695 | /** | |
696 | * @defgroup PaxosService_h_Stash_Full | |
697 | * @{ | |
698 | */ | |
699 | virtual bool should_stash_full(); | |
700 | /** | |
701 | * Encode a full version on @p t | |
702 | * | |
703 | * @note We force every service to implement this function, since we strongly | |
704 | * desire the encoding of full versions. | |
705 | * @note Services that do not trim their state, will be bound to only create | |
706 | * one full version. Full version stashing is determined/controled by | |
707 | * trimming: we stash a version each time a trim is bound to erase the | |
708 | * latest full version. | |
709 | * | |
710 | * @param t Transaction on which the full version shall be encoded. | |
711 | */ | |
712 | virtual void encode_full(MonitorDBStore::TransactionRef t) = 0; | |
713 | ||
714 | /** | |
715 | * @} | |
716 | */ | |
717 | ||
718 | /** | |
719 | * Cancel events. | |
720 | * | |
721 | * @note This function is a wrapper for Paxos::cancel_events | |
722 | */ | |
723 | void cancel_events() { | |
724 | paxos->cancel_events(); | |
725 | } | |
726 | ||
727 | /** | |
728 | * @defgroup PaxosService_h_store_funcs Back storage interface functions | |
729 | * @{ | |
730 | */ | |
731 | /** | |
732 | * @defgroup PaxosService_h_store_modify Wrapper function interface to access | |
733 | * the back store for modification | |
734 | * purposes | |
735 | * @{ | |
736 | */ | |
737 | void put_first_committed(MonitorDBStore::TransactionRef t, version_t ver) { | |
738 | t->put(get_service_name(), first_committed_name, ver); | |
739 | } | |
740 | /** | |
741 | * Set the last committed version to @p ver | |
742 | * | |
743 | * @param t A transaction to which we add this put operation | |
744 | * @param ver The last committed version number being put | |
745 | */ | |
746 | void put_last_committed(MonitorDBStore::TransactionRef t, version_t ver) { | |
747 | t->put(get_service_name(), last_committed_name, ver); | |
748 | ||
749 | /* We only need to do this once, and that is when we are about to make our | |
750 | * first proposal. There are some services that rely on first_committed | |
751 | * being set -- and it should! -- so we need to guarantee that it is, | |
752 | * specially because the services itself do not do it themselves. They do | |
753 | * rely on it, but they expect us to deal with it, and so we shall. | |
754 | */ | |
755 | if (!get_first_committed()) | |
756 | put_first_committed(t, ver); | |
757 | } | |
758 | /** | |
759 | * Put the contents of @p bl into version @p ver | |
760 | * | |
761 | * @param t A transaction to which we will add this put operation | |
762 | * @param ver The version to which we will add the value | |
763 | * @param bl A bufferlist containing the version's value | |
764 | */ | |
765 | void put_version(MonitorDBStore::TransactionRef t, version_t ver, | |
766 | bufferlist& bl) { | |
767 | t->put(get_service_name(), ver, bl); | |
768 | } | |
769 | /** | |
770 | * Put the contents of @p bl into a full version key for this service, that | |
771 | * will be created with @p ver in mind. | |
772 | * | |
773 | * @param t The transaction to which we will add this put operation | |
774 | * @param ver A version number | |
775 | * @param bl A bufferlist containing the version's value | |
776 | */ | |
777 | void put_version_full(MonitorDBStore::TransactionRef t, | |
778 | version_t ver, bufferlist& bl) { | |
779 | string key = mon->store->combine_strings(full_prefix_name, ver); | |
780 | t->put(get_service_name(), key, bl); | |
781 | } | |
782 | /** | |
783 | * Put the version number in @p ver into the key pointing to the latest full | |
784 | * version of this service. | |
785 | * | |
786 | * @param t The transaction to which we will add this put operation | |
787 | * @param ver A version number | |
788 | */ | |
789 | void put_version_latest_full(MonitorDBStore::TransactionRef t, version_t ver) { | |
790 | string key = mon->store->combine_strings(full_prefix_name, full_latest_name); | |
791 | t->put(get_service_name(), key, ver); | |
792 | } | |
793 | /** | |
794 | * Put the contents of @p bl into the key @p key. | |
795 | * | |
796 | * @param t A transaction to which we will add this put operation | |
797 | * @param key The key to which we will add the value | |
798 | * @param bl A bufferlist containing the value | |
799 | */ | |
800 | void put_value(MonitorDBStore::TransactionRef t, | |
801 | const string& key, bufferlist& bl) { | |
802 | t->put(get_service_name(), key, bl); | |
803 | } | |
804 | ||
31f18b77 FG |
805 | /** |
806 | * Put integer value @v into the key @p key. | |
807 | * | |
808 | * @param t A transaction to which we will add this put operation | |
809 | * @param key The key to which we will add the value | |
810 | * @param v An integer | |
811 | */ | |
812 | void put_value(MonitorDBStore::TransactionRef t, | |
813 | const string& key, version_t v) { | |
814 | t->put(get_service_name(), key, v); | |
815 | } | |
816 | ||
7c673cae FG |
817 | /** |
818 | * @} | |
819 | */ | |
820 | ||
821 | /** | |
822 | * @defgroup PaxosService_h_store_get Wrapper function interface to access | |
823 | * the back store for reading purposes | |
824 | * @{ | |
825 | */ | |
826 | ||
827 | /** | |
828 | * @defgroup PaxosService_h_version_cache Obtain cached versions for this | |
829 | * service. | |
830 | * @{ | |
831 | */ | |
832 | /** | |
833 | * Get the first committed version | |
834 | * | |
835 | * @returns Our first committed version (that is available) | |
836 | */ | |
837 | version_t get_first_committed() const{ | |
838 | return cached_first_committed; | |
839 | } | |
840 | /** | |
841 | * Get the last committed version | |
842 | * | |
843 | * @returns Our last committed version | |
844 | */ | |
845 | version_t get_last_committed() const{ | |
846 | return cached_last_committed; | |
847 | } | |
848 | ||
849 | /** | |
850 | * @} | |
851 | */ | |
852 | ||
853 | /** | |
854 | * Get the contents of a given version @p ver | |
855 | * | |
856 | * @param ver The version being obtained | |
857 | * @param bl The bufferlist to be populated | |
858 | * @return 0 on success; <0 otherwise | |
859 | */ | |
860 | virtual int get_version(version_t ver, bufferlist& bl) { | |
861 | return mon->store->get(get_service_name(), ver, bl); | |
862 | } | |
863 | /** | |
864 | * Get the contents of a given full version of this service. | |
865 | * | |
866 | * @param ver A version number | |
867 | * @param bl The bufferlist to be populated | |
868 | * @returns 0 on success; <0 otherwise | |
869 | */ | |
870 | virtual int get_version_full(version_t ver, bufferlist& bl) { | |
871 | string key = mon->store->combine_strings(full_prefix_name, ver); | |
872 | return mon->store->get(get_service_name(), key, bl); | |
873 | } | |
874 | /** | |
875 | * Get the latest full version number | |
876 | * | |
877 | * @returns A version number | |
878 | */ | |
879 | version_t get_version_latest_full() { | |
880 | string key = mon->store->combine_strings(full_prefix_name, full_latest_name); | |
881 | return mon->store->get(get_service_name(), key); | |
882 | } | |
883 | ||
884 | /** | |
885 | * Get a value from a given key. | |
886 | * | |
887 | * @param[in] key The key | |
888 | * @param[out] bl The bufferlist to be populated with the value | |
889 | */ | |
890 | int get_value(const string& key, bufferlist& bl) { | |
891 | return mon->store->get(get_service_name(), key, bl); | |
892 | } | |
893 | /** | |
894 | * Get an integer value from a given key. | |
895 | * | |
896 | * @param[in] key The key | |
897 | */ | |
898 | version_t get_value(const string& key) { | |
899 | return mon->store->get(get_service_name(), key); | |
900 | } | |
901 | ||
902 | /** | |
903 | * @} | |
904 | */ | |
905 | /** | |
906 | * @} | |
907 | */ | |
908 | }; | |
909 | ||
910 | #endif | |
911 |