From 8ac478986883b2a72093fca42533a10427543966 Mon Sep 17 00:00:00 2001
From: mechanik20051988 <mechanik20051988@tarantool.org>
Date: Fri, 15 Jan 2021 19:07:56 +0300
Subject: [PATCH] memtx: fix test for gh5304 issue and
 memtx_space_is_recovering function

In previous version of patch we compared memtx state with
MEMTX_FINAL_RECOVERY to check that memtx recovery completed.
This is not quite true, memtx_state == MEMTX_FINAL_RECOVERY
means that the recovery from snapshot is finished, but recovery
from wals not. We need to compare memtx_state with MEMTX_OK
to check that recovery totally finished.
In previous test version on_replace trigger (created on
_user space) is never called. It's because is_recovery_finished()
always returns false: on_schema_init is invoked BEFORE
user's data recovery process (so trigger is not created at all
at this moment).
In new test version you can see correct user case:
we create on_replace trigger on _index system space,
which replaces/inserts/updates tuples in temp and loc spaces.
So each time user creates new space and index for it,
trigger replaces/inserts/updates tuples in temp and loc spaces.
Because trigger replaces/inserts/updates tuple with same
primary key, we get error when insert trigger called.

Follow-up #5304
---
 .../unreleased/fix-is-recovering-function     |   4 +
 src/box/lua/ctl.c                             |   2 +-
 src/box/memtx_space.h                         |   2 +-
 test/box/gh-5304-insert_during_recovery.lua   |  10 +-
 .../box/gh-5304-insert_during_recovery.result | 108 ++++++++++++++++++
 .../gh-5304-insert_during_recovery.test.lua   |  35 ++++++
 6 files changed, 155 insertions(+), 6 deletions(-)
 create mode 100644 changelogs/unreleased/fix-is-recovering-function

diff --git a/changelogs/unreleased/fix-is-recovering-function b/changelogs/unreleased/fix-is-recovering-function
new file mode 100644
index 0000000000..ff2117d588
--- /dev/null
+++ b/changelogs/unreleased/fix-is-recovering-function
@@ -0,0 +1,4 @@
+## bugfix/core
+
+* Fix lbox_ctl_is_recovery_finished(): in some cases it might return true
+  even if recovery was still in the progress.
diff --git a/src/box/lua/ctl.c b/src/box/lua/ctl.c
index b44474b124..b81199662b 100644
--- a/src/box/lua/ctl.c
+++ b/src/box/lua/ctl.c
@@ -94,7 +94,7 @@ lbox_ctl_is_recovery_finished(struct lua_State *L)
 	struct memtx_engine *memtx;
 	memtx = (struct memtx_engine *)engine_by_name("memtx");
 	lua_pushboolean(L, (memtx ?
-		(memtx->state < MEMTX_FINAL_RECOVERY ? 0 : 1) : 0));
+		(memtx->state < MEMTX_OK ? 0 : 1) : 0));
 	return 1;
 }
 
diff --git a/src/box/memtx_space.h b/src/box/memtx_space.h
index 68e6264adb..a13dae7145 100644
--- a/src/box/memtx_space.h
+++ b/src/box/memtx_space.h
@@ -93,7 +93,7 @@ memtx_space_is_recovering(struct space *space)
 {
 	assert(space_is_memtx(space));
 	struct memtx_engine *memtx = (struct memtx_engine *)space->engine;
-	return memtx->state < MEMTX_FINAL_RECOVERY;
+	return memtx->state < MEMTX_OK;
 }
 
 #if defined(__cplusplus)
diff --git a/test/box/gh-5304-insert_during_recovery.lua b/test/box/gh-5304-insert_during_recovery.lua
index c8b6c5cfb7..2d84a95e30 100644
--- a/test/box/gh-5304-insert_during_recovery.lua
+++ b/test/box/gh-5304-insert_during_recovery.lua
@@ -32,13 +32,15 @@ end
 
 if arg[2] == 'is_recovery_finished' then
     box.ctl.on_schema_init(function()
-        if box.ctl.is_recovery_finished() then
-            box.space._user:on_replace(trigger)
-        end
+        box.space._index:on_replace(function(old_space, new_space)
+            if box.ctl.is_recovery_finished() then
+                trigger(old_space, new_space)
+            end
+        end)
     end)
 else
     box.ctl.on_schema_init(function()
-        box.space._user:on_replace(trigger)
+        box.space._index:on_replace(trigger)
     end)
 end
 
diff --git a/test/box/gh-5304-insert_during_recovery.result b/test/box/gh-5304-insert_during_recovery.result
index 19823c71fd..deec6ee04d 100644
--- a/test/box/gh-5304-insert_during_recovery.result
+++ b/test/box/gh-5304-insert_during_recovery.result
@@ -61,6 +61,42 @@ test_run:cmd('start server test with args="replace is_recovery_finished"')
  | ---
  | - true
  | ...
