diff --git a/src/box/box.cc b/src/box/box.cc index d146ec16284b382ede9033b4dc2429162b0fdcab..121ad787d59b3cab1ceaea964de5cefec05522a1 100644 --- a/src/box/box.cc +++ b/src/box/box.cc @@ -89,11 +89,6 @@ static void title(const char *new_status) const struct vclock *box_vclock = &replicaset.vclock; -/** - * Set if there's a fiber performing box_checkpoint() right now. - */ -static bool checkpoint_is_in_progress; - /** * Set if backup is in progress, i.e. box_backup_start() was * called but box_backup_stop() hasn't been yet. @@ -2185,45 +2180,8 @@ box_checkpoint() /* Signal arrived before box.cfg{} */ if (! is_box_configured) return 0; - int rc = 0; - if (checkpoint_is_in_progress) { - diag_set(ClientError, ER_CHECKPOINT_IN_PROGRESS); - return -1; - } - checkpoint_is_in_progress = true; - /* create checkpoint files */ - latch_lock(&schema_lock); - if ((rc = engine_begin_checkpoint())) - goto end; - - struct vclock vclock; - if ((rc = wal_begin_checkpoint(&vclock))) - goto end; - if ((rc = engine_commit_checkpoint(&vclock))) - goto end; - - wal_commit_checkpoint(&vclock); - gc_add_checkpoint(&vclock); -end: - if (rc) - engine_abort_checkpoint(); - - latch_unlock(&schema_lock); - checkpoint_is_in_progress = false; - - /* - * Wait for background garbage collection that might - * have been triggered by this checkpoint to complete. - * Strictly speaking, it isn't necessary, but it - * simplifies testing as it guarantees that by the - * time box.snapshot() returns, all outdated checkpoint - * files have been removed. - */ - if (!rc) - gc_wait(); - - return rc; + return gc_checkpoint(); } int diff --git a/src/box/gc.c b/src/box/gc.c index 87273b8ddf63690e4c2050581f7c8eae190742b6..153ef65ce8b682b122c5c938736bff78c5251c2a 100644 --- a/src/box/gc.c +++ b/src/box/gc.c @@ -45,11 +45,14 @@ #include <small/rlist.h> #include "diag.h" +#include "errcode.h" #include "fiber.h" #include "fiber_cond.h" +#include "latch.h" #include "say.h" #include "vclock.h" #include "cbus.h" +#include "schema.h" #include "engine.h" /* engine_collect_garbage() */ #include "wal.h" /* wal_collect_garbage() */ @@ -242,7 +245,11 @@ gc_schedule(void) fiber_wakeup(gc.fiber); } -void +/** + * Wait for background garbage collection scheduled prior + * to this point to complete. + */ +static void gc_wait(void) { unsigned scheduled = gc.scheduled; @@ -320,6 +327,65 @@ gc_add_checkpoint(const struct vclock *vclock) gc_schedule(); } +int +gc_checkpoint(void) +{ + int rc; + struct vclock vclock; + + if (gc.checkpoint_is_in_progress) { + diag_set(ClientError, ER_CHECKPOINT_IN_PROGRESS); + return -1; + } + gc.checkpoint_is_in_progress = true; + + /* + * We don't support DDL operations while making a checkpoint. + * Lock them out. + */ + latch_lock(&schema_lock); + + /* + * Rotate WAL and call engine callbacks to create a checkpoint + * on disk for each registered engine. + */ + rc = engine_begin_checkpoint(); + if (rc != 0) + goto out; + rc = wal_begin_checkpoint(&vclock); + if (rc != 0) + goto out; + rc = engine_commit_checkpoint(&vclock); + if (rc != 0) + goto out; + wal_commit_checkpoint(&vclock); + + /* + * Finally, track the newly created checkpoint in the garbage + * collector state. + */ + gc_add_checkpoint(&vclock); +out: + if (rc != 0) + engine_abort_checkpoint(); + + latch_unlock(&schema_lock); + gc.checkpoint_is_in_progress = false; + + /* + * Wait for background garbage collection that might + * have been triggered by this checkpoint to complete. + * Strictly speaking, it isn't necessary, but it + * simplifies testing as it guarantees that by the + * time box.snapshot() returns, all outdated checkpoint + * files have been removed. + */ + if (rc == 0) + gc_wait(); + + return rc; +} + void gc_ref_checkpoint(struct gc_checkpoint *checkpoint, struct gc_checkpoint_ref *ref, const char *format, ...) diff --git a/src/box/gc.h b/src/box/gc.h index a141ace680dada047c055735fdaeba3fb0fc240a..84a441f2684a55677b6780e26dc6c93488fa331f 100644 --- a/src/box/gc.h +++ b/src/box/gc.h @@ -31,6 +31,7 @@ * SUCH DAMAGE. */ +#include <stdbool.h> #include <stddef.h> #include <small/rlist.h> @@ -141,6 +142,10 @@ struct gc_state { * taken at that moment of time. */ unsigned completed, scheduled; + /** + * Set if there's a fiber making a checkpoint right now. + */ + bool checkpoint_is_in_progress; }; extern struct gc_state gc; @@ -191,13 +196,6 @@ gc_init(void); void gc_free(void); -/** - * Wait for background garbage collection scheduled prior - * to this point to complete. - */ -void -gc_wait(void); - /** * Advance the garbage collector vclock to the given position. * Deactivate WAL consumers that need older data. @@ -217,13 +215,25 @@ void gc_set_min_checkpoint_count(int min_checkpoint_count); /** - * Track a new checkpoint in the garbage collector state. - * Note, this function may run garbage collector to remove + * Track an existing checkpoint in the garbage collector state. + * Note, this function may trigger garbage collection to remove * old checkpoints. */ void gc_add_checkpoint(const struct vclock *vclock); +/** + * Make a checkpoint. + * + * This function runs engine/WAL callbacks to create a checkpoint + * on disk, then tracks the new checkpoint in the garbage collector + * state (see gc_add_checkpoint()). + * + * Returns 0 on success. On failure returns -1 and sets diag. + */ +int +gc_checkpoint(void); + /** * Get a reference to @checkpoint and store it in @ref. * This will block the garbage collector from deleting