From: Vladislav Shpilevoy via Tarantool-patches <tarantool-patches@dev.tarantool.org> To: tarantool-patches@dev.tarantool.org, olegrok@tarantool.org, yaroslav.dynnikov@tarantool.org Subject: [Tarantool-patches] [PATCH 2/2] vshard: fix buckets_count() on replicas Date: Thu, 13 May 2021 13:07:06 +0200 [thread overview] Message-ID: <04d7d05f09a5ee7ed52b27c480e81232c406e415.1620903962.git.v.shpilevoy@tarantool.org> (raw) In-Reply-To: <cover.1620903962.git.v.shpilevoy@tarantool.org> vshard.storage.buckets_count() uses a cached count value when no changes in _bucket space. The changes are detected using an on_replace trigger on _bucket space. But it wasn't installed on the replicas. As a result, on replica vshard.storage.buckets_count() didn't change at all after being called once. The patch makes replicas install the trigger on _bucket and update the bucket generation + drop the caches properly. The tricky part is that _bucket might not exist on a replica if it was vshard-configured earlier than the master. But for that occasion the trigger installation on _bucket is delayed until vshard schema is replicated from the master. An alternative was to introduce a special version of buckets_count() on replicas which does not use the cache at all. But the trigger is going to be useful in scope of #173 where it should drop bucket refs on the replica. Closes #276 Needed for #173 --- test/misc/reconfigure.result | 21 ++++--- test/misc/reconfigure.test.lua | 13 +++-- test/router/boot_replica_first.result | 9 ++- test/router/boot_replica_first.test.lua | 7 ++- test/storage/storage.result | 44 ++++++++++++++ test/storage/storage.test.lua | 19 ++++++ vshard/storage/init.lua | 78 ++++++++++++++++++++++--- 7 files changed, 164 insertions(+), 27 deletions(-) diff --git a/test/misc/reconfigure.result b/test/misc/reconfigure.result index 3b34841..a03ab5a 100644 --- a/test/misc/reconfigure.result +++ b/test/misc/reconfigure.result @@ -271,9 +271,9 @@ box.cfg.read_only --- - false ... -#box.space._bucket:on_replace() +assert(#box.space._bucket:on_replace() == 1) --- -- 1 +- true ... _ = test_run:switch('storage_2_a') --- @@ -305,10 +305,13 @@ box.cfg.read_only --- - true ... --- Should be zero on the slave node. Even though earlier the node was a master. -#box.space._bucket:on_replace() +-- +-- gh-276: replica should have triggers. This is important for proper update of +-- caches and in future for discarding refs in scope of gh-173. +-- +assert(#box.space._bucket:on_replace() == 1) --- -- 0 +- true ... _ = test_run:switch('storage_2_b') --- @@ -340,9 +343,9 @@ box.cfg.read_only --- - false ... -#box.space._bucket:on_replace() +assert(#box.space._bucket:on_replace() == 1) --- -- 1 +- true ... _ = test_run:switch('storage_3_a') --- @@ -373,9 +376,9 @@ box.cfg.read_only --- - false ... -#box.space._bucket:on_replace() +assert(#box.space._bucket:on_replace() == 1) --- -- 1 +- true ... _ = test_run:switch('router_1') --- diff --git a/test/misc/reconfigure.test.lua b/test/misc/reconfigure.test.lua index 348628c..61ea3c0 100644 --- a/test/misc/reconfigure.test.lua +++ b/test/misc/reconfigure.test.lua @@ -109,7 +109,7 @@ table.sort(uris) uris box.cfg.replication box.cfg.read_only -#box.space._bucket:on_replace() +assert(#box.space._bucket:on_replace() == 1) _ = test_run:switch('storage_2_a') info = vshard.storage.info() @@ -119,8 +119,11 @@ table.sort(uris) uris box.cfg.replication box.cfg.read_only --- Should be zero on the slave node. Even though earlier the node was a master. -#box.space._bucket:on_replace() +-- +-- gh-276: replica should have triggers. This is important for proper update of +-- caches and in future for discarding refs in scope of gh-173. +-- +assert(#box.space._bucket:on_replace() == 1) _ = test_run:switch('storage_2_b') info = vshard.storage.info() @@ -130,7 +133,7 @@ table.sort(uris) uris box.cfg.replication box.cfg.read_only -#box.space._bucket:on_replace() +assert(#box.space._bucket:on_replace() == 1) _ = test_run:switch('storage_3_a') info = vshard.storage.info() @@ -140,7 +143,7 @@ table.sort(uris) uris box.cfg.replication box.cfg.read_only -#box.space._bucket:on_replace() +assert(#box.space._bucket:on_replace() == 1) _ = test_run:switch('router_1') info = vshard.router.info() diff --git a/test/router/boot_replica_first.result b/test/router/boot_replica_first.result index 1705230..3c5a08c 100644 --- a/test/router/boot_replica_first.result +++ b/test/router/boot_replica_first.result @@ -76,10 +76,13 @@ vshard.storage.call(1, 'read', 'echo', {100}) | message: 'Cannot perform action with bucket 1, reason: Not found' | name: WRONG_BUCKET | ... --- Should not have triggers. -#box.space._bucket:on_replace() +-- +-- gh-276: should have triggers. This is important for proper update of caches +-- and in future for discarding refs in scope of gh-173. +-- +assert(#box.space._bucket:on_replace() == 1) | --- - | - 0 + | - true | ... test_run:switch('router') diff --git a/test/router/boot_replica_first.test.lua b/test/router/boot_replica_first.test.lua index 7b1b3fd..f973f2b 100644 --- a/test/router/boot_replica_first.test.lua +++ b/test/router/boot_replica_first.test.lua @@ -29,8 +29,11 @@ test_run:switch('box_1_b') test_run:wait_lsn('box_1_b', 'box_1_a') -- Fails, but gracefully. Because the bucket is not found here. vshard.storage.call(1, 'read', 'echo', {100}) --- Should not have triggers. -#box.space._bucket:on_replace() +-- +-- gh-276: should have triggers. This is important for proper update of caches +-- and in future for discarding refs in scope of gh-173. +-- +assert(#box.space._bucket:on_replace() == 1) test_run:switch('router') vshard.router.bootstrap() diff --git a/test/storage/storage.result b/test/storage/storage.result index d18b7f8..570d9c6 100644 --- a/test/storage/storage.result +++ b/test/storage/storage.result @@ -708,6 +708,24 @@ assert(vshard.storage.buckets_count() == 0) --- - true ... +test_run:wait_lsn('storage_1_b', 'storage_1_a') +--- +... +_ = test_run:switch('storage_1_b') +--- +... +-- +-- gh-276: bucket count cache should be properly updated on the replica nodes. +-- For that the replicas must also install on_replace trigger on _bucket space +-- to watch for changes. +-- +assert(vshard.storage.buckets_count() == 0) +--- +- true +... +_ = test_run:switch('storage_1_a') +--- +... vshard.storage.bucket_force_create(1, 5) --- - true @@ -716,6 +734,19 @@ assert(vshard.storage.buckets_count() == 5) --- - true ... +test_run:wait_lsn('storage_1_b', 'storage_1_a') +--- +... +_ = test_run:switch('storage_1_b') +--- +... +assert(vshard.storage.buckets_count() == 5) +--- +- true +... +_ = test_run:switch('storage_1_a') +--- +... vshard.storage.bucket_force_create(6, 5) --- - true @@ -724,9 +755,22 @@ assert(vshard.storage.buckets_count() == 10) --- - true ... +test_run:wait_lsn('storage_1_b', 'storage_1_a') +--- +... +_ = test_run:switch('storage_1_b') +--- +... +assert(vshard.storage.buckets_count() == 10) +--- +- true +... -- -- Bucket_generation_wait() registry function. -- +_ = test_run:switch('storage_1_a') +--- +... lstorage = require('vshard.registry').storage --- ... diff --git a/test/storage/storage.test.lua b/test/storage/storage.test.lua index 97558f6..494e2e8 100644 --- a/test/storage/storage.test.lua +++ b/test/storage/storage.test.lua @@ -201,14 +201,33 @@ for bid, _ in pairs(buckets) do vshard.storage.bucket_force_drop(bid) end _ = test_run:switch('storage_1_a') assert(vshard.storage.buckets_count() == 0) +test_run:wait_lsn('storage_1_b', 'storage_1_a') +_ = test_run:switch('storage_1_b') +-- +-- gh-276: bucket count cache should be properly updated on the replica nodes. +-- For that the replicas must also install on_replace trigger on _bucket space +-- to watch for changes. +-- +assert(vshard.storage.buckets_count() == 0) + +_ = test_run:switch('storage_1_a') vshard.storage.bucket_force_create(1, 5) assert(vshard.storage.buckets_count() == 5) +test_run:wait_lsn('storage_1_b', 'storage_1_a') +_ = test_run:switch('storage_1_b') +assert(vshard.storage.buckets_count() == 5) + +_ = test_run:switch('storage_1_a') vshard.storage.bucket_force_create(6, 5) assert(vshard.storage.buckets_count() == 10) +test_run:wait_lsn('storage_1_b', 'storage_1_a') +_ = test_run:switch('storage_1_b') +assert(vshard.storage.buckets_count() == 10) -- -- Bucket_generation_wait() registry function. -- +_ = test_run:switch('storage_1_a') lstorage = require('vshard.registry').storage ok, err = lstorage.bucket_generation_wait(-1) assert(not ok and err.message) diff --git a/vshard/storage/init.lua b/vshard/storage/init.lua index c4400f7..b1a20d6 100644 --- a/vshard/storage/init.lua +++ b/vshard/storage/init.lua @@ -115,6 +115,14 @@ if not M then -- replace the old function is to keep its reference. -- bucket_on_replace = nil, + -- + -- Reference to the function used as on_replace trigger on + -- _schema space. Saved explicitly by the same reason as + -- _bucket on_replace. + -- It is used by replicas to wait for schema bootstrap + -- because they might be configured earlier than the + -- master. + schema_on_replace = nil, -- Fast alternative to box.space._bucket:count(). But may be nil. Reset -- on each generation change. bucket_count_cache = nil, @@ -398,6 +406,62 @@ local function schema_version_make(ver) return setmetatable(ver, schema_version_mt) end +local function schema_install_triggers() + local _bucket = box.space._bucket + if M.bucket_on_replace then + local ok, err = pcall(_bucket.on_replace, _bucket, nil, + M.bucket_on_replace) + if not ok then + log.warn('Could not drop old trigger from '.. + '_bucket: %s', err) + end + end + _bucket:on_replace(bucket_generation_increment) + M.bucket_on_replace = bucket_generation_increment +end + +local function schema_install_on_replace(_, new) + -- Wait not just for _bucket to appear, but for the entire + -- schema. This might be important if the schema will ever + -- consist of more than just _bucket. + if new[1] ~= 'vshard_version' then + return + end + schema_install_triggers() + + local _schema = box.space._schema + local ok, err = pcall(_schema.on_replace, _schema, nil, M.schema_on_replace) + if not ok then + log.warn('Could not drop trigger from _schema inside of the '.. + 'trigger: %s', err) + end + M.schema_on_replace = nil + -- Drop the caches which might have been created while the + -- schema was being replicated. + bucket_generation_increment() +end + +-- +-- Install the triggers later when there is an actual schema to install them on. +-- On replicas it might happen that they are vshard-configured earlier than the +-- master and therefore don't have the schema right away. +-- +local function schema_install_triggers_delayed() + log.info('Could not find _bucket space to install triggers - delayed '.. + 'until the schema is replicated') + assert(not box.space._bucket) + local _schema = box.space._schema + if M.schema_on_replace then + local ok, err = pcall(_schema.on_replace, _schema, nil, + M.schema_on_replace) + if not ok then + log.warn('Could not drop trigger from _schema: %s', err) + end + end + _schema:on_replace(schema_install_on_replace) + M.schema_on_replace = schema_install_on_replace +end + -- VShard versioning works in 4 numbers: major, minor, patch, and -- a last helper number incremented on every schema change, if -- first 3 numbers stay not changed. That happens when users take @@ -2612,17 +2676,15 @@ local function storage_cfg(cfg, this_replica_uuid, is_reload) local uri = luri.parse(this_replica.uri) schema_upgrade(is_master, uri.login, uri.password) - if M.bucket_on_replace then - box.space._bucket:on_replace(nil, M.bucket_on_replace) - M.bucket_on_replace = nil - end - lref.cfg() - if is_master then - box.space._bucket:on_replace(bucket_generation_increment) - M.bucket_on_replace = bucket_generation_increment + if is_master or box.space._bucket then + schema_install_triggers() + else + schema_install_triggers_delayed() end + lref.cfg() lsched.cfg(vshard_cfg) + lreplicaset.rebind_replicasets(new_replicasets, M.replicasets) lreplicaset.outdate_replicasets(M.replicasets) M.replicasets = new_replicasets -- 2.24.3 (Apple Git-128)
next prev parent reply other threads:[~2021-05-13 11:08 UTC|newest] Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top 2021-05-13 11:07 [Tarantool-patches] [PATCH 0/2] vshard.storage.buckets_count() bug Vladislav Shpilevoy via Tarantool-patches 2021-05-13 11:07 ` [Tarantool-patches] [PATCH 1/2] test: fix test output on latest Tarantool Vladislav Shpilevoy via Tarantool-patches 2021-05-13 20:23 ` Oleg Babin via Tarantool-patches 2021-05-19 21:50 ` Yaroslav Dynnikov via Tarantool-patches 2021-05-21 19:26 ` Vladislav Shpilevoy via Tarantool-patches 2021-05-21 21:54 ` Yaroslav Dynnikov via Tarantool-patches 2021-05-13 11:07 ` Vladislav Shpilevoy via Tarantool-patches [this message] 2021-05-13 20:23 ` [Tarantool-patches] [PATCH 2/2] vshard: fix buckets_count() on replicas Oleg Babin via Tarantool-patches 2021-05-21 19:26 ` Vladislav Shpilevoy via Tarantool-patches 2021-05-24 6:57 ` Oleg Babin via Tarantool-patches 2021-05-19 21:51 ` Yaroslav Dynnikov via Tarantool-patches 2021-05-21 19:30 ` Vladislav Shpilevoy via Tarantool-patches 2021-05-13 20:23 ` [Tarantool-patches] [PATCH 0/2] vshard.storage.buckets_count() bug Oleg Babin via Tarantool-patches 2021-05-25 20:43 ` Vladislav Shpilevoy via Tarantool-patches
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=04d7d05f09a5ee7ed52b27c480e81232c406e415.1620903962.git.v.shpilevoy@tarantool.org \ --to=tarantool-patches@dev.tarantool.org \ --cc=olegrok@tarantool.org \ --cc=v.shpilevoy@tarantool.org \ --cc=yaroslav.dynnikov@tarantool.org \ --subject='Re: [Tarantool-patches] [PATCH 2/2] vshard: fix buckets_count() on replicas' \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox