Skip to content

Commit 82c9da6

Browse files
stats: add detailed statistics for select/pairs
After this patch, statistics `select` section additionally contains `details` collectors. ``` crud.stats('my_space').select.details --- - map_reduces: 4 tuples_fetched: 10500 tuples_lookup: 238000 ... ``` `map_reduces` is the count of planned map reduces (including those not executed successfully). `tuples_fetched` is the count of tuples fetched from storages during execution, `tuples_lookup` is the count of tuples looked up on storages while collecting responses for calls (including scrolls for multibatch requests). Details data is updated as part of the request process, so you may get new details before `select`/`pairs` call is finished and observed with count, latency and time collectors. Part of #224
1 parent e35d2fa commit 82c9da6

12 files changed

+408
-31
lines changed

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -753,6 +753,23 @@ returns. `count` is total requests count since instance start
753753
or stats restart. `latency` is average time of requests execution,
754754
`time` is the total time of requests execution.
755755

756+
`select` section additionally contains `details` collectors.
757+
```lua
758+
crud.stats('my_space').select.details
759+
---
760+
- map_reduces: 4
761+
tuples_fetched: 10500
762+
tuples_lookup: 238000
763+
...
764+
```
765+
`map_reduces` is the count of planned map reduces (including those not
766+
executed successfully). `tuples_fetched` is the count of tuples fetched
767+
from storages during execution, `tuples_lookup` is the count of tuples
768+
looked up on storages while collecting responses for calls (including
769+
scrolls for multibatch requests). Details data is updated as part of
770+
the request process, so you may get new details before `select`/`pairs`
771+
call is finished and observed with count, latency and time collectors.
772+
756773
Since `pairs` request behavior differs from any other crud request, its
757774
statistics collection also has specific behavior. Statistics (`select`
758775
section) are updated after `pairs` cycle is finished: you

crud/select.lua

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ local function select_on_storage(space_name, index_id, conditions, opts)
5959
end
6060

