[tarantool-patches] [PATCH 2/2] On ctl event trigger

Georgy Kirichenko georgy at tarantool.org
Tue Aug 28 19:19:13 MSK 2018


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
---
 src/box/CMakeLists.txt        |   2 +
 src/box/alter.cc              |  16 ++
 src/box/applier.cc            |   6 +
 src/box/box.cc                |  42 ++++
 src/box/lua/init.c            |   2 +
 src/box/lua/load_cfg.lua      |   1 +
 test/box/ctl_event.result     | 364 ++++++++++++++++++++++++++++++++++
 test/box/ctl_event.test.lua   |  76 +++++++
 test/box/lua/trig_master.lua  |   8 +
 test/box/lua/trig_replica.lua |  46 +++++
 test/box/misc.result          |   1 +
 11 files changed, 564 insertions(+)
 create mode 100644 test/box/ctl_event.result
 create mode 100644 test/box/ctl_event.test.lua
 create mode 100644 test/box/lua/trig_master.lua
 create mode 100644 test/box/lua/trig_replica.lua

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/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: <master-uuid>'")
+---
+- true
+...
+test_run:cmd("push filter 'space_id: [0-9]+' to 'space_id: <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: <master-uuid>
+    status: 1
+  - type: 1
+    status: 8
+  - type: 4
+    server: <master-uuid>
+    status: 2
+  - type: 4
+    server: <master-uuid>
+    status: 4
+  - type: 4
+    server: <master-uuid>
+    status: 5
+  - type: 2
+    action: 2
+    space_id: <space_id>
+  - type: 2
+    action: 2
+    space_id: <space_id>
+  - type: 2
+    action: 2
+    space_id: <space_id>
+  - type: 2
+    action: 2
+    space_id: <space_id>
+  - type: 2
+    action: 1
+    space_id: <space_id>
+  - type: 2
+    action: 2
+    space_id: <space_id>
+  - type: 2
+    action: 2
+    space_id: <space_id>
+  - type: 2
+    action: 1
+    space_id: <space_id>
+  - type: 2
+    action: 2
+    space_id: <space_id>
+  - type: 2
+    action: 1
+    space_id: <space_id>
+  - type: 2
+    action: 2
+    space_id: <space_id>
+  - type: 2
+    action: 1
+    space_id: <space_id>
+  - type: 2
+    action: 2
+    space_id: <space_id>
+  - type: 2
+    action: 1
+    space_id: <space_id>
+  - type: 2
+    action: 2
+    space_id: <space_id>
+  - type: 2
+    action: 1
+    space_id: <space_id>
+  - type: 2
+    action: 2
+    space_id: <space_id>
+  - type: 2
+    action: 2
+    space_id: <space_id>
+  - type: 2
+    action: 2
+    space_id: <space_id>
+  - type: 1
+    status: 9
+  - type: 4
+    server: <master-uuid>
+    status: 6
+  - type: 2
+    action: 1
+    space_id: <space_id>
+  - type: 2
+    action: 1
+    space_id: <space_id>
+  - type: 1
+    status: 10
+  - type: 4
+    server: <master-uuid>
+    status: 7
+  - type: 4
+    server: <master-uuid>
+    status: 4
+  - type: 4
+    server: <master-uuid>
+    status: 8
+  - type: 4
+    server: <master-uuid>
+    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: <master-uuid>
+    status: 1
+  - type: 2
+    action: 2
+    space_id: <space_id>
+  - type: 2
+    action: 2
+    space_id: <space_id>
+  - type: 2
+    action: 2
+    space_id: <space_id>
+  - type: 2
+    action: 2
+    space_id: <space_id>
+  - type: 2
+    action: 1
+    space_id: <space_id>
+  - type: 2
+    action: 2
+    space_id: <space_id>
+  - type: 2
+    action: 2
+    space_id: <space_id>
+  - type: 2
+    action: 1
+    space_id: <space_id>
+  - type: 2
+    action: 2
+    space_id: <space_id>
+  - type: 2
+    action: 1
+    space_id: <space_id>
+  - type: 2
+    action: 2
+    space_id: <space_id>
+  - type: 2
+    action: 1
+    space_id: <space_id>
+  - type: 2
+    action: 2
+    space_id: <space_id>
+  - type: 2
+    action: 1
+    space_id: <space_id>
+  - type: 2
+    action: 2
+    space_id: <space_id>
+  - type: 2
+    action: 1
+    space_id: <space_id>
+  - type: 2
+    action: 2
+    space_id: <space_id>
+  - type: 2
+    action: 2
+    space_id: <space_id>
+  - type: 2
+    action: 2
+    space_id: <space_id>
+  - type: 2
+    action: 1
+    space_id: <space_id>
+  - type: 2
+    action: 1
+    space_id: <space_id>
+  - type: 1
+    status: 2
+  - type: 1
+    status: 5
+  - type: 4
+    server: <master-uuid>
+    status: 2
+  - type: 4
+    server: <master-uuid>
+    status: 4
+  - type: 4
+    server: <master-uuid>
+    status: 8
+  - type: 4
+    server: <master-uuid>
+    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: <master-uuid>'")
+test_run:cmd("push filter 'space_id: [0-9]+' to 'space_id: <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
-- 
2.18.0





More information about the Tarantool-patches mailing list