aboutsummaryrefslogtreecommitdiffstats
path: root/searchcore/src/tests/proton/documentdb/documentbucketmover/documentbucketmover_test.cpp
diff options
context:
space:
mode:
authorTor Brede Vekterli <vekterli@yahooinc.com>2022-05-12 11:02:48 +0000
committerTor Brede Vekterli <vekterli@yahooinc.com>2022-05-12 13:18:11 +0000
commit742d5d49f147ff652d78f5b8d270c81008d96283 (patch)
tree68eac9c736e1b20350de6b76a5a1392a84da56d9 /searchcore/src/tests/proton/documentdb/documentbucketmover/documentbucketmover_test.cpp
parent32e26201ca86681150eb47661ae551a1c188c594 (diff)
Don't attempt to actually execute document moves from a cancelled bucket mover
This prevents the following race condition where the bucket mover logic fails to notify the content layer that the bucket sub DB status has changed for a particular bucket: 1. Bucket state is changed over SPI, a mover is created and registered and a BucketTask is scheduled onto the persistence queues to actually do the document reads and finalize the move. 2. Before the bucket task is executed, bucket state is changed again over the SPI. A new mover is created, the old one is cancelled (tagging mover as not consistent) and another BucketTask is scheduled onto the persistence queues. Note: the old task still remains. 3. Old bucket task is executed and performs the actual document moving despite being cancelled. No notification is done towards the content layer since the mover was tagged as not being consistent. 4. New bucket task is executed and tries to move the same document set as the old mover. Since the documents are no longer present in the source document DB, the moves fail. This tags the mover as inconsistent and no notification is done. Bucket is automatically rechecked, but since all docs are already moved away there is nothing more to do and no subsequent mover is created. This means the "should notify?" edge is not triggered and the content layer remains blissfully unaware of any sub DB changes. This commit simply changes cancellation to actually inhibit document moves from taking place. This lets the preempting mover successfully complete its moves, thus triggering the notify-edge as expected.
Diffstat (limited to 'searchcore/src/tests/proton/documentdb/documentbucketmover/documentbucketmover_test.cpp')
-rw-r--r--searchcore/src/tests/proton/documentdb/documentbucketmover/documentbucketmover_test.cpp32
1 files changed, 31 insertions, 1 deletions
diff --git a/searchcore/src/tests/proton/documentdb/documentbucketmover/documentbucketmover_test.cpp b/searchcore/src/tests/proton/documentdb/documentbucketmover/documentbucketmover_test.cpp
index 9bc374b8386..2db34e45140 100644
--- a/searchcore/src/tests/proton/documentdb/documentbucketmover/documentbucketmover_test.cpp
+++ b/searchcore/src/tests/proton/documentdb/documentbucketmover/documentbucketmover_test.cpp
@@ -294,7 +294,7 @@ TEST_F(ControllerFixture, require_that_we_move_buckets_in_several_steps)
EXPECT_EQ(0, numPending());
EXPECT_EQ(7u, docsMoved().size());
EXPECT_EQ(3u, bucketsModified().size());
- EXPECT_EQ(_ready.bucket(2), bucketsModified()[0]);
+ ASSERT_EQ(_ready.bucket(2), bucketsModified()[0]);
EXPECT_EQ(_notReady.bucket(3), bucketsModified()[1]);
EXPECT_EQ(_notReady.bucket(4), bucketsModified()[2]);
}
@@ -481,6 +481,36 @@ TEST_F(ControllerFixture, explicitly_active_not_ready_bucket_can_be_moved_to_rea
EXPECT_EQ(_notReady.bucket(3), bucketsModified()[0]);
}
+TEST_F(ControllerFixture, bucket_change_notification_is_not_lost_with_concurrent_bucket_movers)
+{
+ addReady(_ready.bucket(1));
+ _bmj->recompute(); // Bucket 1 should be (and is) ready, bucket 2 is ready (but should not be).
+ _bucketExecutor.defer_new_tasks(); // Don't execute immediately, we need to force multiple pending moves
+ masterExecute([this]() {
+ deactivateBucket(_ready.bucket(2));
+ _bmj->scanAndMove(4, 3);
+ // New deactivation received from above prior to completion of scan. This can happen since
+ // moves are asynchronous and the distributor can send new (de-)activations before the old move is done.
+ // In our case, we've enforced that another move is already pending in the bucket executor.
+ deactivateBucket(_ready.bucket(2));
+ _bmj->scanAndMove(4, 3);
+ });
+ sync();
+ ASSERT_EQ(_bucketExecutor.num_deferred_tasks(), 2u);
+ _bucketExecutor.schedule_single_deferred_task();
+ sync();
+ // We have to fake that moving a document marks it as not found in the source sub DB.
+ // This doesn't automatically happen when using mocks. The most important part is that
+ // we ensure that moving isn't erroneously tested as if it were idempotent.
+ for (const auto& move : docsMoved()) {
+ failRetrieveForLid(move.getPrevLid());
+ }
+ _bucketExecutor.schedule_single_deferred_task();
+ sync();
+ EXPECT_TRUE(_bmj->done());
+ ASSERT_EQ(1u, bucketsModified().size());
+ EXPECT_EQ(_ready.bucket(2), bucketsModified()[0]);
+}
TEST_F(ControllerFixture, require_that_notifyCreateBucket_causes_bucket_to_be_reconsidered_by_job)
{