Skip to content

Support vshard names as keys #404

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Dec 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/test_on_push.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,13 @@ jobs:
cartridge-version: "2.8.0"
- tarantool-version: "2.11"
metrics-version: "1.0.0"
vshard-version: "0.1.24"
vshard-version: "0.1.25"
- tarantool-version: "2.11"
external-merger-version: "0.0.5"
external-keydef-version: "0.0.4"
- tarantool-version: "master"
metrics-version: "1.0.0"
vshard-version: "0.1.24"
vshard-version: "0.1.25"
fail-fast: false
# Can't install older versions on 22.04,
# see https://github.com/tarantool/setup-tarantool/issues/36
Expand Down Expand Up @@ -141,7 +141,7 @@ jobs:
include:
- tarantool-version: "master"
metrics-version: "1.0.0"
vshard-version: "0.1.24"
vshard-version: "0.1.25"
fail-fast: false
runs-on: ubuntu-20.04
steps:
Expand Down
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## Unreleased

### Added
* `mode` option for `crud.min` and `crud.max` (#404).

### Fixed
* Compatibility with vshard 0.1.25 `name_as_key` identification mode
for Tarantool 3.0 (#403).

## [1.4.1] - 23-10-23

### Changed
Expand Down
28 changes: 26 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1100,9 +1100,33 @@ See more examples of pairs queries [here.](https://github.com/tarantool/crud/blo

### Min and max

`CRUD` supports operations to get the minimum (maximum) object from the space index

```lua
local objects, err = crud.min(space_name, index_id, opts)
local objects, err = crud.max(space_name, index_id, opts)
```

where:

* `space_name` (`string`) - name of the space
* `index_id` (`?string|number`) - index name or index id. Primary index by default
* `opts`:
* `timeout` (`?number`) - `vshard.call` timeout (in seconds)
* `fields` (`?table`) - field names for getting only a subset of fields
* `mode` (`?string`, `read` or `write`) - if `write` is specified then `select` is
performed on master, default value is `read`
* `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
* `fetch_latest_metadata` (`?boolean`) - guarantees the
up-to-date metadata (space format) in first return value, otherwise
it may not take into account the latest migration of the data format.
Performance overhead is up to 15%. `false` by default

```lua
-- Find the minimum value in the specified index
local result, err = crud.min(space_name, 'age', opts)
local result, err = crud.min('customers', 'age')
---
- metadata:
- {'name': 'id', 'type': 'unsigned'}
Expand All @@ -1113,7 +1137,7 @@ local result, err = crud.min(space_name, 'age', opts)
- [1, 477, 'Elizabeth', 12]

-- Find the maximum value in the specified index
local result, err = crud.max(space_name, 'age', opts)
local result, err = crud.max('customers', 'age')
---
- metadata:
- {'name': 'id', 'type': 'unsigned'}
Expand Down
18 changes: 3 additions & 15 deletions crud.lua
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ local stats = require('crud.stats')
local readview = require('crud.readview')
local schema = require('crud.schema')

local vshard = require('vshard')
local luri = require('uri')

local crud = {}
Expand Down Expand Up @@ -174,25 +173,14 @@ function crud.init_storage()

local user = nil
if not box.info.ro then
local ok, storage_info = pcall(vshard.storage.info)
if not ok then
error('vshard.storage.cfg() must be called first')
end
local replicaset_uuid, replicaset = utils.get_self_vshard_replicaset()

local box_info = box.info()
local replicaset_uuid
if box_info.replicaset ~= nil then
replicaset_uuid = box_info.replicaset.uuid
else
replicaset_uuid = box_info.cluster.uuid
end
local replicaset_info = storage_info.replicasets[replicaset_uuid]
if replicaset_info == nil or replicaset_info.master == nil then
if replicaset == nil or replicaset.master == nil then
error(string.format('Failed to find a vshard configuration for ' ..
' replicaset with replicaset_uuid %s.',
replicaset_uuid))
end
user = luri.parse(replicaset_info.master.uri).login or 'guest'
user = luri.parse(replicaset.master.uri).login or 'guest'
end

if rawget(_G, '_crud') == nil then
Expand Down
3 changes: 2 additions & 1 deletion crud/borders.lua
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ local function call_get_border_on_router(vshard_router, border_name, space_name,
checks('table', 'string', 'string', '?string|number', {
timeout = '?number',
fields = '?table',
mode = '?string',
vshard_router = '?string|table',
fetch_latest_metadata = '?boolean',
})
Expand Down Expand Up @@ -107,7 +108,7 @@ local function call_get_border_on_router(vshard_router, border_name, space_name,
return nil, BorderError:new("Failed to get router replicasets: %s", err)
end
local call_opts = {
mode = 'read',
mode = opts.mode or 'read',
replicasets = replicasets,
timeout = opts.timeout,
}
Expand Down
45 changes: 35 additions & 10 deletions crud/common/utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,10 @@ function utils.format_replicaset_error(replicaset_uuid, msg, ...)
end

local function get_replicaset_by_replica_uuid(replicasets, uuid)
for replicaset_uuid, replicaset in pairs(replicasets) do
for replica_uuid, _ in pairs(replicaset.replicas) do
if replica_uuid == uuid then
return replicasets[replicaset_uuid]
for _, replicaset in pairs(replicasets) do
for _, replica in pairs(replicaset.replicas) do
if replica.uuid == uuid then
return replicaset
end
end
end
Expand Down Expand Up @@ -1143,23 +1143,23 @@ function utils.storage_info(opts)
local timeout = opts.timeout or const.DEFAULT_VSHARD_CALL_TIMEOUT

for _, replicaset in pairs(replicasets) do
for replica_uuid, replica in pairs(replicaset.replicas) do
replica_state_by_uuid[replica_uuid] = {
for _, replica in pairs(replicaset.replicas) do
replica_state_by_uuid[replica.uuid] = {
status = "error",
is_master = replicaset.master == replica
}
local ok, res = pcall(replica.conn.call, replica.conn, CRUD_STORAGE_INFO_FUNC_NAME,
{}, async_opts)
if ok then
futures_by_replicas[replica_uuid] = res
futures_by_replicas[replica.uuid] = res
else
local err_msg = string.format("Error getting storage info for %s", replica_uuid)
local err_msg = string.format("Error getting storage info for %s", replica.uuid)
if res ~= nil then
log.error("%s: %s", err_msg, res)
replica_state_by_uuid[replica_uuid].message = tostring(res)
replica_state_by_uuid[replica.uuid].message = tostring(res)
else
log.error(err_msg)
replica_state_by_uuid[replica_uuid].message = err_msg
replica_state_by_uuid[replica.uuid].message = err_msg
end
end
end
Expand Down Expand Up @@ -1314,4 +1314,29 @@ function utils.is_cartridge_hotreload_supported()
return true, cartridge_hotreload
end

function utils.get_self_vshard_replicaset()
local box_info = box.info()

local ok, storage_info = pcall(vshard.storage.info)
assert(ok, 'vshard.storage.cfg() must be called first')

local replicaset_uuid
if box_info.replicaset ~= nil then
replicaset_uuid = box_info.replicaset.uuid
else
replicaset_uuid = box_info.cluster.uuid
end

local replicaset
-- Identification key may be name since vshard 0.1.25.
-- See also https://github.com/tarantool/vshard/issues/460.
for _, v in pairs(storage_info.replicasets) do
if v.uuid == replicaset_uuid then
replicaset = v
end
end

return replicaset_uuid, replicaset
end

return utils
6 changes: 3 additions & 3 deletions crud/readview.lua
Original file line number Diff line number Diff line change
Expand Up @@ -260,16 +260,16 @@ function Readview_obj:close(opts)

local errors = {}
for _, replicaset in pairs(replicasets) do
for replica_uuid, replica in pairs(replicaset.replicas) do
for _, replica in pairs(replicaset.replicas) do
for _, value in pairs(self._uuid) do
if replica_uuid == value.uuid then
if replica.uuid == value.uuid then
local replica_result, replica_err = replica.conn:call(CRUD_CLOSE_FUNC_NAME,
{self._uuid}, {timeout = opts.timeout})
if replica_err ~= nil then
table.insert(errors, ReadviewError:new("Failed to close Readview on storage: %s", replica_err))
end
if replica_err == nil and (not replica_result) then
table.insert(errors, ReadviewError:new("Readview was not found on storage: %s", replica_uuid))
table.insert(errors, ReadviewError:new("Readview was not found on storage: %s", replica.uuid))
end
end
end
Expand Down
3 changes: 3 additions & 0 deletions crud/update.lua
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ local function update_on_storage(space_name, key, operations, field_names, opts)
return res, nil
end

-- Relevant for Tarantool older than 2.8.1.
-- We can only add fields to end of the tuple.
-- If schema is updated and nullable fields are added, then we will get error.
-- Therefore, we need to add filling of intermediate nullable fields.
Expand All @@ -68,6 +69,8 @@ local function update_on_storage(space_name, key, operations, field_names, opts)
res, err = schema.wrap_box_space_func_result(space, 'update', {key, operations}, {
add_space_schema_hash = false,
field_names = field_names,
noreturn = opts.noreturn,
fetch_latest_metadata = opts.fetch_latest_metadata,
})
end

Expand Down
68 changes: 53 additions & 15 deletions test/helper.lua
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ end

function helpers.get_test_vshard_sharding()
local sharding = {
{
['s-1'] = {
replicas = {
['s1-master'] = {
instance_uuid = helpers.uuid('b', 1),
Expand All @@ -243,7 +243,7 @@ function helpers.get_test_vshard_sharding()
},
},
},
{
['s-2'] = {
replicas = {
['s2-master'] = {
instance_uuid = helpers.uuid('c', 1),
Expand Down Expand Up @@ -361,12 +361,12 @@ function helpers.get_other_storage_bucket_id(cluster, bucket_id)

local replicasets = vshard.router.routeall()

local other_replicaset_uuid
for replicaset_uuid, replicaset in pairs(replicasets) do
local other_replicaset
for _, replicaset in pairs(replicasets) do
local stat, err = replicaset:callrw('vshard.storage.bucket_stat', {bucket_id})

if err ~= nil and err.name == 'WRONG_BUCKET' then
other_replicaset_uuid = replicaset_uuid
other_replicaset = replicaset
break
end

Expand All @@ -378,13 +378,8 @@ function helpers.get_other_storage_bucket_id(cluster, bucket_id)
end
end

if other_replicaset_uuid == nil then
return nil, 'Other replicaset is not found'
end

local other_replicaset = replicasets[other_replicaset_uuid]
if other_replicaset == nil then
return nil, string.format('Replicaset %s not found', other_replicaset_uuid)
return nil, 'Other replicaset is not found'
end

local buckets_info = other_replicaset:callrw('vshard.storage.buckets_info')
Expand Down Expand Up @@ -734,7 +729,7 @@ function helpers.start_cluster(g, cartridge_cfg, vshard_cfg)
local cfg = table.deepcopy(vshard_cfg)
cfg.engine = g.params.engine

g.cfg = vtest.config_new(cfg)
g.cfg = vtest.config_new(cfg, g.params.backend_cfg)
vtest.cluster_new(g, g.cfg)
g.cfg.engine = nil
end
Expand All @@ -756,14 +751,57 @@ function helpers.get_router(cluster, backend)
end
end

function helpers.parse_module_version(str)
-- https://github.com/tarantool/luatest/blob/f37b353b77be50a1f1ce87c1ff2edf0c1b96d5d1/luatest/utils.lua#L166-L173
local splitstr = str:split('.')
local major = tonumber(splitstr[1]:match('%d+'))
local minor = tonumber(splitstr[2]:match('%d+'))
local patch = tonumber(splitstr[3]:match('%d+'))
return luatest_utils.version(major, minor, patch)
end

function helpers.is_name_supported_as_vshard_id()
local vshard_version = helpers.parse_module_version(require('vshard')._VERSION)
local is_vshard_supports = luatest_utils.version_ge(vshard_version,
luatest_utils.version(0, 1, 25))

local tarantool_version = luatest_utils.get_tarantool_version()
local is_tarantool_supports = luatest_utils.version_ge(tarantool_version,
luatest_utils.version(3, 0, 0))
return is_vshard_supports and is_tarantool_supports
end

function helpers.backend_matrix(base_matrix)
base_matrix = base_matrix or {{}}
local backends = {helpers.backend.VSHARD, helpers.backend.CARTRIDGE}
local backend_params = {
{
backend = helpers.backend.CARTRIDGE,
backend_cfg = nil,
},
}

if helpers.is_name_supported_as_vshard_id() then
table.insert(backend_params, {
backend = helpers.backend.VSHARD,
backend_cfg = {identification_mode = 'uuid_as_key'},
})
table.insert(backend_params, {
backend = helpers.backend.VSHARD,
backend_cfg = {identification_mode = 'name_as_key'},
})
else
table.insert(backend_params, {
backend = helpers.backend.VSHARD,
backend_cfg = nil,
})
end

local matrix = {}
for _, backend in ipairs(backends) do
for _, params in ipairs(backend_params) do
for _, base in ipairs(base_matrix) do
base = table.deepcopy(base)
base.backend = backend
base.backend = params.backend
base.backend_cfg = params.backend_cfg
table.insert(matrix, base)
end
end
Expand Down
Loading