diff --git a/src/box/box.cc b/src/box/box.cc index ba77d7f5014fb29363a60d9cefd3b1094903b428..4f11901d70be7851ad9633c8de0296e1d7a5381b 100644 --- a/src/box/box.cc +++ b/src/box/box.cc @@ -111,13 +111,14 @@ box_check_uri(const char *source, const char *option_name) } } -static void +static enum wal_mode box_check_wal_mode(const char *mode_name) { assert(mode_name != NULL); /* checked in Lua */ int mode = strindex(wal_mode_STRS, mode_name, WAL_MODE_MAX); if (mode == WAL_MODE_MAX) tnt_raise(ClientError, ER_CFG, "wal_mode", mode_name); + return (enum wal_mode) mode; } static void @@ -130,6 +131,17 @@ box_check_readahead(int readahead) } } +static int +box_check_rows_per_wal(int rows_per_wal) +{ + /* check rows_per_wal configuration */ + if (rows_per_wal <= 1) { + tnt_raise(ClientError, ER_CFG, "rows_per_wal", + "the value must be greater than one"); + } + return rows_per_wal; +} + void box_check_config() { @@ -137,12 +149,9 @@ box_check_config() box_check_uri(cfg_gets("listen"), "listen"); box_check_uri(cfg_gets("replication_source"), "replication_source"); box_check_readahead(cfg_geti("readahead")); + box_check_rows_per_wal(cfg_geti("rows_per_wal")); + box_check_wal_mode(cfg_gets("wal_mode")); - /* check rows_per_wal configuration */ - if (cfg_geti("rows_per_wal") <= 1) { - tnt_raise(ClientError, ER_CFG, "rows_per_wal", - "the value must be greater than one"); - } } extern "C" void @@ -184,16 +193,12 @@ box_set_listen(const char *uri) if (uri != NULL) coio_service_start(&binary, uri); - - box_leave_local_standby_mode(NULL); } extern "C" void box_set_wal_mode(const char *mode_name) { - box_check_wal_mode(mode_name); - enum wal_mode mode = (enum wal_mode) - strindex(wal_mode_STRS, mode_name, WAL_MODE_MAX); + enum wal_mode mode = box_check_wal_mode(mode_name); if (mode != recovery->wal_mode && (mode == WAL_FSYNC || recovery->wal_mode == WAL_FSYNC)) { tnt_raise(ClientError, ER_CFG, "wal_mode", @@ -240,39 +245,6 @@ box_set_readahead(int readahead) /* }}} configuration bindings */ -void -box_leave_local_standby_mode(void *data __attribute__((unused))) -{ - /* - * recovery->finalize is true when listen is changed - * dynamically. - */ - if (recovery->finalize) - return; - - try { - recovery_finalize(recovery, cfg_geti("rows_per_wal")); - } catch (Exception *e) { - e->log(); - panic("unable to successfully finalize recovery"); - } - - /* - * notify engines about end of recovery. - */ - engine_end_recovery(); - - stat_cleanup(stat_base, IPROTO_TYPE_STAT_MAX); - box_set_wal_mode(cfg_gets("wal_mode")); - - if (recovery_has_remote(recovery)) - recovery_follow_remote(recovery); - /* Enter read-write mode. */ - if (recovery->server_id > 0) - box_set_ro(false); - title("running", NULL); - say_info("ready to accept requests"); -} /** * Execute a request against a given space id with @@ -429,89 +401,98 @@ engine_init() engine_register(sophia); } -void -box_init() +static inline void +box_do_init(void) { - box_check_config(); - - stat_init(); - stat_base = stat_register(iproto_type_strs, IPROTO_TYPE_STAT_MAX); - tuple_init(cfg_getd("slab_alloc_arena"), cfg_geti("slab_alloc_minimal"), cfg_geti("slab_alloc_maximal"), cfg_getd("slab_alloc_factor")); - try { - engine_init(); - - schema_init(); - user_cache_init(); - /* - * The order is important: to initialize sessions, - * we need to access the admin user, which is used - * as a default session user when running triggers. - */ - session_init(); - - title("loading", NULL); - - /* recovery initialization */ - recovery = recovery_new(cfg_gets("snap_dir"), - cfg_gets("wal_dir"), - recover_row, NULL); - recovery_set_remote(recovery, - cfg_gets("replication_source")); - recovery_setup_panic(recovery, - cfg_geti("panic_on_snap_error"), - cfg_geti("panic_on_wal_error")); - - if (recovery_has_data(recovery)) { - /* Tell Sophia engine LSN it must recover to. */ - int64_t checkpoint_id = - recovery_last_checkpoint(recovery); - engine_begin_recover_snapshot(checkpoint_id); - /* Process existing snapshot */ - recover_snap(recovery); - engine_end_recover_snapshot(); - } else if (recovery_has_remote(recovery)) { - /* Initialize a new replica */ - replica_bootstrap(recovery); - engine_end_recover_snapshot(); - box_snapshot(); - } else { - /* Initialize the first server of a new cluster */ - recovery_bootstrap(recovery); - box_set_cluster_uuid(); - box_set_server_uuid(); - engine_end_recover_snapshot(); - box_snapshot(); - } - fiber_gc(); - } catch (Exception *e) { - e->log(); - panic("can't initialize storage: %s", e->errmsg()); + stat_init(); + stat_base = stat_register(iproto_type_strs, IPROTO_TYPE_STAT_MAX); + + engine_init(); + + schema_init(); + user_cache_init(); + /* + * The order is important: to initialize sessions, + * we need to access the admin user, which is used + * as a default session user when running triggers. + */ + session_init(); + + title("loading", NULL); + + /* recovery initialization */ + recovery = recovery_new(cfg_gets("snap_dir"), + cfg_gets("wal_dir"), + recover_row, NULL); + recovery_set_remote(recovery, + cfg_gets("replication_source")); + recovery_setup_panic(recovery, + cfg_geti("panic_on_snap_error"), + cfg_geti("panic_on_wal_error")); + + if (recovery_has_data(recovery)) { + /* Tell Sophia engine LSN it must recover to. */ + int64_t checkpoint_id = + recovery_last_checkpoint(recovery); + engine_begin_recover_snapshot(checkpoint_id); + /* Process existing snapshot */ + recover_snap(recovery); + engine_end_recover_snapshot(); + } else if (recovery_has_remote(recovery)) { + /* Initialize a new replica */ + replica_bootstrap(recovery); + engine_end_recover_snapshot(); + box_snapshot(); + } else { + /* Initialize the first server of a new cluster */ + recovery_bootstrap(recovery); + box_set_cluster_uuid(); + box_set_server_uuid(); + engine_end_recover_snapshot(); + box_snapshot(); } + fiber_gc(); title("orphan", NULL); - recovery_follow_local(recovery, - cfg_getd("wal_dir_rescan_delay")); + recovery_follow_local(recovery, cfg_getd("wal_dir_rescan_delay")); title("hot_standby", NULL); iproto_init(&binary); - /** - * listen is a dynamic option, so box_set_listen() - * will be called after box_init() as long as there - * is a value for listen in the configuration table. - * - * However, if cfg.listen is nil, box_set_listen() will - * not be called - which means that we need to leave - * local hot standby here. The idea is to leave - * local hot standby immediately if listen is not given, - * and only after binding to the listen uri otherwise. - */ - if (cfg_gets("listen") == NULL) - box_leave_local_standby_mode(NULL); + box_set_listen(cfg_gets("listen")); + + int rows_per_wal = box_check_rows_per_wal(cfg_geti("rows_per_wal")); + enum wal_mode wal_mode = box_check_wal_mode(cfg_gets("wal_mode")); + recovery_finalize(recovery, wal_mode, rows_per_wal); + + engine_end_recovery(); + + stat_cleanup(stat_base, IPROTO_TYPE_STAT_MAX); + + if (recovery_has_remote(recovery)) + recovery_follow_remote(recovery); + /* Enter read-write mode. */ + if (recovery->server_id > 0) + box_set_ro(false); + title("running", NULL); + say_info("ready to accept requests"); + + fiber_gc(); +} + +void +box_init() +{ + try { + box_do_init(); + } catch (Exception *e) { + e->log(); + panic("can't initialize storage: %s", e->errmsg()); + } } void diff --git a/src/box/recovery.cc b/src/box/recovery.cc index fae8e77d49f8c82dc1f7695929dd8ffda98e2d64..3d43056d23b3317af42111560f3015a075be06e8 100644 --- a/src/box/recovery.cc +++ b/src/box/recovery.cc @@ -180,9 +180,6 @@ recovery_new(const char *snap_dirname, const char *wal_dirname, xdir_create(&r->wal_dir, wal_dirname, XLOG, &r->server_uuid); - if (r->wal_mode == WAL_FSYNC) - (void) strcat(r->wal_dir.open_wflags, "s"); - vclock_create(&r->vclock); xdir_scan(&r->snap_dir); @@ -522,7 +519,8 @@ recover_remaining_wals(struct recovery_state *r) } void -recovery_finalize(struct recovery_state *r, int rows_per_wal) +recovery_finalize(struct recovery_state *r, enum wal_mode wal_mode, + int rows_per_wal) { recovery_stop_local(r); @@ -559,6 +557,10 @@ recovery_finalize(struct recovery_state *r, int rows_per_wal) recovery_close_log(r); } + r->wal_mode = wal_mode; + if (r->wal_mode == WAL_FSYNC) + (void) strcat(r->wal_dir.open_wflags, "s"); + wal_writer_start(r, rows_per_wal); } diff --git a/src/box/recovery.h b/src/box/recovery.h index 571e67ff0d4a9182663b0bc38c5990b5e1f92773..58289bac9e5293b1741aee412ccb998c70f53717 100644 --- a/src/box/recovery.h +++ b/src/box/recovery.h @@ -137,7 +137,8 @@ void recover_snap(struct recovery_state *r); void recovery_follow_local(struct recovery_state *r, ev_tstamp wal_dir_rescan_delay); void recovery_stop_local(struct recovery_state *r); -void recovery_finalize(struct recovery_state *r, int rows_per_wal); +void recovery_finalize(struct recovery_state *r, enum wal_mode mode, + int rows_per_wal); int64_t wal_write(struct recovery_state *r, struct xrow_header *packet); diff --git a/test/app/cfg.result b/test/app/cfg.result index 86808fe7f6ca7e8bb2f1cbf87844ef1316cc2802..b14c3974d48aa3417d1f5f4f74311664fbafa519 100644 --- a/test/app/cfg.result +++ b/test/app/cfg.result @@ -1,5 +1,5 @@ TAP version 13 -1..21 +1..25 ok - box is not started ok - invalid replication_source ok - invalid wal_mode @@ -9,12 +9,16 @@ ok - box is not started ok - exception on unconfigured box ok - sophia_dir is not auto-created ok - configured box -ok - cfg.wal_mode default value -ok - cfg.wal_mode default value -ok - cfg.wal_mode change -ok - cfg.wal_mode default value -ok - cfg.wal_mode change -ok - cfg.wal_mode default value +ok - wal_mode default value +ok - wal_mode default value +ok - wal_mode change +ok - wal_mode default value +ok - wal_mode change +ok - wal_mode default value +ok - wal_mode fsync +ok - wal_mode fsync -> fsync +ok - wal_mode fsync -> write is not supported +ok - wal_mode write -> fsync is not supported ok - work_dir is invalid ok - sophia_dir is invalid ok - snap_dir is invalid diff --git a/test/app/cfg.test.lua b/test/app/cfg.test.lua index 1b6d2c79698422b2f3602d99f3ebadf7c4572864..9bc072944dc6bcf58296556dead10d2bae62e1b2 100755 --- a/test/app/cfg.test.lua +++ b/test/app/cfg.test.lua @@ -3,7 +3,8 @@ local tap = require('tap') local test = tap.test('cfg') local socket = require('socket') -test:plan(21) +local fio = require('fio') +test:plan(25) -------------------------------------------------------------------------------- -- Invalid values @@ -54,39 +55,61 @@ test:ok(status and result == 'table', 'configured box') -- gh-534: Segmentation fault after two bad wal_mode settings -------------------------------------------------------------------------------- -test:is(box.cfg.wal_mode, "write", "cfg.wal_mode default value") +test:is(box.cfg.wal_mode, "write", "wal_mode default value") box.cfg{wal_mode = ""} -test:is(box.cfg.wal_mode, "write", "cfg.wal_mode default value") +test:is(box.cfg.wal_mode, "write", "wal_mode default value") box.cfg{wal_mode = "none"} -test:is(box.cfg.wal_mode, "none", "cfg.wal_mode change") +test:is(box.cfg.wal_mode, "none", "wal_mode change") -- "" or NULL resets option to default value box.cfg{wal_mode = ""} -test:is(box.cfg.wal_mode, "write", "cfg.wal_mode default value") +test:is(box.cfg.wal_mode, "write", "wal_mode default value") box.cfg{wal_mode = "none"} -test:is(box.cfg.wal_mode, "none", "cfg.wal_mode change") +test:is(box.cfg.wal_mode, "none", "wal_mode change") box.cfg{wal_mode = require('msgpack').NULL} -test:is(box.cfg.wal_mode, "write", "cfg.wal_mode default value") +test:is(box.cfg.wal_mode, "write", "wal_mode default value") + +local tarantool_bin = arg[-1] +local PANIC = 256 +function run_script(code) + local dir = fio.tempdir() + local script_path = fio.pathjoin(dir, 'script.lua') + local script = fio.open(script_path, {'O_CREAT', 'O_WRONLY', 'O_APPEND'}, + tonumber('0777', 8)) + script:write(code) + script:write("\nos.exit(0)") + script:close() + local cmd = [[/bin/sh -c 'cd "%s" && "%s" ./script.lua 2> /dev/null']] + local res = os.execute(string.format(cmd, dir, tarantool_bin)) + fio.rmdir(dir) + return res +end + +-- gh-715: Cannot switch to/from 'fsync' +code = [[ box.cfg{ logger="tarantool.log", wal_mode = 'fsync' }; ]] +test:is(run_script(code), 0, 'wal_mode fsync') + +code = [[ box.cfg{ wal_mode = 'fsync' }; box.cfg { wal_mode = 'fsync' }; ]] +test:is(run_script(code), 0, 'wal_mode fsync -> fsync') + +code = [[ box.cfg{ wal_mode = 'fsync' }; box.cfg { wal_mode = 'none'} ]] +test:is(run_script(code), PANIC, 'wal_mode fsync -> write is not supported') + +code = [[ box.cfg{ wal_mode = 'write' }; box.cfg { wal_mode = 'fsync'} ]] +test:is(run_script(code), PANIC, 'wal_mode write -> fsync is not supported') -- gh-684: Inconsistency with box.cfg and directories -local script = io.open('script.lua', 'w') -script:write([[ box.cfg{ logger="tarantool.log", work_dir='invalid' } ]]) -script:close() -test:isnt(os.execute("/bin/sh -c 'tarantool ./script.lua 2> /dev/null'"), 0, 'work_dir is invalid') - -script = io.open('script.lua', 'w') -script:write([[ pcall( box.cfg, { logger="tarantool.log", sophia_dir='invalid' }) ]]) -script:close() -test:isnt(os.execute("/bin/sh -c 'tarantool ./script.lua 2> /dev/null'"), 0, 'sophia_dir is invalid') - -script = io.open('script.lua', 'w') -script:write([[ box.cfg{ logger="tarantool.log", snap_dir='invalid' } ]]) -script:close() -test:isnt(os.execute("/bin/sh -c 'tarantool ./script.lua 2> /dev/null'"), 0, 'snap_dir is invalid') - -script = io.open('script.lua', 'w') -script:write([[ box.cfg{ logger="tarantool.log", wal_dir='invalid' } ]]) -script:close() -test:isnt(os.execute("/bin/sh -c 'tarantool ./script.lua 2> /dev/null'"), 0, 'wal_dir is invalid') +local code; +code = [[ box.cfg{ work_dir='invalid' } ]] +test:is(run_script(code), PANIC, 'work_dir is invalid') + +code = [[ box.cfg{ sophia_dir='invalid' } ]] +test:is(run_script(code), PANIC, 'sophia_dir is invalid') + +code = [[ box.cfg{ snap_dir='invalid' } ]] +test:is(run_script(code), PANIC, 'snap_dir is invalid') + +code = [[ box.cfg{ wal_dir='invalid' } ]] +test:is(run_script(code), PANIC, 'wal_dir is invalid') -- box.cfg { listen = xx } local path = './tarantool.sock'