From 457b293cb820bccce7424fa03bd23662be2a6b1a Mon Sep 17 00:00:00 2001 From: Vladimir Davydov Date: Fri, 13 Jan 2023 13:36:18 +0300 Subject: [PATCH] net.box: add info about sequence to remote space object A remote space object presented by a net.box connection mimics the API of a local space object presented by box.space. Currently, it misses information about sequences. Let's add it. Note, we have to handle the case when the recently introduced _vspace_sequence system space view is missing on the remote host. To check that this works correctly, we reuse the 2.10.4 test data created by commit 1c33484d535e ("box: add auth_history and last_modified fields to _user space"). We also add the 'gen.lua' that can be used to regenerated the data. Closes #7858 NO_DOC=bug fix --- .../gh-7858-net-box-space-sequence.md | 4 + src/box/lua/net_box.c | 37 ++++-- src/box/lua/net_box.lua | 20 ++- .../gh_7858_net_box_space_sequence_test.lua | 114 ++++++++++++++++++ .../upgrade/2.10.4/00000000000000000009.snap | Bin 5114 -> 0 bytes .../upgrade/2.10.4/00000000000000000014.snap | Bin 0 -> 5198 bytes test/box-luatest/upgrade/2.10.4/gen.lua | 15 +++ ...ox_iproto_transactions_over_streams.result | 5 +- ..._iproto_transactions_over_streams.test.lua | 3 +- 9 files changed, 179 insertions(+), 19 deletions(-) create mode 100644 changelogs/unreleased/gh-7858-net-box-space-sequence.md create mode 100644 test/box-luatest/gh_7858_net_box_space_sequence_test.lua delete mode 100644 test/box-luatest/upgrade/2.10.4/00000000000000000009.snap create mode 100644 test/box-luatest/upgrade/2.10.4/00000000000000000014.snap create mode 100644 test/box-luatest/upgrade/2.10.4/gen.lua diff --git a/changelogs/unreleased/gh-7858-net-box-space-sequence.md b/changelogs/unreleased/gh-7858-net-box-space-sequence.md new file mode 100644 index 000000000000..87e607766825 --- /dev/null +++ b/changelogs/unreleased/gh-7858-net-box-space-sequence.md @@ -0,0 +1,4 @@ +## bugfix/core + +* Added information about the sequence used by a remote space to net.box + connection (gh-7858). diff --git a/src/box/lua/net_box.c b/src/box/lua/net_box.c index 3eb19d6c3e8c..307057f12ca1 100644 --- a/src/box/lua/net_box.c +++ b/src/box/lua/net_box.c @@ -2685,8 +2685,8 @@ netbox_transport_fetch_schema(struct netbox_transport *transport, transport->state = NETBOX_FETCH_SCHEMA; netbox_transport_on_state_change(transport, L); uint32_t peer_version_id = transport->greeting.version_id; - /* _vcollation view was added in 2.2.0-389-g3e3ef182f */ bool peer_has_vcollation = peer_version_id >= version_id(2, 2, 1); + bool peer_has_vspace_sequence = peer_version_id >= version_id(2, 10, 5); restart: lua_newtable(L); int schema_table_idx = lua_gettop(L); @@ -2700,9 +2700,15 @@ netbox_transport_fetch_schema(struct netbox_transport *transport, if (peer_has_vcollation) netbox_encode_select_all(L, &transport->send_buf, vcollation_sync, BOX_VCOLLATION_ID); + uint64_t vspace_sequence_sync = transport->next_sync++; + if (peer_has_vspace_sequence) + netbox_encode_select_all(L, &transport->send_buf, + vspace_sequence_sync, + BOX_VSPACE_SEQUENCE_ID); bool got_vspace = false; bool got_vindex = false; bool got_vcollation = false; + bool got_vspace_sequence = false; schema_version = 0; do { struct xrow_header hdr; @@ -2710,20 +2716,22 @@ netbox_transport_fetch_schema(struct netbox_transport *transport, luaT_error(L); if (hdr.sync != vspace_sync && hdr.sync != vindex_sync && - hdr.sync != vcollation_sync) { + hdr.sync != vcollation_sync && + hdr.sync != vspace_sequence_sync) { netbox_transport_dispatch_response(transport, L, &hdr); continue; } if (iproto_type_is_error(hdr.type)) { uint32_t errcode = hdr.type & (IPROTO_TYPE_ERROR - 1); - if (errcode == ER_NO_SUCH_SPACE && - hdr.sync == vcollation_sync) { - /* - * No _vcollation space - * (server has old schema version). - */ - peer_has_vcollation = false; - continue; + if (errcode == ER_NO_SUCH_SPACE) { + /* Server may have old dd version. */ + if (hdr.sync == vcollation_sync) { + peer_has_vcollation = false; + continue; + } else if (hdr.sync == vspace_sequence_sync) { + peer_has_vspace_sequence = false; + continue; + } } xrow_decode_error(&hdr); luaT_error(L); @@ -2750,6 +2758,9 @@ netbox_transport_fetch_schema(struct netbox_transport *transport, } else if (hdr.sync == vcollation_sync) { key = BOX_VCOLLATION_ID; got_vcollation = true; + } else if (hdr.sync == vspace_sequence_sync) { + key = BOX_VSPACE_SEQUENCE_ID; + got_vspace_sequence = true; } else { unreachable(); } @@ -2757,7 +2768,8 @@ netbox_transport_fetch_schema(struct netbox_transport *transport, tuple_format_runtime); lua_rawseti(L, schema_table_idx, key); } while (!(got_vspace && got_vindex && - (got_vcollation || !peer_has_vcollation))); + (got_vcollation || !peer_has_vcollation) && + (got_vspace_sequence || !peer_has_vspace_sequence))); /* Invoke the 'did_fetch_schema' callback. */ lua_rawgeti(L, LUA_REGISTRYINDEX, transport->opts.callback_ref); lua_pushliteral(L, "did_fetch_schema"); @@ -2765,7 +2777,8 @@ netbox_transport_fetch_schema(struct netbox_transport *transport, lua_rawgeti(L, schema_table_idx, BOX_VSPACE_ID); lua_rawgeti(L, schema_table_idx, BOX_VINDEX_ID); lua_rawgeti(L, schema_table_idx, BOX_VCOLLATION_ID); - lua_call(L, 5, 0); + lua_rawgeti(L, schema_table_idx, BOX_VSPACE_SEQUENCE_ID); + lua_call(L, 6, 0); /* Pop the schema table. */ lua_pop(L, 1); return schema_version; diff --git a/src/box/lua/net_box.lua b/src/box/lua/net_box.lua index 5f4d4e5b2462..c74156081ada 100644 --- a/src/box/lua/net_box.lua +++ b/src/box/lua/net_box.lua @@ -936,7 +936,7 @@ function remote_methods:wait_state(state, timeout) end function remote_methods:_install_schema(schema_version, spaces, indices, - collations) + collations, space_sequences) local sl, space_mt, index_mt = {}, self._space_mt, self._index_mt for _, space in pairs(spaces) do local name = space[3] @@ -977,6 +977,13 @@ function remote_methods:_install_schema(schema_version, spaces, indices, sl[name] = s end + local seql = {} + if space_sequences ~= nil then + for _, seq in ipairs(space_sequences) do + seql[seq[1]] = seq + end + end + for _, index in pairs(indices) do local idx = { space = index[1], @@ -1033,6 +1040,17 @@ function remote_methods:_install_schema(schema_version, spaces, indices, idx.unique = not not index[OPTS].unique end + if idx.id == 0 then + local seq = seql[idx.space] + if seq ~= nil then + idx.sequence_id = seq[2] + idx.sequence_fieldno = seq[4] + 1 + if seq[5] ~= '' then + idx.sequence_path = seq[5] + end + end + end + if sl[idx.space] ~= nil then sl[idx.space].index[idx.id] = idx sl[idx.space].index[idx.name] = idx diff --git a/test/box-luatest/gh_7858_net_box_space_sequence_test.lua b/test/box-luatest/gh_7858_net_box_space_sequence_test.lua new file mode 100644 index 000000000000..3c50555cc84c --- /dev/null +++ b/test/box-luatest/gh_7858_net_box_space_sequence_test.lua @@ -0,0 +1,114 @@ +local server = require('luatest.server') +local t = require('luatest') + +local g = t.group('gh_7858_net_box_space_sequence') + +g.before_all(function(cg) + cg.server = server:new({alias = 'default'}) + cg.server:start() +end) + +g.after_all(function(cg) + cg.server:drop() +end) + +g.after_test('test_net_box_space_sequence', function(cg) + cg.server:exec(function() + if box.space.test1 ~= nil then + box.space.test1:drop() + end + if box.space.test2 ~= nil then + box.space.test2:drop() + end + if box.space.test3 ~= nil then + box.space.test3:drop() + end + if box.sequence.test ~= nil then + box.sequence.test:drop() + end + end) +end) + +g.test_net_box_space_sequence = function(cg) + cg.server:exec(function() + local t = require('luatest') + local net = require('net.box') + box.schema.sequence.create('test') + box.schema.create_space('test1') + box.space.test1:create_index('pk') + box.space.test1:create_index('sk', {parts = {2, 'unsigned'}}) + box.schema.create_space('test2') + box.space.test2:create_index('pk', {sequence = true}) + box.space.test2:create_index('sk', {parts = {2, 'unsigned'}}) + box.schema.create_space('test3') + box.space.test3:create_index('pk', { + parts = {{'[2].foo.bar', 'unsigned'}}, + sequence = box.sequence.test.id, + }) + box.space.test3:create_index('sk', {parts = {1, 'unsigned'}}) + local conn = net.connect(box.cfg.listen) + t.assert_equals(conn.space.test1.index.pk.sequence_id, nil) + t.assert_equals(conn.space.test1.index.pk.sequence_fieldno, nil) + t.assert_equals(conn.space.test1.index.pk.sequence_path, nil) + t.assert_equals(conn.space.test1.index.sk.sequence_id, nil) + t.assert_equals(conn.space.test1.index.sk.sequence_fieldno, nil) + t.assert_equals(conn.space.test1.index.sk.sequence_path, nil) + t.assert_equals(conn.space.test2.index.pk.sequence_id, + box.sequence.test2_seq.id) + t.assert_equals(conn.space.test2.index.pk.sequence_fieldno, 1) + t.assert_equals(conn.space.test2.index.pk.sequence_path, nil) + t.assert_equals(conn.space.test2.index.sk.sequence_id, nil) + t.assert_equals(conn.space.test2.index.sk.sequence_fieldno, nil) + t.assert_equals(conn.space.test2.index.sk.sequence_path, nil) + t.assert_equals(conn.space.test3.index.pk.sequence_id, + box.sequence.test.id) + t.assert_equals(conn.space.test3.index.pk.sequence_fieldno, 2) + t.assert_equals(conn.space.test3.index.pk.sequence_path, '.foo.bar') + t.assert_equals(conn.space.test3.index.sk.sequence_id, nil) + t.assert_equals(conn.space.test3.index.sk.sequence_fieldno, nil) + t.assert_equals(conn.space.test3.index.sk.sequence_path, nil) + conn:close() + end) +end + +local g_old_schema = t.group('gh_7858_net_box_space_sequence.old_schema') + +g_old_schema.before_test('test_net_box_space_sequence', function(cg) + t.tarantool.skip_if_not_debug() + cg.server = server:new({ + alias = 'default', + datadir = 'test/box-luatest/upgrade/2.10.4', + env = {ERRINJ_AUTO_UPGRADE = 'true'}, + }) + cg.server:start() +end) + +g_old_schema.after_test('test_net_box_space_sequence', function(cg) + cg.server:drop() +end) + +g_old_schema.test_net_box_space_sequence = function(cg) + cg.server:exec(function() + local t = require('luatest') + local net = require('net.box') + t.assert_not_equals(box.space.gh_7858, nil) + t.assert_not_equals(box.sequence.gh_7858_seq, nil) + t.assert_equals(box.space.gh_7858.index.pk.sequence_id, + box.sequence.gh_7858_seq.id) + t.assert_equals(box.space.gh_7858.index.pk.sequence_fieldno, 2) + t.assert_equals(box.space.gh_7858.index.pk.sequence_path, '[3]') + local conn = net.connect(box.cfg.listen) + t.assert_equals(conn.error, nil) + t.assert_not_equals(conn.space.gh_7858, nil) + t.assert_equals(conn.space.gh_7858.index.pk.sequence_id, nil) + t.assert_equals(conn.space.gh_7858.index.pk.sequence_fieldno, nil) + t.assert_equals(conn.space.gh_7858.index.pk.sequence_path, nil) + box.schema.upgrade() + t.assert_equals(conn:ping(), true) + t.assert_equals(conn.space.gh_7858.index.pk.sequence_id, + box.sequence.gh_7858_seq.id) + t.assert_equals(conn.space.gh_7858.index.pk.sequence_fieldno, 2) + t.assert_equals(conn.space.gh_7858.index.pk.sequence_path, '[3]') + conn:close() + end) +end diff --git a/test/box-luatest/upgrade/2.10.4/00000000000000000009.snap b/test/box-luatest/upgrade/2.10.4/00000000000000000009.snap deleted file mode 100644 index d21bba921962fc4a1602f651e9af35e1d361bebb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5114 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 diff --git a/test/box-luatest/upgrade/2.10.4/00000000000000000014.snap b/test/box-luatest/upgrade/2.10.4/00000000000000000014.snap new file mode 100644 index 0000000000000000000000000000000000000000..9b3148317f2a10a22a9228164b7de0547d4dac38 GIT binary patch literal 5198 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 ISJe=$?RVU