• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tarantool / crud / 21337227554

25 Jan 2026 06:13PM UTC coverage: 73.47% (-15.0%) from 88.463%
21337227554

Pull #477

github

ita-sammann
test: dump xlogs and snaps for debug
Pull Request #477: test: attempt to mitigate replication bug on cluster start

1 of 2 new or added lines in 1 file covered. (50.0%)

858 existing lines in 47 files now uncovered.

4251 of 5786 relevant lines covered (73.47%)

55.92 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

76.7
/crud/borders.lua
1
local checks = require('checks')
19✔
2
local errors = require('errors')
19✔
3

4
local const = require('crud.common.const')
19✔
5
local dev_checks = require('crud.common.dev_checks')
19✔
6
local call = require('crud.common.call')
19✔
7
local utils = require('crud.common.utils')
19✔
8
local schema = require('crud.common.schema')
19✔
9
local has_keydef, Keydef = pcall(require, 'crud.compare.keydef')
19✔
10
local select_comparators = require('crud.compare.comparators')
19✔
11

12
local BorderError = errors.new_class('BorderError', {capture_stack = false})
19✔
13

14
local borders = {}
19✔
15

16
local STAT_FUNC_NAME = 'get_border_on_storage'
19✔
17
local CRUD_STAT_FUNC_NAME = utils.get_storage_call(STAT_FUNC_NAME)
19✔
18

19

20
local function get_border_on_storage(border_name, space_name, index_id, field_names, fetch_latest_metadata)
21
    dev_checks('string', 'string', 'number', '?table', '?boolean')
8✔
22

23
    assert(border_name == 'min' or border_name == 'max')
8✔
24

25
    local space = box.space[space_name]
8✔
26
    if space == nil then
8✔
27
        return nil, BorderError:new("Space %q doesn't exist", space_name)
×
28
    end
29

30
    local index = space.index[index_id]
8✔
31
    if index == nil then
8✔
32
        return nil, BorderError:new("Index %q of space doesn't exist", index_id, space_name)
×
33
    end
34

35
    return schema.wrap_func_result(space, index[border_name], {
8✔
36
        add_space_schema_hash = true,
37
        field_names = field_names,
8✔
38
        fetch_latest_metadata = fetch_latest_metadata,
8✔
39
    }, index)
8✔
40
end
41

42
borders.storage_api = {[STAT_FUNC_NAME] = get_border_on_storage}
19✔
43

44
local is_closer
45

46
if has_keydef then
19✔
47
    is_closer = function (compare_sign, keydef, tuple, res_tuple)
48
        if res_tuple == nil then
8✔
49
            return true
4✔
50
        end
51

52
        local cmp = keydef:compare(tuple, res_tuple)
8✔
53

54
        return cmp * compare_sign > 0
4✔
55
    end
56
else
57
    is_closer = function (_, comparator, tuple, res_tuple)
58
        if res_tuple == nil then
×
59
            return true
×
60
        end
61
        return comparator(tuple, res_tuple)
×
62
    end
63
end
64

65
local function call_get_border_on_router(vshard_router, border_name, space_name, index_name, opts)
66
    checks('table', 'string', 'string', '?string|number', {
12✔
67
        timeout = '?number',
68
        request_timeout = '?number',
69
        fields = '?table',
70
        mode = '?string',
71
        vshard_router = '?string|table',
72
        fetch_latest_metadata = '?boolean',
73
    })
74

75
    local space, err, netbox_schema_version = utils.get_space(space_name, vshard_router, opts.timeout)
12✔
76
    if err ~= nil then
12✔
77
        return nil, BorderError:new("An error occurred during the operation: %s", err), const.NEED_SCHEMA_RELOAD
×
78
    end
79
    if space == nil then
12✔
80
        return nil, BorderError:new("Space %q doesn't exist", space_name), const.NEED_SCHEMA_RELOAD
16✔
81
    end
82

83
    local index
84
    if index_name == nil then
4✔
UNCOV
85
        index = space.index[0]
×
86
    else
87
        index = space.index[index_name]
4✔
88
    end
89

90
    if index == nil then
4✔
UNCOV
91
        return nil,
×
UNCOV
92
               BorderError:new("Index %q of space %q doesn't exist", index_name, space_name),
×
93
               const.NEED_SCHEMA_RELOAD
94
    end
95

96
    local primary_index = space.index[0]
4✔
97

98
    local cmp_key_parts = utils.merge_primary_key_parts(index.parts, primary_index.parts)
4✔
99
    local field_names = utils.enrich_field_names_with_cmp_key(opts.fields, cmp_key_parts, space:format())
8✔
100

101
    local replicasets, err = vshard_router:routeall()
4✔
102
    if err ~= nil then
4✔
103
        return nil, BorderError:new("Failed to get router replicasets: %s", err)
×
104
    end
105
    local mode = opts.mode or 'read'
4✔
106
    local call_opts = {
4✔
107
        mode = mode,
4✔
108
        replicasets = replicasets,
4✔
109
        timeout = opts.timeout,
4✔
110
        request_timeout = mode == 'read' and opts.request_timeout or nil,
4✔
111
    }
