Skip to content

Commit

Permalink
net.box: fix fetching of schema of an old version
Browse files Browse the repository at this point in the history
After 2.2.0-633-gaa0964ae1 ('net.box: fix schema fetching from 1.10/2.1
servers') net.box expects that _vcollation system view exists on a
tarantool server of 2.2.1+ version. This is however not always so: a
server may be run on a new version of tarantool, but work on a schema of
an old version.

The situation with non last schema is usual for replication cluster in
process of upgrading: all instances run on the new version of tarantool
first (no auto-upgrade is performed by tarantools in a cluster). Then
box.schema.upgrade() should be called, but the instances should be
operable even before the call.

Before the commit net.box was unable to connect a server if it is run on
a schema without _vcollation system view (say, 2.1.3), but the server
executable is of 2.2.1 version or newer.

Follows up #4307
Fixes #4691
  • Loading branch information
Totktonada committed Apr 1, 2020
1 parent 9933c5d commit 65798b7
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 0 deletions.
9 changes: 9 additions & 0 deletions src/box/lua/net_box.lua
Expand Up @@ -53,6 +53,7 @@ local E_UNKNOWN = box.error.UNKNOWN
local E_NO_CONNECTION = box.error.NO_CONNECTION
local E_TIMEOUT = box.error.TIMEOUT
local E_PROC_LUA = box.error.PROC_LUA
local E_NO_SUCH_SPACE = box.error.NO_SUCH_SPACE

-- utility tables
local is_final_state = {closed = 1, error = 1}
Expand Down Expand Up @@ -779,6 +780,13 @@ local function create_transport(host, port, user, password, callback,
local status = hdr[IPROTO_STATUS_KEY]
local response_schema_version = hdr[IPROTO_SCHEMA_VERSION_KEY]
if status ~= 0 then
-- No _vcollation space (server has an old
-- schema version).
local errno = band(status, IPROTO_ERRNO_MASK)
if id == select3_id and errno == E_NO_SUCH_SPACE then
peer_has_vcollation = false
goto continue
end
local body
body, body_end = decode(body_rpos)
return error_sm(E_NO_CONNECTION, body[IPROTO_ERROR_KEY])
Expand All @@ -793,6 +801,7 @@ local function create_transport(host, port, user, password, callback,
body, body_end = decode(body_rpos)
response[id] = body[IPROTO_DATA_KEY]
end
::continue::
until response[select1_id] and response[select2_id] and
(not peer_has_vcollation or response[select3_id])
-- trick: response[select3_id] is nil when the key is nil
Expand Down
93 changes: 93 additions & 0 deletions test/box-tap/gh-4691-net-box-connect-schema-2-1-3.test.lua
@@ -0,0 +1,93 @@
#!/usr/bin/env tarantool

--
-- gh-4691: net.box fails to connect to tarantool-2.2+ server with
-- a schema of version 2.1.3 or below (w/o _vcollation system
-- space).
--
-- Tarantool does not update a schema automatically when an
-- instance is in a replication cluster, because it may break
-- instances that are run under an old tarantool version. It is
-- quite usual to have non-upgraded instances for some time,
-- because upgrade is usually performed step-by-step.
--
-- net.box leans on a server version from greeting to determine
-- whether _vcollation system view should exist and reports an
-- error if fetching of the space fails. This causes the problem:
-- a version may be 2.2+, but _vcollation does not exists, because
-- schema upgrade is not performed yet. The fix of gh-4691 allows
-- the server to respond ER_NO_SUCH_SPACE for the query.
--

local test_run = require('test_run').new()
local net_box = require('net.box')
local tap = require('tap')

local function before_all()
local opts = {
'script = "box-tap/no_auto_schema_upgrade.lua"',
'workdir = "box-tap/snap/2.1.3"',
'return_listen_uri = True',
}
local opts_str = table.concat(opts, ', ')
local cmd = 'create server schema_2_1_3 with %s'
local uri = test_run:cmd(cmd:format(opts_str))
test_run:cmd('start server schema_2_1_3')

-- Create 'test' space with 'unicode_ci' index part.
--
-- We need it to verify that net.box will expose collation_id
-- for an index key part when collation names information is
-- not available.
--
-- Note: read_only = false on reconfiguration does not lead to
-- a schema upgrading.
test_run:eval('schema_2_1_3', ([[
box.cfg{read_only = false}
box.schema.create_space('test')
box.space.test:create_index('pk', {parts =
{{field = 1, type = 'string', collation = 'unicode_ci'}}})
box.cfg{read_only = true}
]]):gsub('\n', ' '))
return uri
end

local function test_connect_schema_2_1_3(test, uri)
test:plan(3)

local connection = net_box.connect(uri)

-- Connection is alive.
test:ok(connection:is_connected(), 'connection is alive')

-- Space metainfo is correct: collation_id is exposed when
-- collation name information is not available.
local key_part = connection.space.test.index[0].parts[1]
test:is(key_part.collation, nil,
'collation names are not available')
test:is(type(key_part.collation_id), 'number',
'collation numeric ids are exposed')

connection:close()
end

local function after_all()
-- Drop 'test' space.
test_run:eval('schema_2_1_3', ([[
box.cfg{read_only = false}
box.space.test:drop()
box.cfg{read_only = true}
]]):gsub('\n', ' '))

test_run:cmd('stop server schema_2_1_3')
test_run:cmd('cleanup server schema_2_1_3')
test_run:cmd('delete server schema_2_1_3')
end

local uri = before_all()
local test = tap.test('gh-4691-net-box-connect-schema-2-1-3')
test:plan(1)
test:test('connect_schema_2_1_3', test_connect_schema_2_1_3, uri)
after_all()

os.exit(test:check() and 0 or 1)
30 changes: 30 additions & 0 deletions test/box-tap/no_auto_schema_upgrade.lua
@@ -0,0 +1,30 @@
#!/usr/bin/env tarantool

--
-- This instance is intended to be used with existing snapshot
-- from a previous tarantool version. It does not perform
-- automatic schema upgrade.
--
-- Having tarantool in this state allows us to create test cases
-- for net.box, relay, applier connected to an instance in such
-- state or test behaviour of the instance itself.
--
-- There are two ways to disable automatic schema upgrade: set
-- 'replication' box.cfg(<...>) option or set 'read_only' option.
-- Use the latter one, because it is simpler.
--

box.cfg({
listen = os.getenv('LISTEN'),
read_only = true,
})

-- Give 'guest' user read/write accesses to all spaces.
--
-- Note: reconfiguration with read_only = false does not lead to
-- schema upgrading.
box.cfg{read_only = false}
box.schema.user.grant('guest', 'read, write', 'universe')
box.cfg{read_only = true}

require('console').listen(os.getenv('ADMIN'))
Binary file added test/box-tap/snap/2.1.3/00000000000000000000.snap
Binary file not shown.

0 comments on commit 65798b7

Please sign in to comment.