Skip to content

router: make full map_callrw with split args#628

Open
mrForza wants to merge 5 commits into
tarantool:masterfrom
mrForza:mrforza/gh-559-full-map-call-rw-with-split-args
Open

router: make full map_callrw with split args#628
mrForza wants to merge 5 commits into
tarantool:masterfrom
mrForza:mrforza/gh-559-full-map-call-rw-with-split-args

Conversation

@mrForza

@mrForza mrForza commented Dec 15, 2025

Copy link
Copy Markdown
Contributor

This patch introduces a new way of map_callrw execution by which we can pass some arguments to all storages and split buckets' arguments to those storages that have at least one bucket of bucket_ids. To achieve this we introduce a new string option - mode to map_callrw api.

@TarantoolBot document

Title: vshard: mode option for router.map_callrw()

This string option regulates on which storages the user function will be executed via map_callrw. Possible values:

  1. full - the user function will be executed on all storages in
    cluster.
  2. partial - the user function will be executed on certain sotrages on
    which buckets from bucket_ids were found.

After that changes map_callrw works in 4 different scenarios depending on mode and bucket_ids options:

  1. <mode = 'full', bucket_ids = nil> - user function will be executed
    with args on all storages.
  2. <mode = 'full', bucket_ids = {[bid_1] = {b_arg_1}, ...}> - storages
    that have at least one bucket of bucket_ids will execute user
    function with args and additional buckets' arguments. Other storages
    will execute user function only with args.
  3. <mode = 'partial', bucket_ids = {bid_1, ...}> - user function will be
    executed with args on storages that have at least one bucket of
    bucket_ids.
  4. <mode = 'partial', bucket_id = {[bid_1] = {b_arg_1}, ...}> - the same
    as the 3rd scenario but buckets' arguments (b_arg_1, ..., b_arg_n) will
    be added as additional arguments to user function.

Also now map_callrw ends with error in cases of <mode = 'full', bucket_ids = {1, 2, ...}> and <mode = 'partial', bucket_ids = nil>.

Closes #559

@Gerold103 Gerold103 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks 🔥! I sure hope it will help the customers 😁.

Comment thread vshard/router/init.lua Outdated
Comment thread vshard/router/init.lua Outdated
Comment thread vshard/router/init.lua Outdated
Comment thread vshard/router/init.lua Outdated
Comment thread vshard/router/init.lua Outdated
@Gerold103

Copy link
Copy Markdown
Collaborator

The commit message doesn't mention the ticket. You need to write Closes #559 right before the TarantoolBot doc request.

@Gerold103 Gerold103 assigned mrForza and unassigned Gerold103 Dec 18, 2025
@mrForza mrForza force-pushed the mrforza/gh-559-full-map-call-rw-with-split-args branch from 1f4e9b4 to 11e76dc Compare December 22, 2025 12:37
@mrForza mrForza assigned Serpentian and unassigned Serpentian and mrForza Dec 23, 2025

@Serpentian Serpentian left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the patch! It looks great, but I'm afraid, it's a little bit broken now, let's fix and test the new mode with broken cache on the router

Comment thread test/router-luatest/map_callrw_test.lua
Comment thread vshard/router/init.lua Outdated
Comment thread vshard/router/init.lua Outdated
Comment thread vshard/router/init.lua Outdated
Comment thread vshard/router/init.lua Outdated
Comment thread vshard/router/init.lua
@Serpentian Serpentian assigned mrForza and unassigned Serpentian Dec 23, 2025
@mrForza mrForza force-pushed the mrforza/gh-559-full-map-call-rw-with-split-args branch 2 times, most recently from 1fa423e to afbe43e Compare December 26, 2025 10:41
@mrForza mrForza assigned Serpentian and unassigned mrForza Dec 26, 2025
@mrForza mrForza requested a review from Serpentian December 26, 2025 11:04
Comment thread vshard/router/init.lua Outdated
Comment thread vshard/router/init.lua
@Serpentian Serpentian assigned mrForza and unassigned Serpentian Dec 29, 2025
@mrForza mrForza force-pushed the mrforza/gh-559-full-map-call-rw-with-split-args branch 3 times, most recently from 4b2f73b to 2b8e8f1 Compare January 15, 2026 07:29
@mrForza mrForza assigned Serpentian and unassigned mrForza Jan 15, 2026
@mrForza mrForza requested a review from Serpentian January 15, 2026 07:42

@Serpentian Serpentian left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the patchset. Very good! Seems like the last comments from me

