Skip to content

Commit 7a4a64d

Browse files
committed
Add stats collection and pert tests for batch methods
Since we have PR #244 it will be nice to collect statistics for batch operations too. To establish the effectiveness of `crud.batch_insert()` method compared to `crud.insert()`, perf tests were added. `crud.insert()` in the loop and `crud.batch_insert()` are compared for different batch sizes. Closes #193
1 parent d3642ef commit 7a4a64d

File tree

4 files changed

+220
-9
lines changed

4 files changed

+220
-9
lines changed

crud.lua

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,11 @@ crud.insert_object = stats.wrap(insert.object, stats.op.INSERT)
3535

3636
-- @refer batch_insert.tuples_batch
3737
-- @function insert_many
38-
crud.insert_many = batch_insert.tuples_batch
38+
crud.insert_many = stats.wrap(batch_insert.tuples_batch, stats.op.INSERT_MANY)
3939

4040
-- @refer batch_insert.objects_batch
4141
-- @function insert_object_many
42-
crud.insert_object_many = batch_insert.objects_batch
42+
crud.insert_object_many = stats.wrap(batch_insert.objects_batch, stats.op.INSERT_MANY)
4343

4444
-- @refer get.call
4545
-- @function get
@@ -63,11 +63,11 @@ crud.upsert = stats.wrap(upsert.tuple, stats.op.UPSERT)
6363

6464
-- @refer batch_upsert.tuples_batch
6565
-- @function upsert_many
66-
crud.upsert_many = batch_upsert.tuples_batch
66+
crud.upsert_many = stats.wrap(batch_upsert.tuples_batch, stats.op.UPSERT_MANY)
6767

6868
-- @refer batch_upsert.objects_batch
6969
-- @function upsert_object_many
70-
crud.upsert_object_many = batch_upsert.objects_batch
70+
crud.upsert_object_many = stats.wrap(batch_upsert.objects_batch, stats.op.UPSERT_MANY)
7171

7272
-- @refer upsert.object
7373
-- @function upsert

