Skip to content
This repository was archived by the owner on Apr 14, 2022. It is now read-only.

Commit db645f7

Browse files
committed
Add options to graphql.compile()
The options are: * resulting_object_cnt_max * fetched_object_cnt_max * timeout_ms Fixes #63.
1 parent 428e8ec commit db645f7

File tree

5 files changed

+144
-57
lines changed

5 files changed

+144
-57
lines changed

graphql/accessor_general.lua

Lines changed: 72 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -867,7 +867,7 @@ local function process_tuple(self, state, tuple, opts)
867867
if clock.monotonic64() > qcontext.deadline_clock then
868868
error(e.timeout_exceeded((
869869
'query execution timeout exceeded timeout_ms limit (%s ms)'):format(
870-
tostring(self.settings.timeout_ms))))
870+
tostring(state.qcontext.query_settings.timeout_ms))))
871871
end
872872
local collection_name = opts.collection_name
873873
local pcre = opts.pcre
@@ -1038,21 +1038,23 @@ local function select_internal(self, collection_name, from, filter, args, extra)
10381038
collection_name))
10391039

10401040
-- read-write variables for process_tuple
1041+
local qcontext = extra.qcontext
10411042
local select_state = {
10421043
count = 0,
10431044
objs = {},
10441045
pivot_found = false,
1045-
qcontext = extra.qcontext
1046+
qcontext = qcontext
10461047
}
10471048

10481049
-- read only process_tuple options
1050+
local query_settings = qcontext.query_settings
10491051
local select_opts = {
10501052
limit = args.limit,
10511053
filter = filter,
10521054
do_filter = not full_match,
10531055
pivot_filter = nil, -- filled later if needed
1054-
resulting_object_cnt_max = self.settings.resulting_object_cnt_max,
1055-
fetched_object_cnt_max = self.settings.fetched_object_cnt_max,
1056+
resulting_object_cnt_max = query_settings.resulting_object_cnt_max,
1057+
fetched_object_cnt_max = query_settings.fetched_object_cnt_max,
10561058
collection_name = collection_name,
10571059
unflatten_tuple = self.funcs.unflatten_tuple,
10581060
use_tomap = self.collection_use_tomap[collection_name] or false,
@@ -1079,10 +1081,10 @@ local function select_internal(self, collection_name, from, filter, args, extra)
10791081
collection_name)
10801082

10811083
-- count full scan select request
1082-
extra.qcontext.statistics.select_requests_cnt =
1083-
extra.qcontext.statistics.select_requests_cnt + 1
1084-
extra.qcontext.statistics.full_scan_select_requests_cnt =
1085-
extra.qcontext.statistics.full_scan_select_requests_cnt + 1
1084+
qcontext.statistics.select_requests_cnt =
1085+
qcontext.statistics.select_requests_cnt + 1
1086+
qcontext.statistics.full_scan_select_requests_cnt =
1087+
qcontext.statistics.full_scan_select_requests_cnt + 1
10861088

10871089
for _, tuple in primary_index:pairs() do
10881090
assert(pivot == nil,
@@ -1127,10 +1129,10 @@ local function select_internal(self, collection_name, from, filter, args, extra)
11271129
local tuple_count = 0
11281130

11291131
-- count index select request
1130-
extra.qcontext.statistics.select_requests_cnt =
1131-
extra.qcontext.statistics.select_requests_cnt + 1
1132-
extra.qcontext.statistics.index_select_requests_cnt =
1133-
extra.qcontext.statistics.index_select_requests_cnt + 1
1132+
qcontext.statistics.select_requests_cnt =
1133+
qcontext.statistics.select_requests_cnt + 1
1134+
qcontext.statistics.index_select_requests_cnt =
1135+
qcontext.statistics.index_select_requests_cnt + 1
11341136

11351137
for _, tuple in index:pairs(index_value, iterator_opts) do
11361138
tuple_count = tuple_count + 1
@@ -1307,23 +1309,58 @@ local function validate_funcs(funcs)
13071309
type(funcs.delete_tuple))
13081310
end
13091311

1312+
local function validate_query_settings(query_settings, opts)
1313+
local opts = opts or {}
1314+
local allow_nil = opts.allow_nil or false
1315+
1316+
local resulting_object_cnt_max = query_settings.resulting_object_cnt_max
1317+
local fetched_object_cnt_max = query_settings.fetched_object_cnt_max
1318+
local timeout_ms = query_settings.timeout_ms
1319+
1320+
if not allow_nil or type(resulting_object_cnt_max) ~= 'nil' then
1321+
assert(type(resulting_object_cnt_max) == 'number' and
1322+
resulting_object_cnt_max > 0,
1323+
'resulting_object_cnt_max must be natural number')
1324+
end
1325+
if not allow_nil or type(fetched_object_cnt_max) ~= 'nil' then
1326+
assert(type(fetched_object_cnt_max) == 'number' and
1327+
fetched_object_cnt_max > 0,
1328+
'fetched_object_cnt_max must be natural number')
1329+
end
1330+
if not allow_nil or type(timeout_ms) ~= 'nil' then
1331+
assert(type(timeout_ms) == 'number' or (type(timeout_ms) == 'cdata' and
1332+
tostring(ffi.typeof(timeout_ms)) == 'ctype<uint64_t>'),
1333+
'timeout_ms must a number, got ' .. type(timeout_ms))
1334+
assert(timeout_ms <= TIMEOUT_INFINITY,
1335+
('timeouts more then graphql.TIMEOUT_INFINITY (%s) ' ..
1336+
'do not supported'):format(tostring(TIMEOUT_INFINITY)))
1337+
end
1338+
end
1339+
13101340
--- This function is called on first select related to a query. Its purpose is
13111341
--- to initialize qcontext table.
13121342
--- @tparam table accessor
13131343
--- @tparam table qcontext per-query table which stores query internal state;
13141344
--- all neccessary initialization of this parameter should be performed by this
13151345
-- function
13161346
local function init_qcontext(accessor, qcontext)
1317-
local settings = accessor.settings
1347+
for k, v in pairs(accessor.query_settings_default) do
1348+
if qcontext.query_settings[k] == nil then
1349+
qcontext.query_settings[k] = v
1350+
end
1351+
end
1352+
validate_query_settings(qcontext.query_settings)
1353+
1354+
qcontext.deadline_clock = clock.monotonic64() +
1355+
qcontext.query_settings.timeout_ms * 1000 * 1000
1356+
13181357
qcontext.statistics = {
13191358
resulting_object_cnt = 0,
13201359
fetched_object_cnt = 0,
13211360
select_requests_cnt = 0,
13221361
full_scan_select_requests_cnt = 0,
13231362
index_select_requests_cnt = 0,
13241363
}
1325-
qcontext.deadline_clock = clock.monotonic64() +
1326-
settings.timeout_ms * 1000 * 1000
13271364
end
13281365

13291366
--- Create default unflatten/flatten/xflatten functions, that can be called
@@ -1474,12 +1511,24 @@ function accessor_general.new(opts, funcs)
14741511
local collections = opts.collections
14751512
local service_fields = opts.service_fields
14761513
local indexes = opts.indexes
1514+
1515+
check(schemas, 'schemas', 'table')
1516+
check(collections, 'collections', 'table')
1517+
check(service_fields, 'service_fields', 'table')
1518+
check(indexes, 'indexes', 'table')
1519+
14771520
local resulting_object_cnt_max = opts.resulting_object_cnt_max or
1478-
DEF_RESULTING_OBJECT_CNT_MAX
1521+
DEF_RESULTING_OBJECT_CNT_MAX
14791522
local fetched_object_cnt_max = opts.fetched_object_cnt_max or
1480-
DEF_FETCHED_OBJECT_CNT_MAX
1481-
-- TODO: move this setting to `tgql.compile` after #59
1523+
DEF_FETCHED_OBJECT_CNT_MAX
14821524
local timeout_ms = opts.timeout_ms or DEF_TIMEOUT_MS
1525+
local query_settings_default = {
1526+
resulting_object_cnt_max = resulting_object_cnt_max,
1527+
fetched_object_cnt_max = fetched_object_cnt_max,
1528+
timeout_ms = timeout_ms,
1529+
}
1530+
validate_query_settings(query_settings_default)
1531+
14831532
-- Mutations are disabled for avro-schema-2*, because it can work
14841533
-- incorrectly for schemas with nullable types.
14851534
local enable_mutations
@@ -1488,27 +1537,6 @@ function accessor_general.new(opts, funcs)
14881537
else
14891538
enable_mutations = opts.enable_mutations
14901539
end
1491-
1492-
assert(type(schemas) == 'table',
1493-
'schemas must be a table, got ' .. type(schemas))
1494-
assert(type(collections) == 'table',
1495-
'collections must be a table, got ' .. type(collections))
1496-
assert(type(service_fields) == 'table',
1497-
'service_fields must be a table, got ' .. type(service_fields))
1498-
assert(type(indexes) == 'table',
1499-
'indexes must be a table, got ' .. type(indexes))
1500-
assert(type(resulting_object_cnt_max) == 'number' and
1501-
resulting_object_cnt_max > 0,
1502-
'resulting_object_cnt_max must be natural number')
1503-
assert(type(fetched_object_cnt_max) == 'number' and
1504-
fetched_object_cnt_max > 0,
1505-
'fetched_object_cnt_max must be natural number')
1506-
assert(type(timeout_ms) == 'number' or (type(timeout_ms) == 'cdata' and
1507-
tostring(ffi.typeof(timeout_ms)) == 'ctype<uint64_t>'),
1508-
'timeout_ms must a number, got ' .. type(timeout_ms))
1509-
assert(timeout_ms <= TIMEOUT_INFINITY,
1510-
('timeouts more then graphql.TIMEOUT_INFINITY (%s) ' ..
1511-
'do not supported'):format(tostring(TIMEOUT_INFINITY)))
15121540
check(enable_mutations, 'enable_mutations', 'boolean')
15131541

15141542
local models, service_fields_defaults = compile_schemas(schemas,
@@ -1537,11 +1565,9 @@ function accessor_general.new(opts, funcs)
15371565
index_cache = index_cache,
15381566
funcs = funcs,
15391567
settings = {
1540-
resulting_object_cnt_max = resulting_object_cnt_max,
1541-
fetched_object_cnt_max = fetched_object_cnt_max,
1542-
timeout_ms = timeout_ms,
15431568
enable_mutations = enable_mutations,
1544-
}
1569+
},
1570+
query_settings_default = query_settings_default,
15451571
}, {
15461572
__index = {
15471573
select = function(self, parent, collection_name, from,
@@ -1576,4 +1602,7 @@ function accessor_general.new(opts, funcs)
15761602
})
15771603
end
15781604

1605+
-- export the function
1606+
accessor_general.validate_query_settings = validate_query_settings
1607+
15791608
return accessor_general

graphql/impl.lua

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
local accessor_space = require('graphql.accessor_space')
55
local accessor_shard = require('graphql.accessor_shard')
6+
local accessor_general = require('graphql.accessor_general')
67
local parse = require('graphql.core.parse')
78
local validate = require('graphql.core.validate')
89
local execute = require('graphql.core.execute')
@@ -39,8 +40,11 @@ local function gql_execute(qstate, variables, operation_name)
3940
check(variables, 'variables', 'table')
4041
check(operation_name, 'operation_name', 'string', 'nil')
4142

43+
assert(qstate.query_settings)
4244
local root_value = {}
43-
local qcontext = {}
45+
local qcontext = {
46+
query_settings = qstate.query_settings,
47+
}
4448

4549
local traceback
4650
local ok, data = xpcall(function()
@@ -67,15 +71,17 @@ end
6771
--- See @{gql_compile} and @{gql_execute} for parameters description.
6872
---
6973
--- @treturn table result of the operation
70-
local function compile_and_execute(state, query, variables, operation_name)
74+
local function compile_and_execute(state, query, variables, operation_name,
75+
opts)
7176
assert(type(state) == 'table', 'use :gql_execute(...) instead of ' ..
7277
'.execute(...)')
7378
assert(state.schema ~= nil, 'have not compiled schema')
7479
check(query, 'query', 'string')
7580
check(variables, 'variables', 'table', 'nil')
7681
check(operation_name, 'operation_name', 'string', 'nil')
82+
check(opts, 'opts', 'table', 'nil')
7783

78-
local compiled_query = state:compile(query)
84+
local compiled_query = state:compile(query, opts)
7985
return compiled_query:execute(variables, operation_name)
8086
end
8187

@@ -87,21 +93,39 @@ end
8793
---
8894
--- @tparam string query text of a GraphQL query
8995
---
96+
--- @tparam[opt] table opts the following options (described in
97+
--- @{accessor_general.new}):
98+
---
99+
--- * resulting_object_cnt_max
100+
--- * fetched_object_cnt_max
101+
--- * timeout_ms
102+
---
90103
--- @treturn table compiled query with `execute` and `avro_schema` functions
91-
local function gql_compile(state, query)
104+
local function gql_compile(state, query, opts)
92105
assert(type(state) == 'table' and type(query) == 'string',
93106
'use :validate(...) instead of .validate(...)')
94107
assert(state.schema ~= nil, 'have not compiled schema')
95108
check(query, 'query', 'string')
109+
check(opts, 'opts', 'table', 'nil')
110+
111+
local opts = opts or {}
96112

97113
local ast = parse(query)
98114
validate(state.schema, ast)
99115

100116
local qstate = {
101117
state = state,
102118
ast = ast,
119+
query_settings = {
120+
resulting_object_cnt_max = opts.resulting_object_cnt_max,
121+
fetched_object_cnt_max = opts.fetched_object_cnt_max,
122+
timeout_ms = opts.timeout_ms,
123+
}
103124
}
104125

126+
accessor_general.validate_query_settings(qstate.query_settings,
127+
{allow_nil = true})
128+
105129
local gql_query = setmetatable(qstate, {
106130
__index = {
107131
execute = gql_execute,

test/common/limit_result.test.lua

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,19 @@ local tap = require('tap')
1010
local test_utils = require('test.test_utils')
1111
local testdata = require('test.testdata.user_order_item_testdata')
1212
local graphql = require('graphql')
13+
local graphql_utils = require('graphql.utils')
14+
local test_run = graphql_utils.optional_require('test_run')
15+
test_run = test_run and test_run.new()
1316

1417
local e = graphql.error_codes
1518

19+
local apply_settings_to = test_run and test_run:get_cfg('apply_settings_to') or
20+
'graphql'
21+
local settings = {
22+
resulting_object_cnt_max = 3,
23+
fetched_object_cnt_max = 5,
24+
}
25+
1626
local function run_queries(gql_wrapper)
1727
local test = tap.test('result cnt')
1828
test:plan(2)
@@ -32,7 +42,8 @@ local function run_queries(gql_wrapper)
3242
}
3343
]]
3444

35-
local gql_query = gql_wrapper:compile(query)
45+
local query_opts = apply_settings_to == 'query' and settings or nil
46+
local gql_query = gql_wrapper:compile(query, query_opts)
3647
local variables = {
3748
user_id = 5,
3849
}
@@ -65,12 +76,10 @@ end
6576

6677
box.cfg({})
6778

79+
local graphql_opts = apply_settings_to == 'graphql' and settings or nil
6880
test_utils.run_testdata(testdata, {
6981
run_queries = run_queries,
70-
graphql_opts = {
71-
resulting_object_cnt_max = 3,
72-
fetched_object_cnt_max = 5,
73-
}
82+
graphql_opts = graphql_opts,
7483
})
7584

7685
os.exit()

test/common/query_timeout.test.lua

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,18 @@ local tap = require('tap')
1010
local test_utils = require('test.test_utils')
1111
local testdata = require('test.testdata.user_order_item_testdata')
1212
local graphql = require('graphql')
13+
local graphql_utils = require('graphql.utils')
14+
local test_run = graphql_utils.optional_require('test_run')
15+
test_run = test_run and test_run.new()
1316

1417
local e = graphql.error_codes
1518

19+
local apply_settings_to = test_run and test_run:get_cfg('apply_settings_to') or
20+
'graphql'
21+
local settings = {
22+
timeout_ms = 0.001,
23+
}
24+
1625
local function run_queries(gql_wrapper)
1726
local test = tap.test('result cnt')
1827
test:plan(1)
@@ -32,7 +41,8 @@ local function run_queries(gql_wrapper)
3241
}
3342
]]
3443

35-
local gql_query = gql_wrapper:compile(query)
44+
local query_opts = apply_settings_to == 'query' and settings or nil
45+
local gql_query = gql_wrapper:compile(query, query_opts)
3646
local variables = {}
3747
local result = gql_query:execute(variables)
3848
assert(result.data == nil, "this test should fail")
@@ -47,11 +57,10 @@ end
4757

4858
box.cfg({})
4959

60+
local graphql_opts = apply_settings_to == 'graphql' and settings or nil
5061
test_utils.run_testdata(testdata, {
5162
run_queries = run_queries,
52-
graphql_opts = {
53-
timeout_ms = 0.001,
54-
}
63+
graphql_opts = graphql_opts,
5564
})
5665

5766
os.exit()

test/common/suite.cfg

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,22 @@
11
{
2+
"query_timeout.test.lua": {
3+
"space (g)": {"conf": "space", "apply_settings_to": "graphql"},
4+
"shard_2x2 (g)": {"conf": "shard_2x2", "apply_settings_to": "graphql"},
5+
"shard_4x1 (g)": {"conf": "shard_4x1", "apply_settings_to": "graphql"},
6+
"space (q)": {"conf": "space", "apply_settings_to": "query"},
7+
"shard_2x2 (q)": {"conf": "shard_2x2", "apply_settings_to": "query"},
8+
"shard_4x1 (q)": {"conf": "shard_4x1", "apply_settings_to": "query"}
9+
},
10+
"limit_result.test.lua": {
11+
"space (g)": {"conf": "space", "apply_settings_to": "graphql"},
12+
"shard_2x2 (g)": {"conf": "shard_2x2", "apply_settings_to": "graphql"},
13+
"shard_4x1 (g)": {"conf": "shard_4x1", "apply_settings_to": "graphql"},
14+
"space (q)": {"conf": "space", "apply_settings_to": "query"},
15+
"shard_2x2 (q)": {"conf": "shard_2x2", "apply_settings_to": "query"},
16+
"shard_4x1 (q)": {"conf": "shard_4x1", "apply_settings_to": "query"}
17+
},
218
"*": {
3-
"space": {"conf": "space"},
19+
"space": {"conf": "space"},
420
"shard_2x2": {"conf": "shard_2x2"},
521
"shard_4x1": {"conf": "shard_4x1"}
622
}

0 commit comments

Comments
 (0)