Skip to content

Commit

Permalink
schema: support cached schema
Browse files Browse the repository at this point in the history
  • Loading branch information
DifferentialOrange committed Oct 9, 2023
1 parent 149407b commit 00ebe5f
Show file tree
Hide file tree
Showing 4 changed files with 167 additions and 85 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1728,6 +1728,8 @@ where:
* `vshard_router` (`?string|table`) - Cartridge vshard group name or
vshard router instance. Set this parameter if your space is not
a part of the default vshard cluster
* `cached` (`?boolean`) - if `false`, reloads storages schema on call;
if `true`, return last known schema; default value is `false`

Returns space schema (or spaces schema map), error.

Expand Down
13 changes: 8 additions & 5 deletions crud/schema.lua
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ local utils = require('crud.common.utils')

local schema = {}

local system_spaces = {
schema.system_spaces = {
-- https://github.com/tarantool/tarantool/blob/3240201a2f5bac3bddf8a74015db9b351954e0b5/src/box/schema_def.h#L77-L127
['_vinyl_deferred_delete'] = true,
['_schema'] = true,
Expand Down Expand Up @@ -69,6 +69,7 @@ schema.call = function(space_name, opts)
checks('?string', {
vshard_router = '?string|table',
timeout = '?number',
cached = '?boolean',
})

opts = opts or {}
Expand All @@ -78,9 +79,11 @@ schema.call = function(space_name, opts)
return nil, SchemaError:new(err)
end

local _, err = schema_module.reload_schema(vshard_router)
if err ~= nil then
return nil, SchemaError:new(err)
if opts.cached ~= true then
local _, err = schema_module.reload_schema(vshard_router)
if err ~= nil then
return nil, SchemaError:new(err)
end
end

local spaces, err = utils.get_spaces(vshard_router, opts.timeout)
Expand All @@ -100,7 +103,7 @@ schema.call = function(space_name, opts)
for name, space in pairs(spaces) do
-- Can be indexed by space id and space name,
-- so we need to be careful with duplicates.
if type(name) == 'string' and system_spaces[name] == nil then
if type(name) == 'string' and schema.system_spaces[name] == nil then
resp[name] = get_crud_schema(space)
end
end
Expand Down
111 changes: 71 additions & 40 deletions test/entrypoint/srv_schema/storage_init.lua
Original file line number Diff line number Diff line change
@@ -1,53 +1,84 @@
local schema = require('crud.schema')

return function()
if box.info.ro == true then
return
end

local engine = os.getenv('ENGINE') or 'memtx'

local customers_space = box.schema.space.create('customers', {
format = {
{name = 'id', type = 'unsigned'},
{name = 'bucket_id', type = 'unsigned'},
{name = 'name', type = 'string'},
{name = 'age', type = 'number'},
},
if_not_exists = true,
engine = engine,
})
customers_space:create_index('id', {
parts = { {field = 'id'} },
if_not_exists = true,
})
customers_space:create_index('bucket_id', {
parts = { {field = 'bucket_id'} },
unique = false,
if_not_exists = true,
})

local shops_space = box.schema.space.create('shops', {
format = {
rawset(_G, 'reload_schema', function()
for name, space in pairs(box.space) do
-- Can be indexed by space id and space name,
-- so we need to be careful with duplicates.
if type(name) == 'string' and schema.system_spaces[name] == nil then
space:drop()
end
end

local customers_space = box.schema.space.create('customers', {
format = {
{name = 'id', type = 'unsigned'},
{name = 'bucket_id', type = 'unsigned'},
{name = 'name', type = 'string'},
{name = 'age', type = 'number'},
},
if_not_exists = true,
engine = engine,
})
customers_space:create_index('id', {
parts = { {field = 'id'} },
if_not_exists = true,
})
customers_space:create_index('bucket_id', {
parts = { {field = 'bucket_id'} },
unique = false,
if_not_exists = true,
})

local shops_space = box.schema.space.create('shops', {
format = {
{name = 'registry_id', type = 'unsigned'},
{name = 'bucket_id', type = 'unsigned'},
{name = 'name', type = 'string'},
{name = 'address', type = 'string'},
{name = 'owner', type = 'string', is_nullable = true},
},
if_not_exists = true,
engine = engine,
})
shops_space:create_index('registry', {
parts = { {field = 'registry_id'} },
if_not_exists = true,
})
shops_space:create_index('bucket_id', {
parts = { {field = 'bucket_id'} },
unique = false,
if_not_exists = true,
})
shops_space:create_index('address', {
parts = { {field = 'address'} },
unique = true,
if_not_exists = true,
})
end)

rawset(_G, 'alter_schema', function()
box.space['customers']:create_index('age', {
parts = { {field = 'age'} },
unique = false,
if_not_exists = true,
})

box.space['shops']:format({
{name = 'registry_id', type = 'unsigned'},
{name = 'bucket_id', type = 'unsigned'},
{name = 'name', type = 'string'},
{name = 'address', type = 'string'},
{name = 'owner', type = 'string', is_nullable = true},
},
if_not_exists = true,
engine = engine,
})
shops_space:create_index('registry', {
parts = { {field = 'registry_id'} },
if_not_exists = true,
})
shops_space:create_index('bucket_id', {
parts = { {field = 'bucket_id'} },
unique = false,
if_not_exists = true,
})
shops_space:create_index('address', {
parts = { {field = 'address'} },
unique = true,
if_not_exists = true,
})
{name = 'salary', type = 'unsigned', is_nullable = true},
})
end)

rawget(_G, 'reload_schema')()
end
126 changes: 86 additions & 40 deletions test/integration/schema_test.lua
Original file line number Diff line number Diff line change
Expand Up @@ -17,53 +17,79 @@ pgroup.after_all(function(g)
helpers.stop_cluster(g.cluster, g.params.backend)
end)

local function expected_schema()
local schema = {
customers = {
format = {
{name = "id", type = "unsigned"},
{name = "bucket_id", type = "unsigned", is_nullable = true},
{name = "name", type = "string"},
{name = "age", type = "number"},
},
indexes = {
[0] = {
id = 0,
name = "id",
parts = {{exclude_null = false, fieldno = 1, is_nullable = false, type = "unsigned"}},
type = "TREE",
unique = true,
},
pgroup.after_each(function(g)
helpers.call_on_servers(g.cluster, {'s1-master', 's2-master'}, function(server)
server:call('reload_schema')
end)

local _, err = g.router:call('crud.schema')
assert(err == nil)
end)

local schema = {
customers = {
format = {
{name = "id", type = "unsigned"},
{name = "bucket_id", type = "unsigned", is_nullable = true},
{name = "name", type = "string"},
{name = "age", type = "number"},
},
indexes = {
[0] = {
id = 0,
name = "id",
parts = {{exclude_null = false, fieldno = 1, is_nullable = false, type = "unsigned"}},
type = "TREE",
unique = true,
},
},
shops = {
format = {
{name = 'registry_id', type = 'unsigned'},
{name = 'bucket_id', type = 'unsigned', is_nullable = true},
{name = 'name', type = 'string'},
{name = 'address', type = 'string'},
{name = 'owner', type = 'string', is_nullable = true},
},
shops = {
format = {
{name = 'registry_id', type = 'unsigned'},
{name = 'bucket_id', type = 'unsigned', is_nullable = true},
{name = 'name', type = 'string'},
{name = 'address', type = 'string'},
{name = 'owner', type = 'string', is_nullable = true},
},
indexes = {
[0] = {
id = 0,
name = "registry",
parts = {{exclude_null = false, fieldno = 1, is_nullable = false, type = "unsigned"}},
type = "TREE",
unique = true,
},
indexes = {
[0] = {
id = 0,
name = "registry",
parts = {{exclude_null = false, fieldno = 1, is_nullable = false, type = "unsigned"}},
type = "TREE",
unique = true,
},
[2] = {
id = 2,
name = "address",
parts = {{exclude_null = false, fieldno = 4, is_nullable = false, type = "string"}},
type = "TREE",
unique = true,
},
[2] = {
id = 2,
name = "address",
parts = {{exclude_null = false, fieldno = 4, is_nullable = false, type = "string"}},
type = "TREE",
unique = true,
},
},
},
}

local function expected_schema()
local sch = table.deepcopy(schema)
return helpers.schema_compatibility(sch)
end

local function altered_schema()
local sch = table.deepcopy(schema)

sch['customers'].indexes[2] = {
id = 2,
name = "age",
parts = {{exclude_null = false, fieldno = 4, is_nullable = false, type = "number"}},
type = "TREE",
unique = false,
}

return helpers.schema_compatibility(schema)
sch['shops'].format[6] = {name = 'salary', type = 'unsigned', is_nullable = true}

return helpers.schema_compatibility(sch)
end

pgroup.test_get_all = function(g)
Expand Down Expand Up @@ -92,3 +118,23 @@ pgroup.test_timeout_option = function(g)

t.assert_equals(err, nil)
end

pgroup.test_schema_cached = function(g)
helpers.call_on_servers(g.cluster, {'s1-master', 's2-master'}, function(server)
server:call('alter_schema')
end)

local result_after, err = g.router:call('crud.schema', {nil, {cached = true}})
t.assert_equals(err, nil)
t.assert_equals(result_after, expected_schema())
end

pgroup.test_schema_reloaded = function(g)
helpers.call_on_servers(g.cluster, {'s1-master', 's2-master'}, function(server)
server:call('alter_schema')
end)

local result_after, err = g.router:call('crud.schema', {nil, {cached = false}})
t.assert_equals(err, nil)
t.assert_equals(result_after, altered_schema())
end

0 comments on commit 00ebe5f

Please sign in to comment.