crud/stats/operation.lua

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,16 @@
66
return {
77
-- INSERT identifies both `insert` and `insert_object`.
88
INSERT = 'insert',
9+
-- INSERT_MANY identifies both `insert_many` and `insert_object_many`.
10+
INSERT_MANY = 'insert_many',
911
GET = 'get',
1012
-- REPLACE identifies both `replace` and `replace_object`.
1113
REPLACE = 'replace',
1214
UPDATE = 'update',
1315
-- UPSERT identifies both `upsert` and `upsert_object`.
1416
UPSERT = 'upsert',
17+
-- UPSERT_MANY identifies both `upsert_many` and `upsert_object_many`.
18+
UPSERT_MANY = 'upsert_many',
1519
DELETE = 'delete',
1620
-- SELECT identifies both `pairs` and `select`.
1721
SELECT = 'select',

test/integration/stats_test.lua

Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,30 @@ local simple_operation_cases = {
171171
},
172172
op = 'insert',
173173
},
174+
{
175+
name = 'insert_many',
176+
func = 'crud.insert_many',
177+
args = {
178+
space_name,
179+
{
180+
{ 21, box.NULL, 'Ivan', 'Ivanov', 20, 'Moscow' },
181+
{ 31, box.NULL, 'Oleg', 'Petrov', 25, 'Moscow' },
182+
}
183+
},
184+
op = 'insert_many',
185+
},
186+
{
187+
name = 'insert_object_many',
188+
func = 'crud.insert_object_many',
189+
args = {
190+
space_name,
191+
{
192+
{ id = 22, name = 'Ivan', last_name = 'Ivanov', age = 20, city = 'Moscow' },
193+
{ id = 32, name = 'Oleg', last_name = 'Petrov', age = 25, city = 'Moscow' },
194+
}
195+
},
196+
op = 'insert_many',
197+
},
174198
{
175199
name = 'get',
176200
func = 'crud.get',
@@ -239,6 +263,38 @@ local simple_operation_cases = {
239263
},
240264
op = 'upsert',
241265
},
266+
{
267+
name = 'upsert_many',
268+
func = 'crud.upsert_many',
269+
args = {
270+
space_name,
271+
{
272+
{ 26, box.NULL, 'Ivan', 'Ivanov', 20, 'Moscow' },
273+
{ 36, box.NULL, 'Oleg', 'Petrov', 25, 'Moscow' },
274+
},
275+
{
276+
{{'+', 'age', 1}},
277+
{{'+', 'age', 1}},
278+
},
279+
},
280+
op = 'upsert_many',
281+
},
282+
{
283+
name = 'upsert_object_many',
284+
func = 'crud.upsert_object_many',
285+
args = {
286+
space_name,
287+
{
288+
{ id = 27, name = 'Ivan', last_name = 'Ivanov', age = 20, city = 'Moscow' },
289+
{ id = 37, name = 'Oleg', last_name = 'Petrov', age = 25, city = 'Moscow' },
290+
},
291+
{
292+
{{'+', 'age', 1}},
293+
{{'+', 'age', 1}}
294+
},
295+
},
296+
op = 'upsert_many',
297+
},
242298
{
243299
name = 'delete',
244300
func = 'crud.delete',
@@ -289,6 +345,20 @@ local simple_operation_cases = {
289345
op = 'insert',
290346
expect_error = true,
291347
},
348+
{
349+
name = 'insert_many_error',
350+
func = 'crud.insert_many',
351+
args = { space_name, {{ 'id' }} },
352+
op = 'insert_many',
353+
expect_error = true,
354+
},
355+
{
356+
name = 'insert_object_many_error',
357+
func = 'crud.insert_object_many',
358+
args = { space_name, {{ 'id' }} },
359+
op = 'insert_many',
360+
expect_error = true,
361+
},
292362
{
293363
name = 'get_error',
294364
func = 'crud.get',
@@ -346,6 +416,20 @@ local simple_operation_cases = {
346416
op = 'upsert',
347417
expect_error = true,
348418
},
419+
{
420+
name = 'upsert_many_error',
421+
func = 'crud.upsert_many',
422+
args = { space_name, {{ 'id' }}, {{{'+', 'age', 1}}} },
423+
op = 'upsert_many',
424+
expect_error = true,
425+
},
426+
{
427+
name = 'upsert_object_many_error',
428+
func = 'crud.upsert_object_many',
429+
args = { space_name, {{ 'id' }}, {{{'+', 'age', 1}}} },
430+
op = 'upsert_many',
431+
expect_error = true,
432+
},
349433
{
350434
name = 'delete_error',
351435
func = 'crud.delete',
@@ -807,8 +891,8 @@ local function validate_metrics(g, metrics)
807891
t.assert_type(stats_sum, 'table', '`tnt_crud_stats` summary metrics found')
808892

809893

810-
local expected_operations = { 'insert', 'batch_insert', 'get', 'replace', 'update',
811-
'upsert', 'batch_upsert', 'delete', 'select', 'truncate', 'len', 'count', 'borders' }
894+
local expected_operations = { 'insert', 'insert_many', 'get', 'replace', 'update',
895+
'upsert', 'upsert_many', 'delete', 'select', 'truncate', 'len', 'count', 'borders' }
812896

813897
if g.params.quantiles == true then
814898
t.assert_items_equals(get_unique_label_values(quantile_stats, 'operation'), expected_operations,

test/performance/perf_test.lua

Lines changed: 126 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ local fiber = require('fiber')
44
local errors = require('errors')
55
local net_box = require('net.box')
66
local log = require('log')
7+
local fun = require('fun')
78

89
local t = require('luatest')
910
local g = t.group('perf')
@@ -98,8 +99,32 @@ local function normalize(s, n)
9899
return (' '):rep(n - len) .. s
99100
end
100101

102+
local function generate_batch_insert_cases(min_value, denominator, count)
103+
local matrix = {}
104+
local cols_names = {}
105+
106+
local batch_size = min_value
107+
for _ = 1, count do
108+
local col_name = tostring(batch_size)
109+
table.insert(cols_names, col_name)
110+
matrix[col_name] = { column_name = col_name, arg = batch_size }
111+
batch_size = batch_size * denominator
112+
end
113+
114+
return {
115+
matrix = matrix,
116+
cols_names = cols_names,
117+
}
118+
end
119+
120+
local min_batch_size = 1
121+
local denominator_batch_size = 10
122+
local count_batch_cases = 5
123+
local batch_insert_cases = generate_batch_insert_cases(min_batch_size, denominator_batch_size, count_batch_cases)
124+
101125
local row_name = {
102126
insert = 'insert',
127+
insert_many = 'insert_many',
103128
select_pk = 'select by pk',
104129
select_gt_pk = 'select gt by pk (limit 10)',
105130
select_secondary_eq = 'select eq by secondary (limit 10)',
@@ -117,6 +142,12 @@ local column_name = {
117142
metrics_quantile_stats = 'crud (metrics stats, with quantiles)',
118143
}
119144

145+
-- insert column names for insert_many/insert comparison cases
146+
fun.reduce(
147+
function(list, value) list[value] = value return list end,
148+
column_name, pairs(batch_insert_cases.cols_names)
149+
)
150+
120151
local function visualize_section(total_report, name, comment, section, params)
121152
local report_str = ('== %s ==\n(%s)\n\n'):format(name, comment or '')
122153

@@ -242,6 +273,7 @@ g.after_all(function(g)
242273
row_name.select_secondary_eq,
243274
row_name.select_secondary_sharded,
244275
row_name.insert,
276+
row_name.insert_many,
245277
}
246278
})
247279

@@ -260,6 +292,15 @@ g.after_all(function(g)
260292
row_name.insert,
261293
}
262294
})
295+
296+
visualize_report(g.total_report, 'BATCH COMPARISON PERFORMANCE REPORT', {
297+
columns = batch_insert_cases.cols_names,
298+
299+
rows = {
300+
row_name.insert,
301+
row_name.insert_many,
302+
},
303+
})
263304
end)
264305

