From 15928ecae76bc170240a39b3f2812ba9629d12f9 Mon Sep 17 00:00:00 2001
From: Georgy Moshkin <gmoshkin@picodata.io>
Date: Mon, 15 May 2023 13:16:14 +0300
Subject: [PATCH] test: cleanup ddl tests

---
 src/storage.rs       |   2 +
 test/int/test_ddl.py | 152 +++++++++++++++++++++++++++++++------------
 2 files changed, 112 insertions(+), 42 deletions(-)

diff --git a/src/storage.rs b/src/storage.rs
index 2910769fe0..4499f0b899 100644
--- a/src/storage.rs
+++ b/src/storage.rs
@@ -424,6 +424,8 @@ impl Clusterwide {
             }
         }
 
+        // If we're not the replication master, the rest of the data will come
+        // via tarantool replication.
         if !is_master {
             return Ok(());
         }
diff --git a/test/int/test_ddl.py b/test/int/test_ddl.py
index c75890f504..176f14ea2a 100644
--- a/test/int/test_ddl.py
+++ b/test/int/test_ddl.py
@@ -2,15 +2,23 @@ from conftest import Cluster
 
 
 def test_ddl_create_space_bulky(cluster: Cluster):
-    # TODO: add 2 more instances, to check that another replicaset is handled
-    # correctly
-    i1, i2 = cluster.deploy(instance_count=2, init_replication_factor=2)
+    i1, i2, i3, i4 = cluster.deploy(instance_count=4, init_replication_factor=2)
 
     # At cluster boot schema version is 0
     assert i1.call("box.space._pico_property:get", "current_schema_version")[1] == 0
     assert i2.call("box.space._pico_property:get", "current_schema_version")[1] == 0
+    assert i3.call("box.space._pico_property:get", "current_schema_version")[1] == 0
+    assert i4.call("box.space._pico_property:get", "current_schema_version")[1] == 0
+
+    # And next schema version will be 1
+    assert i1.next_schema_version() == 1
+    assert i2.next_schema_version() == 1
+    assert i3.next_schema_version() == 1
+    assert i4.next_schema_version() == 1
 
+    ############################################################################
     # Propose a space creation which will fail
+
     abort_index = i1.ddl_create_space(
         dict(
             id=666,
@@ -21,39 +29,69 @@ def test_ddl_create_space_bulky(cluster: Cluster):
         ),
     )
 
+    i1.call(".proc_sync_raft", abort_index, (3, 0))
     i2.call(".proc_sync_raft", abort_index, (3, 0))
+    i3.call(".proc_sync_raft", abort_index, (3, 0))
+    i4.call(".proc_sync_raft", abort_index, (3, 0))
 
     # No space was created
     assert i1.call("box.space._pico_space:get", 666) is None
-    assert i1.call("box.space._space:get", 666) is None
     assert i2.call("box.space._pico_space:get", 666) is None
+    assert i3.call("box.space._pico_space:get", 666) is None
+    assert i4.call("box.space._pico_space:get", 666) is None
+    assert i1.call("box.space._space:get", 666) is None
     assert i2.call("box.space._space:get", 666) is None
+    assert i3.call("box.space._space:get", 666) is None
+    assert i4.call("box.space._space:get", 666) is None
 
     # Schema version hasn't changed
     assert i1.call("box.space._pico_property:get", "current_schema_version")[1] == 0
     assert i2.call("box.space._pico_property:get", "current_schema_version")[1] == 0
+    assert i3.call("box.space._pico_property:get", "current_schema_version")[1] == 0
+    assert i4.call("box.space._pico_property:get", "current_schema_version")[1] == 0
 
+    # But next schema version did change
+    assert i1.next_schema_version() == 2
+    assert i2.next_schema_version() == 2
+    assert i3.next_schema_version() == 2
+    assert i4.next_schema_version() == 2
+
+    ############################################################################
     # Propose a space creation which will succeed
+
     commit_index = i1.ddl_create_space(
         dict(
             id=666,
             name="stuff",
             format=[dict(name="id", type="unsigned", is_nullable=False)],
-            primary_key=[dict(field=0, type="unsigned")],
+            primary_key=[dict(field="id")],
             distribution=dict(kind="global"),
         ),
     )
 
+    i1.call(".proc_sync_raft", commit_index, (3, 0))
     i2.call(".proc_sync_raft", commit_index, (3, 0))
+    i3.call(".proc_sync_raft", commit_index, (3, 0))
+    i4.call(".proc_sync_raft", commit_index, (3, 0))
 
-    # Schema version was updated
+    # This time schema version did change
     assert i1.call("box.space._pico_property:get", "current_schema_version")[1] == 2
     assert i2.call("box.space._pico_property:get", "current_schema_version")[1] == 2
+    assert i3.call("box.space._pico_property:get", "current_schema_version")[1] == 2
+    assert i4.call("box.space._pico_property:get", "current_schema_version")[1] == 2
+
+    # And so did next schema version obviously
+    assert i1.next_schema_version() == 3
+    assert i2.next_schema_version() == 3
+    assert i3.next_schema_version() == 3
+    assert i4.next_schema_version() == 3
 
     # Space was created and is operable
     pico_space_def = [666, "stuff", ["global"], [["id", "unsigned", False]], 2, True]
     assert i1.call("box.space._pico_space:get", 666) == pico_space_def
     assert i2.call("box.space._pico_space:get", 666) == pico_space_def