+test_run:cmd('switch test')
+ | ---
+ | - true
+ | ...
+box.space.temp:select()
+ | ---
+ | - []
+ | ...
+box.space.loc:select()
+ | ---
+ | - []
+ | ...
+-- Creating a new space and index invokes on_replace trigger in _index space,
+-- which inserts tuples in temp and loc spaces (see gh-5304-insert_during_recovery.lua)!
+s0 = box.schema.space.create('test')
+ | ---
+ | ...
+i0 = s0:create_index('test')
+ | ---
+ | ...
+box.space.temp:select()
+ | ---
+ | - - [1]
+ | ...
+box.space.loc:select()
+ | ---
+ | - - [1]
+ | ...
+s0:drop()
+ | ---
+ | ...
+
+test_run:cmd('switch default')
+ | ---
+ | - true
+ | ...
 test_run:cmd('stop server test')
  | ---
  | - true
@@ -69,6 +105,44 @@ test_run:cmd('start server test with args="insert is_recovery_finished"')
  | ---
  | - true
  | ...
+test_run:cmd('switch test')
+ | ---
+ | - true
+ | ...
+box.space.temp:select()
+ | ---
+ | - []
+ | ...
+box.space.loc:select()
+ | ---
+ | - - [1]
+ | ...
+-- Creating a new space and index invoke on_replace trigger in _index space.
+-- Here we get error during insert tuple in loc space, because the tuple
+-- with the same primary key is already in the loc space
+s0 = box.schema.space.create('test')
+ | ---
+ | ...
+i0 = s0:create_index('test')
+ | ---
+ | - error: Duplicate key exists in unique index 'test' in space 'loc'
+ | ...
+box.space.temp:select()
+ | ---
+ | - []
+ | ...
+box.space.loc:select()
+ | ---
+ | - - [1]
+ | ...
+s0:drop()
+ | ---
+ | ...
+
+test_run:cmd('switch default')
+ | ---
+ | - true
+ | ...
 test_run:cmd('stop server test')
  | ---
  | - true
@@ -77,6 +151,40 @@ test_run:cmd('start server test with args="upsert is_recovery_finished"')
  | ---
  | - true
  | ...
+test_run:cmd('switch test')
+ | ---
+ | - true
+ | ...
+box.space.temp:select()
+ | ---
+ | - []
+ | ...
+box.space.loc:select()
+ | ---
+ | - - [1]
+ | ...
+s0 = box.schema.space.create('test')
+ | ---
+ | ...
+i0 = s0:create_index('test')
+ | ---
+ | ...
+box.space.temp:select()
+ | ---
+ | - - [1]
+ | ...
+box.space.loc:select()
+ | ---
+ | - - [1]
+ | ...
+s0:drop()
+ | ---
+ | ...
+
+test_run:cmd('switch default')
+ | ---
+ | - true
+ | ...
 test_run:cmd('stop server test')
  | ---
  | - true
diff --git a/test/box/gh-5304-insert_during_recovery.test.lua b/test/box/gh-5304-insert_during_recovery.test.lua
index 97171e9641..2846499c7f 100644
--- a/test/box/gh-5304-insert_during_recovery.test.lua
+++ b/test/box/gh-5304-insert_during_recovery.test.lua
@@ -18,10 +18,45 @@ test_run:cmd('start server test with args="replace" with crash_expected=True')
 test_run:cmd('start server test with args="insert" with crash_expected=True')
 test_run:cmd('start server test with args="upsert" with crash_expected=True')
 test_run:cmd('start server test with args="replace is_recovery_finished"')
+test_run:cmd('switch test')
+box.space.temp:select()
+box.space.loc:select()
+-- Creating a new space and index invokes on_replace trigger in _index space,
+-- which inserts tuples in temp and loc spaces (see gh-5304-insert_during_recovery.lua)!
+s0 = box.schema.space.create('test')
+i0 = s0:create_index('test')
+box.space.temp:select()
+box.space.loc:select()
+s0:drop()
+
+test_run:cmd('switch default')
 test_run:cmd('stop server test')
 test_run:cmd('start server test with args="insert is_recovery_finished"')
+test_run:cmd('switch test')
+box.space.temp:select()
+box.space.loc:select()
+-- Creating a new space and index invoke on_replace trigger in _index space.
+-- Here we get error during insert tuple in loc space, because the tuple
+-- with the same primary key is already in the loc space
+s0 = box.schema.space.create('test')
+i0 = s0:create_index('test')
+box.space.temp:select()
+box.space.loc:select()
+s0:drop()
+
+test_run:cmd('switch default')
 test_run:cmd('stop server test')
 test_run:cmd('start server test with args="upsert is_recovery_finished"')
+test_run:cmd('switch test')
+box.space.temp:select()
+box.space.loc:select()
+s0 = box.schema.space.create('test')
+i0 = s0:create_index('test')
+box.space.temp:select()
+box.space.loc:select()
+s0:drop()
+
+test_run:cmd('switch default')
 test_run:cmd('stop server test')
 test_run:cmd('cleanup server test')
 test_run:cmd('delete server test')
-- 
GitLab