265306
local function generate_customer()
@@ -488,6 +529,18 @@ local insert_params = function()
488529
return { 'customers', generate_customer() }
489530
end
490531

532+
local batch_insert_params = function(count)
533+
local batch = {}
534+
535+
count = count or 1
536+
537+
for _ = 1, count do
538+
table.insert(batch, generate_customer())
539+
end
540+
541+
return { 'customers', batch }
542+
end
543+
491544
local select_params_pk_eq = function()
492545
return { 'customers', {{'==', 'id', gen() % 10000}} }
493546
end
@@ -606,6 +659,12 @@ local pairs_perf = {
606659
connection_count = 10,
607660
}
608661

662+
local batch_insert_comparison_perf = {
663+
timeout = 30,
664+
fiber_count = 1,
665+
connection_count = 1,
666+
}
667+
609668
local cases = {
610669
vshard_insert = {
611670
prepare = vshard_prepare,
@@ -640,6 +699,29 @@ local cases = {
640699
row_name = row_name.insert,
641700
},
642701

702+
crud_insert_many = {
703+
call = 'crud.insert_many',
704+
params = batch_insert_params,
705+
matrix = stats_cases,
706+
integration_params = integration_params,
707+
perf_params = insert_perf,
708+
row_name = row_name.insert_many,
709+
},
710+
711+
crud_insert_many_without_stats_wrapper = {
712+
prepare = function(g)
713+
g.router:eval([[
714+
rawset(_G, '_plain_insert_many', require('crud.batch_insert').tuples_batch)
715+
]])
716+
end,
717+
call = '_plain_insert_many',
718+
params = batch_insert_params,
719+
matrix = { [''] = { column_name = column_name.without_stats_wrapper } },
720+
integration_params = integration_params,
721+
perf_params = batch_insert_comparison_perf,
722+
row_name = row_name.insert_many,
723+
},
724+
643725
vshard_select_pk_eq = {
644726
prepare = function(g)
645727
select_prepare(g)
@@ -876,14 +958,55 @@ local cases = {
876958
perf_params = pairs_perf,
877959
row_name = row_name.pairs_gt,
878960
},
961+
962+
crud_insert_loop = {
963+
prepare = function(g)
964+
g.router:eval([[
965+
_insert_loop = function(space_name, tuples)
966+
local results
967+
local errors
968+
969+
for _, tuple in ipairs(tuples) do
970+
local res, err = crud.insert(space_name, tuple)
971+
if res ~= nil then
972+
results = results or {}
973+
table.insert(results, res)
974+
end
975+
976+
if err ~= nil then
977+
errors = errors or {}
978+
table.insert(errors, err)
979+
end
980+
end
981+
982+
return results, errors
983+
end
984+
]])
985+
end,
986+
call = '_insert_loop',
987+
params = batch_insert_params,
988+
matrix = batch_insert_cases.matrix,
989+
integration_params = integration_params,
990+
perf_params = batch_insert_comparison_perf,
991+
row_name = row_name.insert,
992+
},
993+
994+
crud_insert_many_different_batch_size = {
995+
call = 'crud.insert_many',
996+
params = batch_insert_params,
997+
matrix = batch_insert_cases.matrix,
998+
integration_params = integration_params,
999+
perf_params = batch_insert_comparison_perf,
1000+
row_name = row_name.insert_many,
1001+
},
8791002
}
8801003

881-
local function generator_f(conn, call, params, report, timeout)
1004+
local function generator_f(conn, call, params, report, timeout, arg)
8821005
local start = clock.monotonic()
8831006

8841007
while (clock.monotonic() - start) < timeout do
8851008
local call_start = clock.monotonic()
886-
local ok, res, err = pcall(conn.call, conn, call, params())
1009+
local ok, res, err = pcall(conn.call, conn, call, params(arg))
8871010
local call_time = clock.monotonic() - call_start
8881011

8891012
if not ok then
@@ -947,7 +1070,7 @@ for name, case in pairs(cases) do
9471070
for id = 1, params.fiber_count do
9481071
local conn_id = id % params.connection_count + 1
9491072
local conn = connections[conn_id]
950-
local f = fiber.new(generator_f, conn, case.call, case.params, report, params.timeout)
1073+
local f = fiber.new(generator_f, conn, case.call, case.params, report, params.timeout, subcase.arg)
9511074
f:set_joinable(true)
9521075
table.insert(fibers, f)
9531076
end

0 commit comments

Comments
 (0)