112
    local results, err, storages_info = call.map(vshard_router,
8✔
113
        CRUD_STAT_FUNC_NAME,
4✔
114
        {border_name, space_name, index.id, field_names, opts.fetch_latest_metadata},
4✔
115
        call_opts
116
    )
4✔
117

118
    if err ~= nil then
4✔
119
        return nil, BorderError:new("Failed to get %s: %s", border_name, err)
×
120
    end
121

122
    local compare_sign = border_name == 'max' and 1 or -1
4✔
123
    local comparator
124
    if has_keydef then
4✔
125
        comparator = Keydef.new(space, field_names, index.id)
8✔
126
    else
127
        local tarantool_iter
128
        if compare_sign > 0 then
×
129
            tarantool_iter = box.index.GT
×
130
        else
131
            tarantool_iter = box.index.LT
×
132
        end
133
        local key_parts = utils.merge_primary_key_parts(index.parts, primary_index.parts)
×
134
        local cmp_operator = select_comparators.get_cmp_operator(tarantool_iter)
×
135
        comparator = select_comparators.gen_tuples_comparator(cmp_operator, key_parts, field_names, space:format())
×
136
    end
137

138
    local res_tuple = nil
4✔
139
    for _, storage_result in pairs(results) do
16✔
140
        local storage_result = storage_result[1]
8✔
141
        if storage_result.err ~= nil then
8✔
142
            local err_wrapped = BorderError:new("Failed to get %s: %s", border_name, storage_result.err)
×
143

144
            local need_reload = schema.result_needs_reload(space, storage_result)
×
145
            if need_reload then
×
146
                return nil, err_wrapped, const.NEED_SCHEMA_RELOAD
×
147
            end
148

149
            return nil, err_wrapped
×
150
        end
151

152
        local tuple = storage_result.res
8✔
153
        if tuple ~= nil and is_closer(compare_sign, comparator, tuple, res_tuple) then
16✔
154
            res_tuple = tuple
6✔
155
        end
156
    end
157

158
    if opts.fetch_latest_metadata == true then
4✔
159
        -- This option is temporary and is related to [1], [2].
160
        -- [1] https://github.com/tarantool/crud/issues/236
161
        -- [2] https://github.com/tarantool/crud/issues/361
162
        space = utils.fetch_latest_metadata_when_map_storages(space, space_name, vshard_router, opts,
UNCOV
163
                                                              storages_info, netbox_schema_version)
×
164
    end
165

166
    local result = utils.format_result({res_tuple}, space, field_names)
4✔
167

168
    if opts.fields ~= nil then
4✔
UNCOV
169
        result = utils.cut_rows(result.rows, result.metadata, opts.fields)
×
170
    end
171

172
    return result
4✔
173
end
174

175
local function get_border(border_name, space_name, index_name, opts)
176
    opts = opts or {}
16✔
177
    local vshard_router, err = utils.get_vshard_router_instance(opts.vshard_router)
16✔
178
    if err ~= nil then
16✔
179
        return nil, BorderError:new(err)
16✔
180
    end
181

182
    return schema.wrap_func_reload(vshard_router, call_get_border_on_router,
8✔
183
        border_name, space_name, index_name, opts
8✔
184
    )
8✔
185
end
186

187
--- Find the minimum value in the specified index
188
--
189
-- @function min
190
--
191
-- @param string space_name
192
--  A space name
193
--
194
-- @param ?string index_name
195
--  An index name (by default, primary index is used)
196
--
197
-- @tparam ?number opts.timeout
198
--  Function call timeout
199
--
200
-- @tparam ?number opts.request_timeout
201
--  vshard call request_timeout
202
--  default is the same as opts.timeout
203
--
204
-- @tparam ?table opts.fields
205
--  Field names for getting only a subset of fields
206
--
207
-- @tparam ?string|table opts.vshard_router
208
--  Cartridge vshard group name or vshard router instance.
209
--  Set this parameter if your space is not a part of the
210
--  default vshard cluster.
211
--
212
-- @return[1] result
213
-- @treturn[2] nil
214
-- @treturn[2] table Error description
215
function borders.min(space_name, index_id, opts)
19✔
216
    return get_border('min', space_name, index_id, opts)
8✔
217
end
218

219
--- Find the maximum value in the specified index
220
--
221
-- @function min
222
--
223
-- @param string space_name
224
--  A space name
225
--
226
-- @param ?string index_name
227
--  An index name (by default, primary index is used)
228
--
229
-- @tparam ?number opts.timeout
230
--  Function call timeout
231
--
232
-- @tparam ?number opts.request_timeout
233
--  vshard call request_timeout
234
--  default is the same as opts.timeout
235
--
236
-- @tparam ?table opts.fields
237
--  Field names for getting only a subset of fields
238
--
239
-- @tparam ?string|table opts.vshard_router
240
--  Cartridge vshard group name or vshard router instance.
241
--  Set this parameter if your space is not a part of the
242
--  default vshard cluster.
243
--
244
-- @return[1] result
245
-- @treturn[2] nil
246
-- @treturn[2] table Error description
247
function borders.max(space_name, index_id, opts)
19✔
248
    return get_border('max', space_name, index_id, opts)
8✔
249
end
250

251
return borders
19✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc