11local errors = require (' errors' )
2+ local vshard = require (' vshard' )
23
34local call_cache = require (' crud.common.call_cache' )
45local dev_checks = require (' crud.common.dev_checks' )
56local utils = require (' crud.common.utils' )
67local sharding_utils = require (' crud.common.sharding.utils' )
7- local fiber_clock = require (' fiber' ). clock
8+ local fiber = require (' fiber' )
89local const = require (' crud.common.const' )
10+ local rebalance = require (' crud.common.rebalance' )
911
1012local BaseIterator = require (' crud.common.map_call_cases.base_iter' )
1113local BasePostprocessor = require (' crud.common.map_call_cases.base_postprocessor' )
@@ -14,14 +16,79 @@ local CallError = errors.new_class('CallError')
1416
1517local CALL_FUNC_NAME = ' call_on_storage'
1618local CRUD_CALL_FUNC_NAME = utils .get_storage_call (CALL_FUNC_NAME )
17-
19+ local CRUD_CALL_FIBER_NAME = CRUD_CALL_FUNC_NAME .. ' / '
1820
1921local call = {}
2022
21- local function call_on_storage (run_as_user , func_name , ...)
23+ local function bucket_unref_many (bucket_ids , mode )
24+ local all_ok = true
25+ local last_err = nil
26+ for _ , bucket_id in pairs (bucket_ids ) do
27+ local ok , err = vshard .storage .bucket_unref (bucket_id , mode )
28+ if not ok then
29+ all_ok = nil
30+ last_err = err
31+ end
32+ end
33+ return all_ok , last_err
34+ end
35+
36+ local function bucket_ref_many (bucket_ids , mode )
37+ local reffed = {}
38+ for _ , bucket_id in pairs (bucket_ids ) do
39+ local ok , err = vshard .storage .bucket_ref (bucket_id , mode )
40+ if not ok then
41+ bucket_unref_many (reffed , mode )
42+ return nil , err
43+ end
44+ table.insert (reffed , bucket_id )
45+ end
46+ return true , nil
47+ end
48+
49+ local function call_on_storage_safe (run_as_user , bucket_ids , mode , func_name , ...)
50+ fiber .name (CRUD_CALL_FIBER_NAME .. ' safe/' .. func_name )
51+
52+ local ok , ref_err = bucket_ref_many (bucket_ids , mode )
53+ if not ok then
54+ return nil , ref_err
55+ end
56+
57+ local res = {box .session .su (run_as_user , call_cache .func_name_to_func (func_name ), ... )}
58+
59+ ok , ref_err = bucket_unref_many (bucket_ids , mode )
60+ if not ok then
61+ return nil , ref_err
62+ end
63+
64+ return unpack (res , 1 , table.maxn (res ))
65+ end
66+
67+ local function call_on_storage_fast (run_as_user , _ , _ , func_name , ...)
68+ fiber .name (CRUD_CALL_FIBER_NAME .. ' fast/' .. func_name )
69+
2270 return box .session .su (run_as_user , call_cache .func_name_to_func (func_name ), ... )
2371end
2472
73+ local call_on_storage = rebalance .safe_mode and call_on_storage_safe or call_on_storage_fast
74+
75+ local function safe_mode_enable ()
76+ call_on_storage = call_on_storage_safe
77+
78+ for fb_id , fb in pairs (fiber .info ()) do
79+ if string.find (fb .name , CRUD_CALL_FIBER_NAME ) then
80+ fiber .kill (fb_id )
81+ end
82+ end
83+ end
84+
85+ local function safe_mode_disable ()
86+ call_on_storage = call_on_storage_fast
87+ end
88+
89+ rebalance .register_safe_mode_enable_hook (safe_mode_enable )
90+ rebalance .register_safe_mode_disable_hook (safe_mode_disable )
91+
2592call .storage_api = {[CALL_FUNC_NAME ] = call_on_storage }
2693
2794function call .get_vshard_call_name (mode , prefer_replica , balance )
@@ -82,8 +149,10 @@ local function wrap_vshard_err(vshard_router, err, func_name, replicaset_id, buc
82149 ))
83150end
84151
85- local function retry_call_with_master_discovery (replicaset , method , func_name , func_args , call_opts )
86- local func_args_ext = utils .append_array ({ box .session .effective_user (), func_name }, func_args )
152+ local function retry_call_with_master_discovery (vshard_router , replicaset ,
153+ method , func_name , func_args ,
154+ call_opts , mode , bucket_ids )
155+ local func_args_ext = utils .append_array ({ box .session .effective_user (), bucket_ids , mode , func_name }, func_args )
87156
88157 -- In case cluster was just bootstrapped with auto master discovery,
89158 -- replicaset may miss master.
@@ -93,7 +162,20 @@ local function retry_call_with_master_discovery(replicaset, method, func_name, f
93162 return resp , err
94163 end
95164
96- if err .name == ' MISSING_MASTER' and replicaset .locate_master ~= nil then
165+ -- This is a partial copy of error handling from vshard.router.router_call_impl()
166+ -- It is much simpler mostly because bucket_set() can't be accessed from outside vshard.
167+ if err .name == ' WRONG_BUCKET' or
168+ err .name == ' BUCKET_IS_LOCKED' or
169+ err .name == ' TRANSFER_IS_IN_PROGRESS' then
170+ vshard_router :_bucket_reset (err .bucket_id )
171+
172+ -- Substitute replicaset only for single bucket_id calls.
173+ if err .destination and vshard_router .replicasets [err .destination ] and # bucket_ids == 1 then
174+ replicaset = vshard_router .replicasets [err .destination ]
175+ else
176+ return nil , err
177+ end
178+ elseif err .name == ' MISSING_MASTER' and replicaset .locate_master ~= nil then
97179 replicaset :locate_master ()
98180 end
99181
@@ -145,10 +227,10 @@ function call.map(vshard_router, func_name, func_args, opts)
145227 request_timeout = opts .mode == ' read' and opts .request_timeout or nil ,
146228 }
147229 while iter :has_next () do
148- local args , replicaset , replicaset_id = iter :get ()
230+ local args , replicaset , replicaset_id , bucket_ids = iter :get ()
149231
150- local future , err = retry_call_with_master_discovery (replicaset , vshard_call_name ,
151- func_name , args , call_opts )
232+ local future , err = retry_call_with_master_discovery (vshard_router , replicaset , vshard_call_name ,
233+ func_name , args , call_opts , opts . mode , bucket_ids )
152234
153235 if err ~= nil then
154236 local result_info = {
@@ -170,9 +252,9 @@ function call.map(vshard_router, func_name, func_args, opts)
170252 futures_by_replicasets [replicaset_id ] = future
171253 end
172254
173- local deadline = fiber_clock () + timeout
255+ local deadline = fiber . clock () + timeout
174256 for replicaset_id , future in pairs (futures_by_replicasets ) do
175- local wait_timeout = deadline - fiber_clock ()
257+ local wait_timeout = deadline - fiber . clock ()
176258 if wait_timeout < 0 then
177259 wait_timeout = 0
178260 end
@@ -221,9 +303,9 @@ function call.single(vshard_router, bucket_id, func_name, func_args, opts)
221303 local timeout = opts .timeout or const .DEFAULT_VSHARD_CALL_TIMEOUT
222304 local request_timeout = opts .mode == ' read' and opts .request_timeout or nil
223305
224- local res , err = retry_call_with_master_discovery (replicaset , vshard_call_name ,
225- func_name , func_args , {timeout = timeout ,
226- request_timeout = request_timeout })
306+ local res , err = retry_call_with_master_discovery (vshard_router , replicaset , vshard_call_name ,
307+ func_name , func_args , {timeout = timeout , request_timeout = request_timeout },
308+ opts . mode , { bucket_id })
227309 if err ~= nil then
228310 return nil , wrap_vshard_err (vshard_router , err , func_name , nil , bucket_id )
229311 end
@@ -248,8 +330,9 @@ function call.any(vshard_router, func_name, func_args, opts)
248330 end
249331 local replicaset_id , replicaset = next (replicasets )
250332
251- local res , err = retry_call_with_master_discovery (replicaset , ' call' ,
252- func_name , func_args , {timeout = timeout })
333+ local res , err = retry_call_with_master_discovery (vshard_router , replicaset , ' call' ,
334+ func_name , func_args , {timeout = timeout },
335+ ' read' , {})
253336 if err ~= nil then
254337 return nil , wrap_vshard_err (vshard_router , err , func_name , replicaset_id )
255338 end
0 commit comments