From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: From: Georgy Kirichenko Subject: Re: [tarantool-patches] [PATCH 2/2] On ctl event trigger Date: Thu, 30 Aug 2018 16:04:56 +0300 Message-ID: <2695667.SYWcXm7UaL@localhost> In-Reply-To: <20180830123807.w2p4fiwcld2v3yun@esperanza> References: <7d9ccb894ef2915181454db39ead93497f9ff9aa.1535472838.git.georgy@tarantool.org> <20180830123807.w2p4fiwcld2v3yun@esperanza> MIME-Version: 1.0 Content-Type: multipart/signed; boundary="nextPart4344240.8R65nnBJ4o"; micalg="pgp-sha256"; protocol="application/pgp-signature" To: Vladimir Davydov Cc: tarantool-patches@freelists.org List-ID: --nextPart4344240.8R65nnBJ4o Content-Transfer-Encoding: 7Bit Content-Type: text/plain; charset="us-ascii" Sorry, i forgot to attach some files in the last commit, there is updated diff version. Corresponding branch is updated. commit f77206197559df7acc0cf53822c2aecadbf5a04e Author: Georgy Kirichenko Date: Tue Aug 28 18:09:24 2018 +0300 On ctl event trigger Introduce a ctl event trigger fired in cases of a bootstrap/recovery status changes, a space create/alter/drop action, an applier state change and shutdown. Trigger could be set with box.ctl_event even before the first box.cfg invocation to control recovery and bootstrap behavior. Event constants accessible via box.ctl_event.const() There are events: - RECOVERY - SPACE - SHUTDOWN - APPLIER A recovery event might have a status: * RECOVERY_SNAPSHOT_START * RECOVERY_SNAPSHOT_DONE * RECOVERY_HOT_STANDBY_START * RECOVERY_HOT_STANDBY_DONE * RECOVERY_XLOGS_DONE * RECOVERY_BOOTSTRAP_START * RECOVERY_BOOTSTRAP_DONE * RECOVERY_INITIAL_JOIN_START * RECOVERY_INITIAL_JOIN_DONE * RECOVERY_FINAL_JOIN_DONE A space event consists of space identifier and action: * SPACE_CREATE * SPACE_ALTER * SPACE_DELETE An applier event contains peer uuid and state: * APPLIER_OFF * APPLIER_CONNECT * APPLIER_CONNECTED * APPLIER_AUTH * APPLIER_READY * APPLIER_INITIAL_JOIN * APPLIER_FINAL_JOIN * APPLIER_JOINED * APPLIER_SYNC * APPLIER_FOLLOW * APPLIER_STOPPED * APPLIER_DISCONNECTED * APPLIER_LOADING Fixes: #3159 diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt index cab6a2276..ee495a037 100644 --- a/src/box/CMakeLists.txt +++ b/src/box/CMakeLists.txt @@ -113,6 +113,7 @@ add_library(box STATIC journal.c wal.c call.c + ctl_event.c ${lua_sources} lua/init.c lua/call.c @@ -131,6 +132,7 @@ add_library(box STATIC lua/session.c lua/net_box.c lua/xlog.c + lua/ctl_event.c ${bin_sources}) target_link_libraries(box box_error tuple stat xrow xlog vclock crc32 scramble diff --git a/src/box/alter.cc b/src/box/alter.cc index b2758a4d9..8e44ada9b 100644 --- a/src/box/alter.cc +++ b/src/box/alter.cc @@ -52,6 +52,7 @@ #include "identifier.h" #include "version.h" #include "sequence.h" +#include "ctl_event.h" /** * chap-sha1 of empty string, i.e. @@ -1620,6 +1621,11 @@ on_replace_dd_space(struct trigger * /* trigger */, void *event) txn_alter_trigger_new(on_create_space_rollback, space); txn_on_rollback(txn, on_rollback); trigger_run_xc(&on_alter_space, space); + struct on_ctl_event event; + event.type = CTL_SPACE; + event.space.action = CTL_SPACE_CREATE; + event.space.space_id = old_id; + trigger_run(&on_ctl_trigger, &event); } else if (new_tuple == NULL) { /* DELETE */ access_check_ddl(old_space->def->name, old_space->def->id, old_space->def->uid, SC_SPACE, PRIV_D, true); @@ -1663,6 +1669,11 @@ on_replace_dd_space(struct trigger * /* trigger */, void *event) txn_alter_trigger_new(on_drop_space_rollback, space); txn_on_rollback(txn, on_rollback); trigger_run_xc(&on_alter_space, old_space); + struct on_ctl_event event; + event.type = CTL_SPACE; + event.space.action = CTL_SPACE_DELETE; + event.space.space_id = old_id; + trigger_run(&on_ctl_trigger, &event); } else { /* UPDATE, REPLACE */ assert(old_space != NULL && new_tuple != NULL); struct space_def *def = @@ -1720,6 +1731,11 @@ on_replace_dd_space(struct trigger * /* trigger */, void *event) alter_space_do(txn, alter); alter_guard.is_active = false; trigger_run_xc(&on_alter_space, alter->new_space); + struct on_ctl_event event; + event.type = CTL_SPACE; + event.space.action = CTL_SPACE_ALTER; + event.space.space_id = old_id; + trigger_run(&on_ctl_trigger, &event); } } diff --git a/src/box/applier.cc b/src/box/applier.cc index 28df8f7ca..4a42c6b43 100644 --- a/src/box/applier.cc +++ b/src/box/applier.cc @@ -48,6 +48,7 @@ #include "error.h" #include "session.h" #include "cfg.h" +#include "ctl_event.h" STRS(applier_state, applier_STATE); @@ -58,6 +59,11 @@ applier_set_state(struct applier *applier, enum applier_state state) say_debug("=> %s", applier_state_strs[state] + strlen("APPLIER_")); trigger_run_xc(&applier->on_state, applier); + struct on_ctl_event ctl_event; + ctl_event.type = CTL_APPLIER; + ctl_event.applier.replica_uuid = applier->uuid; + ctl_event.applier.status = state; + trigger_run(&on_ctl_trigger, &ctl_event); } /** diff --git a/src/box/box.cc b/src/box/box.cc index 0004140e9..01e512cc2 100644 --- a/src/box/box.cc +++ b/src/box/box.cc @@ -73,6 +73,7 @@ #include "call.h" #include "func.h" #include "sequence.h" +#include "ctl_event.h" static char status[64] = "unknown"; @@ -1602,6 +1603,9 @@ box_free(void) * initialized */ if (is_box_configured) { + struct on_ctl_event ctl_event; + ctl_event.type = CTL_SHUTDOWN; + trigger_run(&on_ctl_trigger, &ctl_event); #if 0 session_free(); user_cache_free(); @@ -1670,6 +1674,11 @@ bootstrap_master(const struct tt_uuid *replicaset_uuid) if (boxk(IPROTO_DELETE, BOX_CLUSTER_ID, "[%u]", 1) != 0) diag_raise(); + struct on_ctl_event ctl_event; + ctl_event.type = CTL_RECOVERY; + ctl_event.recovery.status = CTL_RECOVERY_BOOTSTRAP_START; + trigger_run(&on_ctl_trigger, &ctl_event); + /* Register the first replica in the replica set */ box_register_replica(replica_id, &INSTANCE_UUID); assert(replica_by_uuid(&INSTANCE_UUID)->id == 1); @@ -1690,6 +1699,9 @@ bootstrap_master(const struct tt_uuid *replicaset_uuid) if (engine_begin_checkpoint() || engine_commit_checkpoint(&replicaset.vclock)) panic("failed to create a checkpoint"); + ctl_event.type = CTL_RECOVERY; + ctl_event.recovery.status = CTL_RECOVERY_BOOTSTRAP_DONE; + trigger_run(&on_ctl_trigger, &ctl_event); } /** @@ -1703,6 +1715,11 @@ bootstrap_master(const struct tt_uuid *replicaset_uuid) static void bootstrap_from_master(struct replica *master) { + struct on_ctl_event ctl_event; + ctl_event.type = CTL_RECOVERY; + ctl_event.recovery.status = CTL_RECOVERY_INITIAL_JOIN_START; + trigger_run(&on_ctl_trigger, &ctl_event); + struct applier *applier = master->applier; assert(applier != NULL); applier_resume_to_state(applier, APPLIER_READY, TIMEOUT_INFINITY); @@ -1725,6 +1742,9 @@ bootstrap_from_master(struct replica *master) */ engine_begin_initial_recovery_xc(NULL); applier_resume_to_state(applier, APPLIER_FINAL_JOIN, TIMEOUT_INFINITY); + ctl_event.type = CTL_RECOVERY; + ctl_event.recovery.status = CTL_RECOVERY_INITIAL_JOIN_DONE; + trigger_run(&on_ctl_trigger, &ctl_event); /* * Process final data (WALs). @@ -1735,6 +1755,9 @@ bootstrap_from_master(struct replica *master) journal_set(&journal.base); applier_resume_to_state(applier, APPLIER_JOINED, TIMEOUT_INFINITY); + ctl_event.type = CTL_RECOVERY; + ctl_event.recovery.status = CTL_RECOVERY_FINAL_JOIN_DONE; + trigger_run(&on_ctl_trigger, &ctl_event); /* Clear the pointer to journal before it goes out of scope */ journal_set(NULL); @@ -1883,13 +1906,22 @@ local_recovery(const struct tt_uuid *instance_uuid, */ memtx_engine_recover_snapshot_xc(memtx, checkpoint_vclock); + struct on_ctl_event ctl_event; + ctl_event.type = CTL_RECOVERY; + ctl_event.recovery.status = CTL_RECOVERY_SNAPSHOT_DONE; + trigger_run(&on_ctl_trigger, &ctl_event); + engine_begin_final_recovery_xc(); recover_remaining_wals(recovery, &wal_stream.base, NULL, false); /* * Leave hot standby mode, if any, only after * acquiring the lock. */ + if (wal_dir_lock < 0) { + ctl_event.type = CTL_RECOVERY; + ctl_event.recovery.status = CTL_RECOVERY_HOT_STANDBY_START; + trigger_run(&on_ctl_trigger, &ctl_event); title("hot_standby"); say_info("Entering hot standby mode"); recovery_follow_local(recovery, &wal_stream.base, "hot_standby", @@ -1908,11 +1940,17 @@ local_recovery(const struct tt_uuid *instance_uuid, * applied in hot standby mode. */ vclock_copy(&replicaset.vclock, &recovery->vclock); + ctl_event.type = CTL_RECOVERY; + ctl_event.recovery.status = CTL_RECOVERY_HOT_STANDBY_DONE; + trigger_run(&on_ctl_trigger, &ctl_event); box_listen(); box_sync_replication(false); } recovery_finalize(recovery); engine_end_recovery_xc(); + ctl_event.type = CTL_RECOVERY; + ctl_event.recovery.status = CTL_RECOVERY_XLOGS_DONE; + trigger_run(&on_ctl_trigger, &ctl_event); /* Check replica set UUID. */ if (!tt_uuid_is_nil(replicaset_uuid) && @@ -2021,6 +2059,10 @@ box_cfg_xc(void) bool is_bootstrap_leader = false; if (last_checkpoint_lsn >= 0) { /* Recover the instance from the local directory */ + struct on_ctl_event ctl_event; + ctl_event.type = CTL_RECOVERY; + ctl_event.recovery.status = CTL_RECOVERY_SNAPSHOT_START; + trigger_run(&on_ctl_trigger, &ctl_event); local_recovery(&instance_uuid, &replicaset_uuid, &last_checkpoint_vclock); } else { diff --git a/src/box/ctl_event.c b/src/box/ctl_event.c new file mode 100644 index 000000000..387bdd367 --- /dev/null +++ b/src/box/ctl_event.c @@ -0,0 +1,34 @@ +/* + * 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 ``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 + * 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 "ctl_event.h" + +struct rlist on_ctl_trigger = RLIST_HEAD_INITIALIZER(on_ctl_trigger); diff --git a/src/box/ctl_event.h b/src/box/ctl_event.h new file mode 100644 index 000000000..ac57e51b5 --- /dev/null +++ b/src/box/ctl_event.h @@ -0,0 +1,100 @@ +#ifndef BOX_CTL_EVENT_H +#define BOX_CTL_EVENT_H + +/* + * 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 ``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 + * 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 + +#include "small/rlist.h" +#include "tt_uuid.h" + +/* Ctl event types. */ +#define CTL_RECOVERY 1 +#define CTL_SPACE 2 +#define CTL_SHUTDOWN 3 +#define CTL_APPLIER 4 + +/* CTL_RECOVERY event status. */ +#define CTL_RECOVERY_SNAPSHOT_START 1 +#define CTL_RECOVERY_SNAPSHOT_DONE 2 +#define CTL_RECOVERY_HOT_STANDBY_START 3 +#define CTL_RECOVERY_HOT_STANDBY_DONE 4 +#define CTL_RECOVERY_XLOGS_DONE 5 +#define CTL_RECOVERY_BOOTSTRAP_START 6 +#define CTL_RECOVERY_BOOTSTRAP_DONE 7 +#define CTL_RECOVERY_INITIAL_JOIN_START 8 +#define CTL_RECOVERY_INITIAL_JOIN_DONE 9 +#define CTL_RECOVERY_FINAL_JOIN_DONE 10 + +#define CTL_SPACE_CREATE 1 +#define CTL_SPACE_ALTER 2 +#define CTL_SPACE_DELETE 3 + +#if defined(__cplusplus) +extern "C" { +#endif /* defined(__cplusplus) */ + +extern struct rlist on_ctl_trigger; + + +/* CTL_RECOVERY event specific data. */ +struct on_ctl_recovery_event { + uint32_t status; +}; + +/* CTL_SPACE event specific data. */ +struct on_ctl_space_event { + uint32_t action; + uint32_t space_id; +}; + +struct on_ctl_applier_event { + struct tt_uuid replica_uuid; + uint32_t status; +}; + +struct on_ctl_event { + uint32_t type; + union { + struct on_ctl_recovery_event recovery; + struct on_ctl_space_event space; + struct on_ctl_applier_event applier; + }; +}; + + +#if defined(__cplusplus) +} +#endif /* defined(__cplusplus) */ + + +#endif diff --git a/src/box/lua/ctl_event.c b/src/box/lua/ctl_event.c new file mode 100644 index 000000000..aead1cf23 --- /dev/null +++ b/src/box/lua/ctl_event.c @@ -0,0 +1,209 @@ +/* + * 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 ``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 + * 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 +#include +#include + +#include "trigger.h" +#include "box/ctl_event.h" +#include "box/applier.h" + +#include "lua/trigger.h" +#include "lua/utils.h" + +static int +lbox_ctl_event_const(struct lua_State *L) +{ + lua_newtable(L); + lua_pushstring(L, "RECOVERY"); + lua_pushinteger(L, CTL_RECOVERY); + lua_settable(L, -3); + lua_pushstring(L, "SPACE"); + lua_pushinteger(L, CTL_SPACE); + lua_settable(L, -3); + lua_pushstring(L, "SHUTDOWN"); + lua_pushinteger(L, CTL_SHUTDOWN); + lua_settable(L, -3); + + lua_pushstring(L, "RECOVERY_SNAPSHOT_START"); + lua_pushinteger(L, CTL_RECOVERY_SNAPSHOT_START); + lua_settable(L, -3); + lua_pushstring(L, "RECOVERY_SNAPSHOT_DONE"); + lua_pushinteger(L, CTL_RECOVERY_SNAPSHOT_DONE); + lua_settable(L, -3); + lua_pushstring(L, "RECOVERY_HOT_STANDBY_START"); + lua_pushinteger(L, CTL_RECOVERY_HOT_STANDBY_START); + lua_settable(L, -3); + lua_pushstring(L, "RECOVERY_HOT_STANDBY_DONE"); + lua_pushinteger(L, CTL_RECOVERY_HOT_STANDBY_DONE); + lua_settable(L, -3); + lua_pushstring(L, "RECOVERY_XLOGS_DONE"); + lua_pushinteger(L, CTL_RECOVERY_XLOGS_DONE); + lua_settable(L, -3); + lua_pushstring(L, "RECOVERY_BOOTSTRAP_START"); + lua_pushinteger(L, CTL_RECOVERY_BOOTSTRAP_START); + lua_settable(L, -3); + lua_pushstring(L, "RECOVERY_BOOTSTRAP_DONE"); + lua_pushinteger(L, CTL_RECOVERY_BOOTSTRAP_DONE); + lua_settable(L, -3); + lua_pushstring(L, "RECOVERY_INITIAL_JOIN_START"); + lua_pushinteger(L, CTL_RECOVERY_INITIAL_JOIN_START); + lua_settable(L, -3); + lua_pushstring(L, "RECOVERY_INITIAL_JOIN_DONE"); + lua_pushinteger(L, CTL_RECOVERY_INITIAL_JOIN_DONE); + lua_settable(L, -3); + lua_pushstring(L, "RECOVERY_FINAL_JOIN_DONE"); + lua_pushinteger(L, CTL_RECOVERY_FINAL_JOIN_DONE); + lua_settable(L, -3); + + lua_pushstring(L, "SPACE_CREATE"); + lua_pushinteger(L, CTL_SPACE_CREATE); + lua_settable(L, -3); + lua_pushstring(L, "SPACE_ALTER"); + lua_pushinteger(L, CTL_SPACE_ALTER); + lua_settable(L, -3); + lua_pushstring(L, "SPACE_DELETE"); + lua_pushinteger(L, CTL_SPACE_DELETE); + lua_settable(L, -3); + + lua_pushstring(L, "APPLIER_OFF"); + lua_pushinteger(L, APPLIER_OFF); + lua_settable(L, -3); + lua_pushstring(L, "APPLIER_CONNECT"); + lua_pushinteger(L, APPLIER_CONNECT); + lua_settable(L, -3); + lua_pushstring(L, "APPLIER_CONNECTED"); + lua_pushinteger(L, APPLIER_CONNECTED); + lua_settable(L, -3); + lua_pushstring(L, "APPLIER_AUTH"); + lua_pushinteger(L, APPLIER_AUTH); + lua_settable(L, -3); + lua_pushstring(L, "APPLIER_READY"); + lua_pushinteger(L, APPLIER_READY); + lua_settable(L, -3); + lua_pushstring(L, "APPLIER_INITIAL_JOIN"); + lua_pushinteger(L, APPLIER_INITIAL_JOIN); + lua_settable(L, -3); + lua_pushstring(L, "APPLIER_FINAL_JOIN"); + lua_pushinteger(L, APPLIER_FINAL_JOIN); + lua_settable(L, -3); + lua_pushstring(L, "APPLIER_JOINED"); + lua_pushinteger(L, APPLIER_JOINED); + lua_settable(L, -3); + lua_pushstring(L, "APPLIER_SYNC"); + lua_pushinteger(L, APPLIER_SYNC); + lua_settable(L, -3); + lua_pushstring(L, "APPLIER_FOLLOW"); + lua_pushinteger(L, APPLIER_FOLLOW); + lua_settable(L, -3); + lua_pushstring(L, "APPLIER_STOPPED"); + lua_pushinteger(L, APPLIER_STOPPED); + lua_settable(L, -3); + lua_pushstring(L, "APPLIER_DISCONNECTED"); + lua_pushinteger(L, APPLIER_DISCONNECTED); + lua_settable(L, -3); + lua_pushstring(L, "APPLIER_LOADING"); + lua_pushinteger(L, APPLIER_LOADING); + lua_settable(L, -3); + + return 1; +} + +static int +lbox_push_on_ctl_event(struct lua_State *L, void *data) +{ + struct on_ctl_event *event = (struct on_ctl_event *)data; + switch (event->type) { + case CTL_RECOVERY: + lua_newtable(L); + lua_pushstring(L, "type"); + lua_pushinteger(L, CTL_RECOVERY); + lua_settable(L, -3); + lua_pushstring(L, "status"); + lua_pushinteger(L, event->recovery.status); + lua_settable(L, -3); + break; + case CTL_SPACE: + lua_newtable(L); + lua_pushstring(L, "type"); + lua_pushinteger(L, CTL_SPACE); + lua_settable(L, -3); + lua_pushstring(L, "action"); + lua_pushinteger(L, event->space.action); + lua_settable(L, -3); + lua_pushstring(L, "space_id"); + lua_pushinteger(L, event->space.space_id); + lua_settable(L, -3); + break; + case CTL_SHUTDOWN: + lua_newtable(L); + lua_pushstring(L, "type"); + lua_pushinteger(L, CTL_SHUTDOWN); + lua_settable(L, -3); + break; + case CTL_APPLIER: + lua_newtable(L); + lua_pushstring(L, "type"); + lua_pushinteger(L, CTL_APPLIER); + lua_settable(L, -3); + lua_pushstring(L, "replica"); + lua_pushstring(L, tt_uuid_str(&event->applier.replica_uuid)); + lua_settable(L, -3); + lua_pushstring(L, "status"); + lua_pushinteger(L, event->applier.status); + lua_settable(L, -3); + break; + default: + lua_pushnil(L); + } + return 1; +} + +static int +lbox_on_ctl_event(struct lua_State *L) +{ + return lbox_trigger_reset(L, 2, &on_ctl_trigger, + lbox_push_on_ctl_event, NULL); +} + +static const struct luaL_Reg lbox_ctl_lib[] = { + {"const", lbox_ctl_event_const}, + {"on_ctl_event", lbox_on_ctl_event}, + {NULL, NULL} +}; + +void +box_lua_ctl_event_init(struct lua_State *L) +{ + luaL_register_module(L, "box.ctl_event", lbox_ctl_lib); + lua_pop(L, 1); +} diff --git a/src/box/lua/ctl_event.h b/src/box/lua/ctl_event.h new file mode 100644 index 000000000..a878707a3 --- /dev/null +++ b/src/box/lua/ctl_event.h @@ -0,0 +1,48 @@ +#ifndef INCLUDES_TARANTOOL_LUA_CTL_EVENT_H +#define INCLUDES_TARANTOOL_LUA_CTL_EVENT_H + +/* + * 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 ``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 + * 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 lua_State; + +void +box_lua_ctl_event_init(struct lua_State *L); + +#if defined(__cplusplus) +} /* extern "C" */ +#endif /* defined(__cplusplus) */ + +#endif /* INCLUDES_TARANTOOL_LUA_CTL_EVENT_H */ diff --git a/src/box/lua/init.c b/src/box/lua/init.c index 694b5bfd3..140803384 100644 --- a/src/box/lua/init.c +++ b/src/box/lua/init.c @@ -52,6 +52,7 @@ #include "box/lua/stat.h" #include "box/lua/info.h" #include "box/lua/ctl.h" +#include "box/lua/ctl_event.h" #include "box/lua/session.h" #include "box/lua/net_box.h" #include "box/lua/cfg.h" @@ -306,6 +307,7 @@ box_lua_init(struct lua_State *L) box_lua_info_init(L); box_lua_stat_init(L); box_lua_ctl_init(L); + box_lua_ctl_event_init(L); box_lua_session_init(L); box_lua_xlog_init(L); luaopen_net_box(L); diff --git a/src/box/lua/load_cfg.lua b/src/box/lua/load_cfg.lua index c68a3583f..1935143a6 100644 --- a/src/box/lua/load_cfg.lua +++ b/src/box/lua/load_cfg.lua @@ -397,6 +397,7 @@ local box_cfg_guard_whitelist = { tuple = true; runtime = true; NULL = true; + ctl_event = true; }; local box = require('box') diff --git a/test/box/ctl_event.result b/test/box/ctl_event.result new file mode 100644 index 000000000..5da8cf40d --- /dev/null +++ b/test/box/ctl_event.result @@ -0,0 +1,364 @@ +env = require('test_run') +--- +... +test_run = env.new() +--- +... +-- create master instance +test_run:cmd("create server trig_master with script='box/lua/ trig_master.lua'") +--- +- true +... +test_run:cmd("start server trig_master") +--- +- true +... +test_run:cmd("switch trig_master") +--- +- true +... +env = require('test_run') +--- +... +test_run = env.new() +--- +... +-- simple ctl_event_case +ctl_const = box.ctl_event.const() +--- +... +test_run:cmd("setopt delimiter ';'") +--- +- true +... +function on_replace(old, new) + return box.tuple.new({new[1], new[2], 'M'}) +end; +--- +... +function on_ctl_trig(event) + if event.type == ctl_const.SPACE and + event.action == ctl_const.SPACE_CREATE then + local space = box.space[event.space_id] + space:before_replace(on_replace) + end +end; +--- +... +test_run:cmd("setopt delimiter ''"); +--- +- true +... +active_trig = box.ctl_event.on_ctl_event(on_ctl_trig) +--- +... +t1 = box.schema.space.create('trig1') +--- +... +_ = t1:create_index('pk') +--- +... +t1:replace({1, 2}) +--- +- [1, 2, 'M'] +... +t1:select() +--- +- - [1, 2, 'M'] +... +-- clear the trigger +box.ctl_event.on_ctl_event(nil, active_trig) +--- +... +t2 = box.schema.space.create('trig2') +--- +... +_ = t2:create_index('pk') +--- +... +t2:replace({1, 2}) +--- +- [1, 2] +... +t2:select() +--- +- - [1, 2] +... +test_run:cmd("push filter 'replica: [-0-9a-f]+' to 'server: '") +--- +- true +... +test_run:cmd("push filter 'space_id: [0-9]+' to 'space_id: '") +--- +- true +... +-- create replica and test bootstrap events +box.schema.user.grant('guest', 'replication', nil, nil, {if_not_exists = true}) +--- +... +test_run:cmd("create server trig_replica with rpl_master=trig_master, script='box/lua/trig_replica.lua'") +--- +- true +... +test_run:cmd("start server trig_replica") +--- +- true +... +t1:replace({2, 3}) +--- +- [2, 3, 'M'] +... +t1:select() +--- +- - [1, 2, 'M'] + - [2, 3, 'M'] +... +test_run:cmd("switch trig_replica") +--- +- true +... +env = require('test_run') +--- +... +test_run = env.new() +--- +... +-- list all events +events +--- +- - type: 4 + server: + status: 1 + - type: 1 + status: 8 + - type: 4 + server: + status: 2 + - type: 4 + server: + status: 4 + - type: 4 + server: + status: 5 + - type: 2 + action: 2 + space_id: + - type: 2 + action: 2 + space_id: + - type: 2 + action: 2 + space_id: + - type: 2 + action: 2 + space_id: + - type: 2 + action: 1 + space_id: + - type: 2 + action: 2 + space_id: + - type: 2 + action: 2 + space_id: + - type: 2 + action: 1 + space_id: + - type: 2 + action: 2 + space_id: + - type: 2 + action: 1 + space_id: + - type: 2 + action: 2 + space_id: + - type: 2 + action: 1 + space_id: + - type: 2 + action: 2 + space_id: + - type: 2 + action: 1 + space_id: + - type: 2 + action: 2 + space_id: + - type: 2 + action: 1 + space_id: + - type: 2 + action: 2 + space_id: + - type: 2 + action: 2 + space_id: + - type: 2 + action: 2 + space_id: + - type: 1 + status: 9 + - type: 4 + server: + status: 6 + - type: 2 + action: 1 + space_id: + - type: 2 + action: 1 + space_id: + - type: 1 + status: 10 + - type: 4 + server: + status: 7 + - type: 4 + server: + status: 4 + - type: 4 + server: + status: 8 + - type: 4 + server: + status: 9 +... +-- check before replace trigger +box.space.trig1:select() +--- +- - [1, 2, 'M', 'R'] + - [2, 3, 'M', 'R'] +... +test_run:cmd("switch trig_master") +--- +- true +... +test_run:cmd("stop server trig_replica") +--- +- true +... +t1:replace({3, 4}) +--- +- [3, 4, 'M'] +... +test_run:cmd("start server trig_replica") +--- +- true +... +test_run:cmd("switch trig_replica") +--- +- true +... +env = require('test_run') +--- +... +test_run = env.new() +--- +... +-- list all events +events +--- +- - type: 1 + status: 1 + - type: 4 + server: + status: 1 + - type: 2 + action: 2 + space_id: + - type: 2 + action: 2 + space_id: + - type: 2 + action: 2 + space_id: + - type: 2 + action: 2 + space_id: + - type: 2 + action: 1 + space_id: + - type: 2 + action: 2 + space_id: + - type: 2 + action: 2 + space_id: + - type: 2 + action: 1 + space_id: + - type: 2 + action: 2 + space_id: + - type: 2 + action: 1 + space_id: + - type: 2 + action: 2 + space_id: + - type: 2 + action: 1 + space_id: + - type: 2 + action: 2 + space_id: + - type: 2 + action: 1 + space_id: + - type: 2 + action: 2 + space_id: + - type: 2 + action: 1 + space_id: + - type: 2 + action: 2 + space_id: + - type: 2 + action: 2 + space_id: + - type: 2 + action: 2 + space_id: + - type: 2 + action: 1 + space_id: + - type: 2 + action: 1 + space_id: + - type: 1 + status: 2 + - type: 1 + status: 5 + - type: 4 + server: + status: 2 + - type: 4 + server: + status: 4 + - type: 4 + server: + status: 8 + - type: 4 + server: + status: 9 +... +-- check tuples changed only one time +box.space.trig1:select() +--- +- - [1, 2, 'M', 'R'] + - [2, 3, 'M', 'R'] + - [3, 4, 'M', 'R'] +... +test_run:cmd("switch default") +--- +- true +... +test_run:cmd("stop server trig_replica") +--- +- true +... +test_run:cmd("stop server trig_master") +--- +- true +... diff --git a/test/box/ctl_event.test.lua b/test/box/ctl_event.test.lua new file mode 100644 index 000000000..8cb97d80f --- /dev/null +++ b/test/box/ctl_event.test.lua @@ -0,0 +1,76 @@ +env = require('test_run') +test_run = env.new() + +-- create master instance +test_run:cmd("create server trig_master with script='box/lua/ trig_master.lua'") +test_run:cmd("start server trig_master") +test_run:cmd("switch trig_master") +env = require('test_run') +test_run = env.new() + +-- simple ctl_event_case +ctl_const = box.ctl_event.const() +test_run:cmd("setopt delimiter ';'") +function on_replace(old, new) + return box.tuple.new({new[1], new[2], 'M'}) +end; +function on_ctl_trig(event) + if event.type == ctl_const.SPACE and + event.action == ctl_const.SPACE_CREATE then + local space = box.space[event.space_id] + space:before_replace(on_replace) + end +end; +test_run:cmd("setopt delimiter ''"); +active_trig = box.ctl_event.on_ctl_event(on_ctl_trig) +t1 = box.schema.space.create('trig1') +_ = t1:create_index('pk') +t1:replace({1, 2}) +t1:select() + +-- clear the trigger +box.ctl_event.on_ctl_event(nil, active_trig) +t2 = box.schema.space.create('trig2') +_ = t2:create_index('pk') +t2:replace({1, 2}) +t2:select() + +test_run:cmd("push filter 'replica: [-0-9a-f]+' to 'server: '") +test_run:cmd("push filter 'space_id: [0-9]+' to 'space_id: '") + +-- create replica and test bootstrap events +box.schema.user.grant('guest', 'replication', nil, nil, {if_not_exists = true}) +test_run:cmd("create server trig_replica with rpl_master=trig_master, script='box/lua/trig_replica.lua'") +test_run:cmd("start server trig_replica") +t1:replace({2, 3}) +t1:select() + +test_run:cmd("switch trig_replica") +env = require('test_run') +test_run = env.new() + +-- list all events +events + +-- check before replace trigger +box.space.trig1:select() + +test_run:cmd("switch trig_master") +test_run:cmd("stop server trig_replica") + +t1:replace({3, 4}) + +test_run:cmd("start server trig_replica") + +test_run:cmd("switch trig_replica") +env = require('test_run') +test_run = env.new() + +-- list all events +events + +-- check tuples changed only one time +box.space.trig1:select() +test_run:cmd("switch default") +test_run:cmd("stop server trig_replica") +test_run:cmd("stop server trig_master") diff --git a/test/box/lua/trig_master.lua b/test/box/lua/trig_master.lua new file mode 100644 index 000000000..fa253ba4f --- /dev/null +++ b/test/box/lua/trig_master.lua @@ -0,0 +1,8 @@ +#!/usr/bin/env tarantool +require('console').listen(os.getenv('ADMIN')) + +box.cfg({ + listen = os.getenv("LISTEN"), + memtx_memory = 107374182, + replication_connect_timeout = 0.5, +}) diff --git a/test/box/lua/trig_replica.lua b/test/box/lua/trig_replica.lua new file mode 100644 index 000000000..581bdfdb7 --- /dev/null +++ b/test/box/lua/trig_replica.lua @@ -0,0 +1,46 @@ +#!/usr/bin/env tarantool +require('console').listen(os.getenv('ADMIN')) + +events = {} + +local ctl_const = box.ctl_event.const() +local recovery_status = nil + +local function before_replace(old, new) + if recovery_status == ctl_const.RECOVERY_SNAPSHOT_START or + recovery_status == ctl_const.RECOVERY_SNAPSHOT_DONE then + -- local files + return new + end + if new == nil then + return new + end + local k = {new:unpack()} + table.insert(k, 'R') + return box.tuple.new(k) +end + + +local function ctl_event_trigger(event) + -- register the event + table.insert(events, event) + if event.type == ctl_const.RECOVERY then + recovery_status = event.status + end + if event.type == ctl_const.SPACE and + event.action == ctl_const.SPACE_CREATE then + if event.space_id > 511 then + local space = box.space[event.space_id] + space:before_replace(before_replace) + end + end +end + +box.ctl_event.on_ctl_event(ctl_event_trigger) + +box.cfg({ + listen = os.getenv("LISTEN"), + replication = os.getenv("MASTER"), + memtx_memory = 107374182, + replication_connect_timeout = 0.5, +}) diff --git a/test/box/misc.result b/test/box/misc.result index e213d7964..969081e59 100644 --- a/test/box/misc.result +++ b/test/box/misc.result @@ -62,6 +62,7 @@ t - cfg - commit - ctl + - ctl_event - error - feedback - index --nextPart4344240.8R65nnBJ4o Content-Type: application/pgp-signature; name="signature.asc" Content-Description: This is a digitally signed message part. Content-Transfer-Encoding: 7Bit -----BEGIN PGP SIGNATURE----- iQEzBAABCAAdFiEEFZT35EtIMRTDS5hJnoTdFFzh6LUFAluH63gACgkQnoTdFFzh 6LXd2Qf+OC5HvpYrT2V6J0mCK3zlWcxLgdQaoBOsH5vFBtYU/NI9MPGC2XW6hpZ2 YGDtfNJZCHvfavgkqJttMN4m6YEIaranwyOz8Abdqcrjx/zXU0CD0kc0vT4+N4yr xQOdZQ92d+2Ys4ZWkP0EwJ/wO6bUguA2Ghvmfot+ntvq1VtF+i/Kn/S6+bnkci+7 GLDZTsY4ELzIS6+2qbDEymhCru4mtgf1XhtukHztQQ8L2bSd3ejNQ8bEBpYvyJh6 IkKkLxWm2tcbZu8iMQZ8sa8gtyjgUN4mWPmwPhGLJj0zPigX1Y0o9hNoYBEj8ywH O8zsUbt7ibTlD6x2DDhoggu7gNx1ZA== =l3fL -----END PGP SIGNATURE----- --nextPart4344240.8R65nnBJ4o--