+    assert i3.call("box.space._pico_space:get", 666) == pico_space_def
+    assert i4.call("box.space._pico_space:get", 666) == pico_space_def
 
     tt_space_def = [
         666,
@@ -66,6 +104,8 @@ def test_ddl_create_space_bulky(cluster: Cluster):
     ]
     assert i1.call("box.space._space:get", 666) == tt_space_def
     assert i2.call("box.space._space:get", 666) == tt_space_def
+    assert i3.call("box.space._space:get", 666) == tt_space_def
+    assert i4.call("box.space._space:get", 666) == tt_space_def
 
     # Primary index was also created
     # TODO: maybe we want to replace these `None`s with the default values when
@@ -82,6 +122,8 @@ def test_ddl_create_space_bulky(cluster: Cluster):
     ]
     assert i1.call("box.space._pico_index:get", [666, 0]) == pico_pk_def
     assert i2.call("box.space._pico_index:get", [666, 0]) == pico_pk_def
+    assert i3.call("box.space._pico_index:get", [666, 0]) == pico_pk_def
+    assert i4.call("box.space._pico_index:get", [666, 0]) == pico_pk_def
 
     tt_pk_def = [
         666,
@@ -93,27 +135,30 @@ def test_ddl_create_space_bulky(cluster: Cluster):
     ]
     assert i1.call("box.space._index:get", [666, 0]) == tt_pk_def
     assert i2.call("box.space._index:get", [666, 0]) == tt_pk_def
+    assert i3.call("box.space._index:get", [666, 0]) == tt_pk_def
+    assert i4.call("box.space._index:get", [666, 0]) == tt_pk_def
 
-    # Add a new replicaset master
-    i3 = cluster.add_instance(wait_online=True, replicaset_id="r2")
+    ############################################################################
+    # A new replicaset catches up after the fact successfully
 
-    # It's schema was updated automatically
-    assert i3.call("box.space._pico_property:get", "current_schema_version")[1] == 2
-    assert i3.call("box.space._pico_space:get", 666) == pico_space_def
-    assert i3.call("box.space._pico_index:get", [666, 0]) == pico_pk_def
-    # TODO: this fails
-    assert i3.call("box.space._space:get", 666) == tt_space_def
-    assert i3.call("box.space._index:get", [666, 0]) == tt_pk_def
+    i5 = cluster.add_instance(wait_online=True, replicaset_id="r3")
+
+    assert i5.call("box.space._pico_property:get", "current_schema_version")[1] == 2
+    assert i5.next_schema_version() == 3
+    assert i5.call("box.space._pico_space:get", 666) == pico_space_def
+    assert i5.call("box.space._pico_index:get", [666, 0]) == pico_pk_def
+    assert i5.call("box.space._space:get", 666) == tt_space_def
+    assert i5.call("box.space._index:get", [666, 0]) == tt_pk_def
 
-    # Add a follower to the new replicaset
-    i4 = cluster.add_instance(wait_online=True, replicaset_id="r2")
+    i6 = cluster.add_instance(wait_online=True, replicaset_id="r3")
 
     # It's schema was updated automatically as well
-    assert i4.call("box.space._pico_property:get", "current_schema_version")[1] == 2
-    assert i4.call("box.space._pico_space:get", 666) == pico_space_def
-    assert i4.call("box.space._pico_index:get", [666, 0]) == pico_pk_def
-    assert i4.call("box.space._space:get", 666) == tt_space_def
-    assert i4.call("box.space._index:get", [666, 0]) == tt_pk_def
+    assert i6.call("box.space._pico_property:get", "current_schema_version")[1] == 2
+    assert i6.next_schema_version() == 3
+    assert i6.call("box.space._pico_space:get", 666) == pico_space_def
+    assert i6.call("box.space._pico_index:get", [666, 0]) == pico_pk_def
+    assert i6.call("box.space._space:get", 666) == tt_space_def
+    assert i6.call("box.space._index:get", [666, 0]) == tt_pk_def
 
 
 def test_ddl_create_sharded_space(cluster: Cluster):
@@ -138,6 +183,7 @@ def test_ddl_create_sharded_space(cluster: Cluster):
     i1.call(".proc_sync_raft", index, (3, 0))
     i2.call(".proc_sync_raft", index, (3, 0))
 