6161
-- execute select
62-
local tuples, err = select_executor.execute(space, index, filter_func, {
62+
local resp, err = select_executor.execute(space, index, filter_func, {
6363
scan_value = opts.scan_value,
6464
after_tuple = opts.after_tuple,
6565
tarantool_iter = opts.tarantool_iter,
@@ -70,15 +70,20 @@ local function select_on_storage(space_name, index_id, conditions, opts)
7070
end
7171

7272
local cursor
73-
if #tuples < opts.limit or opts.limit == 0 then
73+
if resp.tuples_fetched < opts.limit or opts.limit == 0 then
7474
cursor = {is_end = true}
7575
else
76-
cursor = make_cursor(tuples)
76+
cursor = make_cursor(resp.tuples)
7777
end
7878

79+
cursor.stats = {
80+
tuples_lookup = resp.tuples_lookup,
81+
tuples_fetched = resp.tuples_fetched,
82+
}
83+
7984
-- getting tuples with user defined fields (if `fields` option is specified)
8085
-- and fields that are needed for comparison on router (primary key + scan key)
81-
return cursor, schema.filter_tuples_fields(tuples, opts.field_names)
86+
return cursor, schema.filter_tuples_fields(resp.tuples, opts.field_names)
8287
end
8388

8489
function select_module.init()

crud/select/compat/select.lua

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ local dev_checks = require('crud.common.dev_checks')
88
local common = require('crud.select.compat.common')
99
local schema = require('crud.common.schema')
1010
local sharding_metadata_module = require('crud.common.sharding.sharding_metadata')
11+
local stats = require('crud.stats')
1112

1213
local compare_conditions = require('crud.compare.conditions')
1314
local select_plan = require('crud.compare.plan')
@@ -115,6 +116,8 @@ local function build_select_iterator(space_name, user_conditions, opts)
115116
if err ~= nil then
116117
return nil, err, true
117118
end
119+
else
120+
stats.update_map_reduces(space_name)
118121
end
119122

120123
local tuples_limit = opts.first

crud/select/compat/select_old.lua

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ local sharding = require('crud.common.sharding')
99
local dev_checks = require('crud.common.dev_checks')
1010
local schema = require('crud.common.schema')
1111
local sharding_metadata_module = require('crud.common.sharding.sharding_metadata')
12+
local stats = require('crud.stats')
1213

1314
local compare_conditions = require('crud.compare.conditions')
1415
local select_plan = require('crud.compare.plan')
@@ -59,6 +60,14 @@ local function select_iteration(space_name, plan, opts)
5960

6061
local tuples = {}
6162
for replicaset_uuid, replicaset_results in pairs(results) do
63+
-- Stats extracted with callback here and not passed
64+
-- outside to wrapper because fetch for pairs can be
65+
-- called even after pairs() return from generators.
66+
local cursor = replicaset_results[1]
67+
if cursor.stats ~= nil then
68+
stats.update_fetch_stats(cursor.stats, space_name)
69+
end
70+
6271
tuples[replicaset_uuid] = replicaset_results[2]
6372
end
6473

@@ -141,6 +150,8 @@ local function build_select_iterator(space_name, user_conditions, opts)
141150
if err ~= nil then
142151
return nil, err, true
143152
end
153+
else
154+
stats.update_map_reduces(space_name)
144155
end
145156

146157
-- generate tuples comparator

crud/select/executor.lua

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
local errors = require('errors')
2+
local fun = require('fun')
23

34
local dev_checks = require('crud.common.dev_checks')
45
local select_comparators = require('crud.compare.comparators')
@@ -68,13 +69,12 @@ function executor.execute(space, index, filter_func, opts)
6869

6970
opts = opts or {}
7071

72+
local resp = { tuples_fetched = 0, tuples_lookup = 0, tuples = {} }
73+
7174
if opts.limit == 0 then
72-
return {}
75+
return resp
7376
end
7477

75-
local tuples = {}
76-
local tuples_count = 0
77-
7878
local value = opts.scan_value
7979
if opts.after_tuple ~= nil then
8080
local new_value = generate_value(opts.after_tuple, opts.scan_value, index.parts, opts.tarantool_iter)
@@ -84,7 +84,16 @@ function executor.execute(space, index, filter_func, opts)
8484
end
8585

8686
local tuple
87-
local gen = index:pairs(value, {iterator = opts.tarantool_iter})
87+
local raw_gen, param, state = index:pairs(value, {iterator = opts.tarantool_iter})
88+
local gen = fun.wrap(function(param, state)
89+
local next_state, var = raw_gen(param, state)
90+
91+
if var ~= nil then
92+
resp.tuples_lookup = resp.tuples_lookup + 1
93+
end
94+
95+
return next_state, var
96+
end, param, state)
8897

8998
if opts.after_tuple ~= nil then
9099
local err
@@ -94,7 +103,7 @@ function executor.execute(space, index, filter_func, opts)
94103
end
95104

96105
if tuple == nil then
97-
return {}
106+
return resp
98107
end
99108
end
100109

@@ -110,10 +119,10 @@ function executor.execute(space, index, filter_func, opts)
110119
local matched, early_exit = filter_func(tuple)
111120

112121
if matched then
113-
table.insert(tuples, tuple)
114-
tuples_count = tuples_count + 1
122+
table.insert(resp.tuples, tuple)
123+
resp.tuples_fetched = resp.tuples_fetched + 1
115124

116-
if opts.limit ~= nil and tuples_count >= opts.limit then
125+
if opts.limit ~= nil and resp.tuples_fetched >= opts.limit then
117126
break
118127
end
119128
elseif early_exit then
@@ -123,7 +132,7 @@ function executor.execute(space, index, filter_func, opts)
123132
gen.state, tuple = gen(gen.param, gen.state)
124133
end
125134

126-
return tuples
135+
return resp
127136
end
128137

129138
return executor

crud/select/merger.lua

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ local compat = require('crud.common.compat')
77
local merger_lib = compat.require('tuple.merger', 'merger')
88

99
local Keydef = require('crud.compare.keydef')
10+
local stats = require('crud.stats')
1011

1112
local function bswap_u16(num)
1213
return bit.rshift(bit.bswap(tonumber(num)), 16)
@@ -93,6 +94,7 @@ local function fetch_chunk(context, state)
9394
local replicaset = context.replicaset
9495
local vshard_call_name = context.vshard_call_name
9596
local timeout = context.timeout or call.DEFAULT_VSHARD_CALL_TIMEOUT
97+
local space_name = context.space_name
9698
local future = state.future
9799

98100
-- The source was entirely drained.
@@ -109,6 +111,14 @@ local function fetch_chunk(context, state)
109111
-- Decode metainfo, leave data to be processed by the merger.
110112
local cursor = decode_metainfo(buf)
111113

114+
-- Extract stats info.
115+
-- Stats extracted with callback here and not passed
116+
-- outside to wrapper because fetch for pairs can be
117+
-- called even after pairs() return from generators.
118+
if cursor.stats ~= nil then
119+
stats.update_fetch_stats(cursor.stats, space_name)
120+
end
121+
112122
-- Check whether we need the next call.
113123
if cursor.is_end then
114124
local next_state = {}
@@ -157,6 +167,7 @@ local function new(replicasets, space, index_id, func_name, func_args, opts)
157167
replicaset = replicaset,
158168
vshard_call_name = vshard_call_name,
159169
timeout = call_opts.timeout,
170+
space_name = space.name,
160171
}
161172
local state = {future = future}
162173
local source = merger_lib.new_buffer_source(fetch_chunk, context, state)

crud/stats/init.lua

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,62 @@ function stats.wrap(func, op, opts)
255255
end
256256
end
257257

258+
local storage_stats_schema = { tuples_fetched = 'number', tuples_lookup = 'number' }
259+
--- Callback to collect storage tuples stats (select/pairs).
260+
--
261+
-- @function update_fetch_stats
262+
--
263+
-- @tab storage_stats
264+
-- Statistics from select storage call.
265+
--
266+
-- @number storage_stats.tuples_fetched
267+
-- Count of tuples fetched during storage call.
268+
--
269+
-- @number storage_stats.tuples_lookup
270+
-- Count of tuples looked up on storages while collecting response.
271+
--
272+
-- @string space_name
273+
-- Name of space.
274+
--
275+
-- @treturn boolean Returns `true`.
276+
--
277+
function stats.update_fetch_stats(storage_stats, space_name)
278+
dev_checks(storage_stats_schema, 'string')
279+
280+
if not stats.is_enabled() then
281+
return true
282+
end
283+
284+
registry.observe_fetch(
285+
storage_stats.tuples_fetched,
286+
storage_stats.tuples_lookup,
287+
space_name
288+
)
289+
290+
return true
291+
end
292+
293+
--- Callback to collect planned map reduces stats (select/pairs).
294+
--
295+
-- @function update_map_reduces
296+
--
297+
-- @string space_name
298+
-- Name of space.
299+
--
300+
-- @treturn boolean Returns `true`.
301+
--
302+
function stats.update_map_reduces(space_name)
303+
dev_checks('string')
304+
305+
if not stats.is_enabled() then
306+
return true
307+
end
308+
309+
registry.observe_map_reduces(1, space_name)
310+
311+
return true
312+
end
313+
258314
--- Table with CRUD operation lables.
259315
--
260316
-- @tfield string INSERT

crud/stats/local_registry.lua

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
local dev_checks = require('crud.common.dev_checks')
66
local stash = require('crud.common.stash')
7+
local op_module = require('crud.stats.operation')
78
local registry_utils = require('crud.stats.registry_utils')
89

910
local registry = {}
@@ -98,4 +99,56 @@ function registry.observe(latency, space_name, op, status)
9899
return true
99100
end
100101

102+
--- Increase statistics of storage select/pairs calls
103+
--
104+
-- @function observe_fetch
105+
--
106+
-- @string space_name
107+
-- Name of space.
108+
--
109+
-- @number tuples_fetched
110+
-- Count of tuples fetched during storage call.
111+
--
112+
-- @number tuples_lookup
113+
-- Count of tuples looked up on storages while collecting response.
114+
--
115+
-- @treturn boolean Returns true.
116+
--
117+
function registry.observe_fetch(tuples_fetched, tuples_lookup, space_name)
118+
dev_checks('number', 'number', 'string')
119+
120+
local op = op_module.SELECT
121+
registry_utils.init_collectors_if_required(internal.registry.spaces, space_name, op)
122+
local collectors = internal.registry.spaces[space_name][op].details
123+
124+
collectors.tuples_fetched = collectors.tuples_fetched + tuples_fetched
125+
collectors.tuples_lookup = collectors.tuples_lookup + tuples_lookup
126+
127+
return true
128+
end
129+
130+
--- Increase statistics of planned map reduces during select/pairs
131+
--
132+
-- @function observe_map_reduces
133+
--
134+
-- @number count
135+
-- Count of map reduces planned.
136+
--
137+
-- @string space_name
138+
-- Name of space.
139+
--
140+
-- @treturn boolean Returns true.
141+
--
142+
function registry.observe_map_reduces(count, space_name)
143+
dev_checks('number', 'string')
144+
145+
local op = op_module.SELECT
146+
registry_utils.init_collectors_if_required(internal.registry.spaces, space_name, op)
147+
local collectors = internal.registry.spaces[space_name][op].details
148+
149+
collectors.map_reduces = collectors.map_reduces + count
150+
151+
return true
152+
end
153+
101154
return registry

crud/stats/registry_utils.lua

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,25 @@
33
--
44

55
local dev_checks = require('crud.common.dev_checks')
6+
local op_module = require('crud.stats.operation')
67

78
local registry_utils = {}
89

910
--- Build collectors for local registry.
1011
--
1112
-- @function build_collectors
1213
--
14+
-- @string op
15+
-- Label of registry collectors.
16+
-- Use `require('crud.stats').op` to pick one.
17+
--
1318
-- @treturn table Returns collectors for success and error requests.
14-
-- Collectors store 'count', 'latency' and 'time' values.
19+
-- Collectors store 'count', 'latency' and 'time' values. Also
20+
-- returns additional collectors for select operation.
1521
--
16-
function registry_utils.build_collectors()
22+
function registry_utils.build_collectors(op)
23+
dev_checks('string')
24+
1725
local collectors = {
1826
ok = {
1927
count = 0,
@@ -27,6 +35,14 @@ function registry_utils.build_collectors()
2735
},
2836
}
2937

38+
if op == op_module.SELECT then
39+
collectors.details = {
40+
tuples_fetched = 0,
41+
tuples_lookup = 0,
42+
map_reduces = 0,
43+
}
44+
end
45+
3046
return collectors
3147
end
3248

@@ -53,7 +69,7 @@ function registry_utils.init_collectors_if_required(spaces, space_name, op)
5369

5470
local space_collectors = spaces[space_name]
5571
if space_collectors[op] == nil then
56-
space_collectors[op] = registry_utils.build_collectors()
72+
space_collectors[op] = registry_utils.build_collectors(op)
5773
end
5874
end
5975

0 commit comments

Comments
 (0)