diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt index d1276472bc9171a90e0d47184582145bc174203f..d7a52c5ee5ca6fe1fa688bf05f1cccaa07aa475c 100644 --- a/src/box/CMakeLists.txt +++ b/src/box/CMakeLists.txt @@ -106,6 +106,7 @@ add_library(box STATIC txn.c box.cc gc.c + checkpoint_schedule.c user_def.c user.cc authentication.cc diff --git a/src/box/checkpoint_schedule.c b/src/box/checkpoint_schedule.c new file mode 100644 index 0000000000000000000000000000000000000000..d37eba7f3ccf6e83438c3b8b2784be37567dcf8b --- /dev/null +++ b/src/box/checkpoint_schedule.c @@ -0,0 +1,76 @@ +/* + * Copyright 2010-2018, Tarantool AUTHORS, please see AUTHORS file. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include "checkpoint_schedule.h" + +#include <assert.h> +#include <math.h> +#include <stdlib.h> + +void +checkpoint_schedule_cfg(struct checkpoint_schedule *sched, + double now, double interval) +{ + sched->interval = interval; + sched->start_time = now + interval; + + /* + * Add a random offset to the start time so as to avoid + * simultaneous checkpointing when multiple instances + * are running on the same host. + */ + if (interval > 0) + sched->start_time += fmod(rand(), interval); +} + +void +checkpoint_schedule_reset(struct checkpoint_schedule *sched, double now) +{ + sched->start_time = now + sched->interval; +} + +double +checkpoint_schedule_timeout(struct checkpoint_schedule *sched, double now) +{ + if (sched->interval <= 0) + return 0; /* checkpointing disabled */ + + if (now < sched->start_time) + return sched->start_time - now; + + /* Time elapsed since the last checkpoint. */ + double elapsed = fmod(now - sched->start_time, sched->interval); + + /* Time left to the next checkpoint. */ + double timeout = sched->interval - elapsed; + + assert(timeout > 0); + return timeout; +} diff --git a/src/box/checkpoint_schedule.h b/src/box/checkpoint_schedule.h new file mode 100644 index 0000000000000000000000000000000000000000..7fbbfe2f9eb3e2f0303c4c4b89197de24cac9c9b --- /dev/null +++ b/src/box/checkpoint_schedule.h @@ -0,0 +1,85 @@ +#ifndef TARANTOOL_BOX_CHECKPOINT_SCHEDULE_H_INCLUDED +#define TARANTOOL_BOX_CHECKPOINT_SCHEDULE_H_INCLUDED +/* + * Copyright 2010-2018, Tarantool AUTHORS, please see AUTHORS file. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(__cplusplus) +extern "C" { +#endif /* defined(__cplusplus) */ + +struct checkpoint_schedule { + /** + * Configured interval between checkpoints, in seconds. + * Set to 0 if periodic checkpointing is disabled. + */ + double interval; + /** + * Time of the first scheduled checkpoint. It is used + * for calculating times of all subsequent checkpoints. + */ + double start_time; +}; + +/** + * (Re)configure a checkpoint schedule. + * + * @now is the current time. + * @interval is the configured interval between checkpoints. + */ +void +checkpoint_schedule_cfg(struct checkpoint_schedule *sched, + double now, double interval); + +/** + * Reset a checkpoint schedule. + * + * Called when a checkpoint is triggered out of the schedule. + * Used to adjusts the schedule accordingly. + * + * @now is the current time. + */ +void +checkpoint_schedule_reset(struct checkpoint_schedule *sched, double now); + +/** + * Return the time to the next scheduled checkpoint, in seconds. + * If auto checkpointing is disabled, returns 0. + * + * @now is the current time. + */ +double +checkpoint_schedule_timeout(struct checkpoint_schedule *sched, double now); + +#if defined(__cplusplus) +} /* extern "C" */ +#endif /* defined(__cplusplus) */ + +#endif /* TARANTOOL_BOX_CHECKPOINT_SCHEDULE_H_INCLUDED */ diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index aef53160044bf1d96c3a57c78be8e07a4e1f80f0..aac86f9e71ca425287f83209c552fda1eeb849cb 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -195,3 +195,9 @@ target_link_libraries(coll.test core unit ${ICU_LIBRARIES} misc) add_executable(tuple_bigref.test tuple_bigref.c) target_link_libraries(tuple_bigref.test tuple unit) + +add_executable(checkpoint_schedule.test + checkpoint_schedule.c + ${PROJECT_SOURCE_DIR}/src/box/checkpoint_schedule.c +) +target_link_libraries(checkpoint_schedule.test m unit) diff --git a/test/unit/checkpoint_schedule.c b/test/unit/checkpoint_schedule.c new file mode 100644 index 0000000000000000000000000000000000000000..025c73b1f922235b75326b6b82436e2783d9bbd1 --- /dev/null +++ b/test/unit/checkpoint_schedule.c @@ -0,0 +1,96 @@ +#include <math.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdlib.h> +#include <time.h> + +#include "unit.h" +#include "checkpoint_schedule.h" + +static inline bool +feq(double a, double b) +{ + return fabs(a - b) <= 1; +} + +int +main() +{ + header(); + plan(38); + + srand(time(NULL)); + double now = rand(); + + struct checkpoint_schedule sched; + checkpoint_schedule_cfg(&sched, now, 0); + + is(checkpoint_schedule_timeout(&sched, now), 0, + "checkpointing disabled - timeout after configuration"); + + now += rand(); + is(checkpoint_schedule_timeout(&sched, now), 0, + "checkpointing disabled - timeout after sleep"); + + checkpoint_schedule_reset(&sched, now); + is(checkpoint_schedule_timeout(&sched, now), 0, + "checkpointing disabled - timeout after reset"); + + double intervals[] = { 100, 600, 1200, 1800, 3600, }; + int intervals_len = sizeof(intervals) / sizeof(intervals[0]); + for (int i = 0; i < intervals_len; i++) { + double interval = intervals[i]; + + checkpoint_schedule_cfg(&sched, now, interval); + double t = checkpoint_schedule_timeout(&sched, now); + ok(t >= interval && t <= interval * 2, + "checkpoint interval %.0lf - timeout after configuration", + interval); + + double t0; + for (int j = 0; j < 100; j++) { + checkpoint_schedule_cfg(&sched, now, interval); + t0 = checkpoint_schedule_timeout(&sched, now); + if (fabs(t - t0) > interval / 4) + break; + } + ok(fabs(t - t0) > interval / 4, + "checkpoint interval %.0lf - initial timeout randomization", + interval); + + now += t0 / 2; + t = checkpoint_schedule_timeout(&sched, now); + ok(feq(t, t0 / 2), + "checkpoint interval %.0lf - timeout after sleep 1", + interval); + + now += t0 / 2; + t = checkpoint_schedule_timeout(&sched, now); + ok(feq(t, interval), + "checkpoint interval %.0lf - timeout after sleep 2", + interval); + + now += interval / 2; + t = checkpoint_schedule_timeout(&sched, now); + ok(feq(t, interval / 2), + "checkpoint interval %.0lf - timeout after sleep 3", + interval); + + now += interval; + t = checkpoint_schedule_timeout(&sched, now); + ok(feq(t, interval / 2), + "checkpoint interval %.0lf - timeout after sleep 4", + interval); + + checkpoint_schedule_reset(&sched, now); + t = checkpoint_schedule_timeout(&sched, now); + ok(feq(t, interval), + "checkpoint interval %.0lf - timeout after reset", + interval); + } + + check_plan(); + footer(); + + return 0; +} diff --git a/test/unit/checkpoint_schedule.result b/test/unit/checkpoint_schedule.result new file mode 100644 index 0000000000000000000000000000000000000000..e34c762a1b9141d3861ccd9d89ba206b465bc2c5 --- /dev/null +++ b/test/unit/checkpoint_schedule.result @@ -0,0 +1,41 @@ + *** main *** +1..38 +ok 1 - checkpointing disabled - timeout after configuration +ok 2 - checkpointing disabled - timeout after sleep +ok 3 - checkpointing disabled - timeout after reset +ok 4 - checkpoint interval 100 - timeout after configuration +ok 5 - checkpoint interval 100 - initial timeout randomization +ok 6 - checkpoint interval 100 - timeout after sleep 1 +ok 7 - checkpoint interval 100 - timeout after sleep 2 +ok 8 - checkpoint interval 100 - timeout after sleep 3 +ok 9 - checkpoint interval 100 - timeout after sleep 4 +ok 10 - checkpoint interval 100 - timeout after reset +ok 11 - checkpoint interval 600 - timeout after configuration +ok 12 - checkpoint interval 600 - initial timeout randomization +ok 13 - checkpoint interval 600 - timeout after sleep 1 +ok 14 - checkpoint interval 600 - timeout after sleep 2 +ok 15 - checkpoint interval 600 - timeout after sleep 3 +ok 16 - checkpoint interval 600 - timeout after sleep 4 +ok 17 - checkpoint interval 600 - timeout after reset +ok 18 - checkpoint interval 1200 - timeout after configuration +ok 19 - checkpoint interval 1200 - initial timeout randomization +ok 20 - checkpoint interval 1200 - timeout after sleep 1 +ok 21 - checkpoint interval 1200 - timeout after sleep 2 +ok 22 - checkpoint interval 1200 - timeout after sleep 3 +ok 23 - checkpoint interval 1200 - timeout after sleep 4 +ok 24 - checkpoint interval 1200 - timeout after reset +ok 25 - checkpoint interval 1800 - timeout after configuration +ok 26 - checkpoint interval 1800 - initial timeout randomization +ok 27 - checkpoint interval 1800 - timeout after sleep 1 +ok 28 - checkpoint interval 1800 - timeout after sleep 2 +ok 29 - checkpoint interval 1800 - timeout after sleep 3 +ok 30 - checkpoint interval 1800 - timeout after sleep 4 +ok 31 - checkpoint interval 1800 - timeout after reset +ok 32 - checkpoint interval 3600 - timeout after configuration +ok 33 - checkpoint interval 3600 - initial timeout randomization +ok 34 - checkpoint interval 3600 - timeout after sleep 1 +ok 35 - checkpoint interval 3600 - timeout after sleep 2 +ok 36 - checkpoint interval 3600 - timeout after sleep 3 +ok 37 - checkpoint interval 3600 - timeout after sleep 4 +ok 38 - checkpoint interval 3600 - timeout after reset + *** main: done ***