+    ############################################################################
     # Space was created and is operable
     pico_space_def = [
         666,
@@ -173,6 +219,7 @@ def test_ddl_create_sharded_space(cluster: Cluster):
     assert i1.call("box.space._space:get", 666) == tt_space_def
     assert i2.call("box.space._space:get", 666) == tt_space_def
 
+    ############################################################################
     # Primary index was also created
     pico_pk_def = [
         666,
@@ -198,6 +245,7 @@ def test_ddl_create_sharded_space(cluster: Cluster):
     assert i1.call("box.space._index:get", [666, 0]) == tt_pk_def
     assert i2.call("box.space._index:get", [666, 0]) == tt_pk_def
 
+    ############################################################################
     # This time bucket id was also created
     pico_bucket_id_def = [
         666,
@@ -232,34 +280,32 @@ def test_ddl_create_space_partial_failure(cluster: Cluster):
     i3.eval("box.schema.space.create(...)", "space_name_conflict")
 
     # Propose a space creation which will fail
-    index = i1.ddl_create_space(
-        dict(
-            id=666,
-            name="space_name_conflict",
-            format=[dict(name="id", type="unsigned", is_nullable=False)],
-            primary_key=[dict(field=1, type="unsigned")],
-            distribution=dict(kind="global"),
-        ),
+    space_def = dict(
+        id=666,
+        name="space_name_conflict",
+        format=[dict(name="id", type="unsigned", is_nullable=False)],
+        primary_key=[dict(field="id")],
+        distribution=dict(kind="global"),
     )
+    index = i1.ddl_create_space(space_def)
 
     i2.call(".proc_sync_raft", index, (3, 0))
     i3.call(".proc_sync_raft", index, (3, 0))
 
     # No space was created
     assert i1.call("box.space._pico_space:get", 666) is None
-    assert i1.call("box.space._space:get", 666) is None
     assert i2.call("box.space._pico_space:get", 666) is None
-    assert i2.call("box.space._space:get", 666) is None
     assert i3.call("box.space._pico_space:get", 666) is None
+    assert i1.call("box.space._space:get", 666) is None
+    assert i2.call("box.space._space:get", 666) is None
     assert i3.call("box.space._space:get", 666) is None
 
-    # TODO: add instance which will conflict with this ddl and make sure it
-    # panics
+    # TODO: terminate i3, commit create space and wake i3 back up
 
 
 def test_ddl_from_snapshot(cluster: Cluster):
     # Second instance is only for quorum
-    i1, i2 = cluster.deploy(instance_count=2)
+    i1, i2 = cluster.deploy(instance_count=2, init_replication_factor=2)
 
     i1.assert_raft_status("Leader")
 
@@ -270,8 +316,8 @@ def test_ddl_from_snapshot(cluster: Cluster):
             id=666,
             name="stuff",
             format=[dict(name="id", type="unsigned", is_nullable=False)],
-            primary_key=[dict(field=1, type="unsigned")],
-            distribution=dict(kind="global"),
+            primary_key=[dict(field="id")],
+            distribution=dict(kind="sharded_implicitly", sharding_key=["id"]),
         ),
     )
 
@@ -284,7 +330,10 @@ def test_ddl_from_snapshot(cluster: Cluster):
         "memtx",
         0,
         dict(),
-        [dict(name="id", type="unsigned", is_nullable=False)],
+        [
+            dict(name="id", type="unsigned", is_nullable=False),
+            dict(name="bucket_id", type="unsigned", is_nullable=False),
+        ],
     ]
     assert i1.call("box.space._space:get", 666) == tt_space_def
     assert i2.call("box.space._space:get", 666) == tt_space_def
@@ -295,17 +344,36 @@ def test_ddl_from_snapshot(cluster: Cluster):
         "primary_key",
         "tree",
         dict(unique=True),
-        [[1, "unsigned", None, None, None]],
+        [[0, "unsigned", None, False, None]],
     ]
     assert i1.call("box.space._index:get", [666, 0]) == tt_pk_def
     assert i2.call("box.space._index:get", [666, 0]) == tt_pk_def
 
+    tt_bucket_id_def = [
+        666,
+        1,
+        "bucket_id",
+        "tree",
+        dict(unique=False),
+        [[1, "unsigned", None, False, None]],
+    ]
+    assert i1.call("box.space._index:get", [666, 1]) == tt_bucket_id_def
+    assert i2.call("box.space._index:get", [666, 1]) == tt_bucket_id_def
+
     # Compact the log to trigger snapshot for the newcommer
     i1.raft_compact_log()
     i2.raft_compact_log()
 
-    i3 = cluster.add_instance(wait_online=True)
-
-    # Check space was created from the snapshot data
+    # A replicaset master catches up from snapshot
+    i3 = cluster.add_instance(wait_online=True, replicaset_id="R2")
     assert i3.call("box.space._space:get", 666) == tt_space_def
     assert i3.call("box.space._index:get", [666, 0]) == tt_pk_def
+    assert i3.call("box.space._index:get", [666, 1]) == tt_bucket_id_def
+    assert i3.call("box.space._schema:get", "pico_schema_version")[1] == 1
+
+    # A replicaset follower catches up from snapshot
+    i4 = cluster.add_instance(wait_online=True, replicaset_id="R2")
+    assert i4.call("box.space._space:get", 666) == tt_space_def
+    assert i4.call("box.space._index:get", [666, 0]) == tt_pk_def
+    assert i4.call("box.space._index:get", [666, 1]) == tt_bucket_id_def
+    assert i4.call("box.space._schema:get", "pico_schema_version")[1] == 1
-- 
GitLab