|
21 | 21 | #include "tests/mock/mock_kvstore.h" |
22 | 22 | #include "tests/mock/mock_stream.h" |
23 | 23 | #include "tests/mock/mock_synchronous_ep_engine.h" |
| 24 | +#include "tests/module_tests/vbucket_utils.h" |
| 25 | + |
24 | 26 | #include <kv_bucket.h> |
25 | 27 |
|
26 | 28 | using namespace ::testing; |
27 | 29 |
|
28 | 30 | /// Test fixture for DCPBackfillDisk class tests. |
29 | | -class DCPBackfillDiskTest : public SingleThreadedEPBucketTest {}; |
| 31 | +class DCPBackfillDiskTest : public SingleThreadedEPBucketTest { |
| 32 | +protected: |
| 33 | + void backfillGetDriver(IncludeValue incVal, |
| 34 | + IncludeXattrs incXattr, |
| 35 | + IncludeDeletedUserXattrs incDeletedXattr, |
| 36 | + int expectedGetCalls); |
| 37 | +}; |
30 | 38 |
|
31 | 39 | /** |
32 | 40 | * Regression test for MB-47790 - if a backfill fails during the scan() phase |
@@ -96,3 +104,88 @@ TEST_F(DCPBackfillDiskTest, ScanDiskError) { |
96 | 104 | // Replace the MockKVStore with the real one so we can tidy up correctly |
97 | 105 | MockKVStore::restoreOriginalRWKVStore(*store); |
98 | 106 | } |
| 107 | + |
| 108 | +void DCPBackfillDiskTest::backfillGetDriver( |
| 109 | + IncludeValue incVal, |
| 110 | + IncludeXattrs incXattr, |
| 111 | + IncludeDeletedUserXattrs incDeletedXattr, |
| 112 | + int expectedGetCalls) { |
| 113 | + setVBucketStateAndRunPersistTask(vbid, vbucket_state_active); |
| 114 | + // Create item, checkpoint and flush so item is to be backfilled from disk |
| 115 | + store_item(vbid, makeStoredDocKey("key"), "value"); |
| 116 | + flushAndRemoveCheckpoints(vbid); |
| 117 | + |
| 118 | + // Set up and define expectations for the hook that is called in the body of |
| 119 | + // CacheCallback::get(), i.e., when an item's value is retrieved from cache |
| 120 | + auto& vb = *engine->getKVBucket()->getVBucket(vbid); |
| 121 | + testing::StrictMock<testing::MockFunction<void()>> getInternalHook; |
| 122 | + VBucketTestIntrospector::setIsCalledHook(vb, |
| 123 | + getInternalHook.AsStdFunction()); |
| 124 | + EXPECT_CALL(getInternalHook, Call()).Times(expectedGetCalls); |
| 125 | + |
| 126 | + // Items now only on disk, create producer |
| 127 | + auto producer = std::make_shared<MockDcpProducer>( |
| 128 | + *engine, cookie, "test-producer", 0 /*flags*/, false /*startTask*/); |
| 129 | + |
| 130 | + auto stream = std::make_shared<MockActiveStream>( |
| 131 | + engine.get(), |
| 132 | + producer, |
| 133 | + 0, /* flags */ |
| 134 | + 0, /* opaque */ |
| 135 | + vb, /* vbucket */ |
| 136 | + 0, /* start seqNo */ |
| 137 | + 1, /* end seqNo */ |
| 138 | + 0, /* vbucket uuid */ |
| 139 | + 0, /* snapshot start seqNo */ |
| 140 | + 0, /* snapshot end seqNo */ |
| 141 | + incVal, /* includeValue */ |
| 142 | + incXattr, /* includeXattrs */ |
| 143 | + incDeletedXattr, /* includeDeletedUserXattrs */ |
| 144 | + std::string{}); /* jsonFilter */ |
| 145 | + |
| 146 | + stream->setActive(); |
| 147 | + ASSERT_TRUE(stream->isBackfilling()); |
| 148 | + |
| 149 | + auto& bfm = producer->getBFM(); |
| 150 | + ASSERT_EQ(backfill_success, bfm.backfill()); // initialize backfill |
| 151 | + |
| 152 | + auto backfillRemaining = stream->getNumBackfillItemsRemaining(); |
| 153 | + ASSERT_TRUE(backfillRemaining); |
| 154 | + |
| 155 | + ASSERT_EQ(backfill_success, bfm.backfill()); // scan |
| 156 | + ASSERT_EQ(2, stream->public_readyQSize()); |
| 157 | + |
| 158 | + auto response = stream->public_popFromReadyQ(); |
| 159 | + EXPECT_EQ(DcpResponse::Event::SnapshotMarker, response->getEvent()); |
| 160 | + |
| 161 | + // Ensure an item mutation is in the DCP stream |
| 162 | + response = stream->public_popFromReadyQ(); |
| 163 | + EXPECT_EQ(DcpResponse::Event::Mutation, response->getEvent()); |
| 164 | + |
| 165 | + // Ensure this item has the correct key, and value (if IncludeValue::Yes) |
| 166 | + MutationResponse mutResponse = dynamic_cast<MutationResponse&>(*response); |
| 167 | + SingleThreadedRCPtr item = mutResponse.getItem(); |
| 168 | + EXPECT_EQ(item->getKey(), makeStoredDocKey("key")); |
| 169 | + |
| 170 | + if (!(stream->isKeyOnly())) { |
| 171 | + EXPECT_EQ(item->getValue()->to_s(), "value"); |
| 172 | + } |
| 173 | +} |
| 174 | + |
| 175 | +// Tests that CacheCallback::get is never called when a stream is keyOnly. |
| 176 | +TEST_F(DCPBackfillDiskTest, KeyOnlyBackfillSkipsScan) { |
| 177 | + DCPBackfillDiskTest::backfillGetDriver(IncludeValue::No, |
| 178 | + IncludeXattrs::No, |
| 179 | + IncludeDeletedUserXattrs::No, |
| 180 | + 0); |
| 181 | +} |
| 182 | + |
| 183 | +// Complement to KeyOnlyBackfillSkipGet. Other tests already cover all cases, |
| 184 | +// but not using the hook. This test validates the hook, and thus |
| 185 | +// KeyOnlyBackfillSkipGet itself, is performing correctly and can be trusted. |
| 186 | +TEST_F(DCPBackfillDiskTest, ValueBackfillRegressionTest) { |
| 187 | + DCPBackfillDiskTest::backfillGetDriver(IncludeValue::Yes, |
| 188 | + IncludeXattrs::Yes, |
| 189 | + IncludeDeletedUserXattrs::Yes, |
| 190 | + 1); |
| 191 | +} |
0 commit comments