Comment thread vshard/router/init.lua Outdated
Comment thread vshard/router/init.lua Outdated
Comment thread vshard/router/init.lua Outdated
Comment thread test/router-luatest/map_callrw_test.lua
@Serpentian Serpentian assigned mrForza and unassigned Serpentian Jan 15, 2026
@mrForza mrForza force-pushed the mrforza/gh-559-full-map-call-rw-with-split-args branch from 2b8e8f1 to de86696 Compare January 16, 2026 08:48
@mrForza mrForza assigned Serpentian and unassigned mrForza Jan 16, 2026
@mrForza mrForza requested a review from Serpentian January 16, 2026 10:33

@Serpentian Serpentian left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you a lot! Clean work, IMHO. Hope, it'll help TDG make requests safer

Comment thread vshard/router/init.lua
@Serpentian Serpentian assigned Gerold103 and mrForza and unassigned Serpentian Jan 16, 2026
@mrForza mrForza force-pushed the mrforza/gh-559-full-map-call-rw-with-split-args branch from de86696 to edfeebc Compare January 19, 2026 09:21
@mrForza mrForza requested a review from Gerold103 January 19, 2026 10:04
@mrForza mrForza assigned Gerold103 and unassigned Gerold103 and mrForza Jan 19, 2026
Comment thread vshard/router/init.lua Outdated
Comment on lines +777 to +779
local function router_ref_storage_all(router, timeout, rid)
local function router_ref_storage_all(router, timeout, refed_replicasets, rid)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand. Why do you need "refed replicasets"? The stages are Ref + Map.

Ref works in 2 ways:

  • Ref all storages regardless of buckets. This was done previously by router_ref_storage_all() without any replicasets referenced in advance.
  • Or ref specific storages which have at least one bucket. For that we had router_ref_storage_by_buckets(). It was already doing all the checks to make sure that the referenced storage has the needed buckets.

This patch was only supposed to add additional arguments to these already existing ways of execution. Not change the way of referencing. It was previously "ref all or ref by buckets" and remains like this.

What changed here? What am I missing?

@mrForza mrForza Jan 21, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need "refed replicasets"?

We need refed_replicasets in router_ref_storage_all in order to check that storages which were referenced by router_ref_storage_by_buckets still have refs (in other words they are not expired). Also if we were just use router_ref_storage_by_buckets and then router_ref_storage_all without refed_replicasets, we would have to complicate the logic in this functions to avoid doubled refs for previously refed storages. See Nikita's comment related to this problem.


What changed here? What am I missing?

To show what has changed, I'll draw a table of map_callrw scenarios:

mode bucket_ids ref-functions which we use
partial {bid_1, ..., bid_n} router_ref_storage_by_buckets
partial {bid_1 = {bargs_1}, ..., bid_n = {bargs_n}} router_ref_storage_by_buckets
full nil router_ref_storage_all
full {bid_1 = {bargs_1}, ..., bid_n = {bargs_n}} router_ref_storage_by_buckets + router_ref_storage_all

Our main goal is to implement full map_callrw with split args with minimal cost. We don't need to introduce new ref-functions or new ways of referencing for it. We only want to minimally modify existing ref-functions and combine it in some way. If we want to have full map_callrw with split args we need 2 important things:

  1. ref all storages
  2. build a table of bucket arguments

router_ref_storage_by_buckets + router_ref_storage_all in minimally modified way can help us to achieve these 2 things. Also as you can see, for the 1-3 map_callrw scenarios we continue to use the old ref-functions. In my opinion this patch implements full map_callrw with split args without any serious complication of map_callrw business logic.

Comment thread vshard/router/init.lua Outdated
Comment on lines 1210 to 1216
if mode == MAP_CALLRW_FULL then
timeout, err, err_id, replicasets_to_map =
router_ref_storage_all(router, timeout, replicasets_to_map, rid)
if not timeout then
goto fail
end
end

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So now in the full mode with buckets we call router_ref_storage_by_buckets() and then router_ref_storage_all()? Why not call just router_ref_storage_all()? The effect would be the same - all storages receiving a ref.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, in full map_callrw with split args we must invoke router_ref_storage_by_buckets, because without using this function the router_group_map_callrw_args will build incorrect table of form {rs_id = {b_id = {b_args}}} as the router's cache may be broken. router_ref_storage_by_buckets finds the buckets in the cluster, updates the router's cache - router.route_map and refs storages which relate to given buckets ids whereas the router_ref_storage_all doesn't do that. See Nikita's comment related to this problem.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The possibility of the router's cache being broken is quite low. We just introduced one more network hop just for that.

Would it work to perhaps pass grouped bucket IDs to replicasets as arguments of router_ref_storage_all()? Like make this one call ref_storage_with buckets for storages where we expect specific buckets, and just "ref" for storages where we don't expect any specific buckets.

At least then we wouldn't have to do 2 network hops to each replicaset just to build/check the refs. Building refs must be possible with a single hop.

mrForza added 5 commits June 15, 2026 11:35
Before this patch the main `map_callrw` ref functions such as
`router_ref_storage_all` and `router_ref_storage_by_buckets` were
enormous (71 and 108 lines of code). Also these functions have a large
number of similar functional code blocks such as "sending refs",
"collecting refs" e.t.c. Since in tarantool#559 patch we will
extend the logic of full map_callrw making it able to work with split
args, the `router_ref_storage_all` can double in size. It can lead to
degradation of our codebase due to less readability.

To fix it we firstly determine general and repeated code blocks in ref
functions:
1) `ref-prepare`: groups buckets by replicasets with router's cache,
   builds a table of "target" replicasets and waits necessary masters.
2) `ref-send`: sends refs to the remote storage asynchronously and builds
   a table of future objects for the next processing.
3) `ref-collect`: waits until future objects are ready in order to
   extract payload from it (responses of storages' functions).
4) `ref-process`: a custom logic for `full` or `partial` map_callrw
   modes which describes how we should process results from future
   objects.

After defining the main stages of ref map_callrw functions we should
unify them so that we can use them in both `router_ref_storage_all` and
`router_ref_storage_by_buckets`.

Needed for tarantool#559

NO_TEST=refactoring
NO_DOC=refactoring
In this patch we change `allstatus.GARBAGE/SENT` on `BACTIVE/BSENT` to
not repeat the code.

Needed for tarantool#214

NO_DOC=refactoring
NO_TEST=refactoring
This patch takes initialization of `rid` out to `router_map_callrw` and
passes this variable to ref-functions. It is needed for future features
tidiness, for example - `make full map_callrw with split args` in which
the logic of `router_map_callrw` becomes more complex.

Needed for tarantool#559

NO_DOC=refactoring
NO_TEST=refactoring
Before this patch the `router-luatest/reload_test` checked router's
services only with old routers. However in future patch (tarantoolgh-214) we
need to check map_callrw with old storages.

In order to make it able we:
1) change `vtest.cluster_new` so that we can pass server_config with
   certain ENV (LUA_PATH) variable into it. It can help us to create
   a new cluster on old version of vshard.
2) change `reload_router` to more general `reload_server` in order to
   unify the process of servers (router / storage) upgrade in
   `router-luatest/reload_test`.
3) unify the process of cluster creation -
   `create_cluster_on_specific_version` and the process of getting
   server's config with new ENV (LUA_PATH) variable -
   `get_config_for_specific_vshard_version`.

Needed for tarantool#214

NO_DOC=refactoring
NO_TEST=refactoring
This patch introduces a new way of `map_callrw` execution by which we can
pass some arguments to all storages and split buckets' arguments to those
storages that have at least one bucket of `bucket_ids`. To achieve this we
introduce a new string option - `mode` to `map_callrw` api.

Also we change the logic of `router_ref_storage_all` ref function. Firstly
we ref all storages and get back an amount of "moved" buckets according
to the previously built router's cache. Then if there are no "moved"
buckets we accumulate and check total amount of buckets on all storages
and finish map_callrw ref stage. Otherwise, if there are some "moved"
buckets we perform the second network hop by checking on which replicasets
do the remaining "moved" buckets reside on.

Closes tarantool#559

@TarantoolBot document

Title: vshard: `mode` option for `router.map_callrw()`

This string option regulates on which storages the user function will be
executed via `map_callrw`. Possible values:
1) mode = 'partial'. In this mode user function will be executed on
   storages that have at least one bucket of 'bucket_ids'. The
   'bucket_ids' option can be presented in two ways: like a numeric array
   of buckets' ids or like a map of buckets' arguments. In first one user
   function will only receive args, in second one it will additionally
   receive buckets' arguments.
2) mode = 'full'. In this mode user function will be executed with args on
   all storages in cluster. Also we can pass 'bucket_ids' only like a map
   of bucket's arguments - `bucket_ids = {id = {args}, id = {args}, ...}`.
   In this way the user function will additionally receive buckets'
   arguments on those storages that have at least one bucket of
   `bucket_ids`.

If we didn't specify the 'mode' option, then it is set based on
'bucket_ids' option for backward compatibility. If 'bucket_ids' is
presented, the mode will be 'partial' otherwise 'full'.

Also now `map_callrw` ends with error in cases when we
1) pass `mode = 'full'` and a numeric array of buckets' ids -
   `bucket_ids = {id, id, ...}`
2) pass `mode = 'partial'` and `bucket_ids = nil`.
@mrForza mrForza force-pushed the mrforza/gh-559-full-map-call-rw-with-split-args branch from edfeebc to 512feab Compare June 15, 2026 08:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Full map_callrw with split args

3 participants