Skip to content

Commit 0573550

Browse files
committed
MB-34262: Replicate ephemeral tombstone times
1) Ephemeral backfill wasn't copying the tombstone time from the OSV deleted time field into the outgoing Item, this DCP delete v2 sent 0 or the expiry time to the client. This is corrected in the memory backfill so that the time is now passed to the Item that DCP will use to build the outbound message. 2) The ephemeral DCP consumer path wasn't doing the reverse of 1 (same for DelWithMeta). That is when the replicated delete is pushed into the HT/seqno linked list, we didn't copy through any delete time, a new delete time was always being generated. Change-Id: I7457acd699766a1a029d636663f50aebb479d934 Reviewed-on: http://review.couchbase.org/109560 Well-Formed: Build Bot <[email protected]> Reviewed-by: Dave Rigby <[email protected]> Tested-by: Build Bot <[email protected]>
1 parent ea8dcdd commit 0573550

File tree

5 files changed

+116
-53
lines changed

5 files changed

+116
-53
lines changed

engines/ep/src/dcp/backfill_memory.cc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "dcp/backfill_memory.h"
2121
#include "dcp/stream.h"
2222
#include "ep_engine.h"
23+
#include "ep_time.h"
2324
#include "ephemeral_vb.h"
2425
#include "seqlist.h"
2526

@@ -294,6 +295,12 @@ backfill_status_t DCPBackfillMemoryBuffered::scan() {
294295
// before calling StoredValue::toItem
295296
auto hbl = evb->ht.getLockedBucket((*rangeItr).getKey());
296297
item = (*rangeItr).toItem(false, getVBucketId());
298+
// A deleted ephemeral item stores the delete time under a delete
299+
// time field, this must be copied to the expiry time so that DCP
300+
// can transmit the original time of deletion
301+
if (item->isDeleted()) {
302+
item->setExpTime(ep_abs_time((*rangeItr).getDeletedTime()));
303+
}
297304
} catch (const std::bad_alloc&) {
298305
stream->log(EXTENSION_LOG_WARNING,
299306
"Alloc error when trying to create an "

engines/ep/src/ephemeral_vb.cc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#include "checkpoint.h"
2121
#include "dcp/backfill_memory.h"
22+
#include "ep_time.h"
2223
#include "ephemeral_tombstone_purger.h"
2324
#include "executorpool.h"
2425
#include "failover-table.h"
@@ -531,6 +532,16 @@ std::tuple<StoredValue*, VBNotifyCtx> EphemeralVBucket::softDeleteStoredValue(
531532
newSv->setBySeqno(bySeqno);
532533
}
533534

535+
// Replica/DelWithMeta can dictate the tombstone time, check for it.
536+
if (queueItmCtx.generateDeleteTime == GenerateDeleteTime::No &&
537+
newSv->isDeleted() && newSv->getExptime()) {
538+
// The deleted time is relative and the replicated tombstone time is
539+
// absolute and held in the expiry field, convert the abs to rel
540+
// using ep_reltime
541+
newSv->toOrderedStoredValue()->setDeletedTime(
542+
ep_reltime(newSv->getExptime(), {}));
543+
}
544+
534545
notifyCtx = queueDirty(*newSv, queueItmCtx);
535546

536547
/* Update the high seqno in the sequential storage */

engines/ep/src/stored-value.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -958,6 +958,11 @@ class OrderedStoredValue : public StoredValue {
958958
*/
959959
rel_time_t getDeletedTime() const;
960960

961+
/**
962+
* Set the time the item was deleted to the specified time.
963+
*/
964+
void setDeletedTime(rel_time_t time);
965+
961966
protected:
962967
SerialisedDocKey* key() {
963968
return reinterpret_cast<SerialisedDocKey*>(this + 1);
@@ -976,11 +981,6 @@ class OrderedStoredValue : public StoredValue {
976981
*/
977982
void setValueImpl(const Item& itm);
978983

979-
/**
980-
* Set the time the item was deleted to the specified time.
981-
*/
982-
inline void setDeletedTime(rel_time_t time);
983-
984984
private:
985985
// Constructor. Private, as needs to be carefully created via
986986
// OrderedStoredValueFactory.

engines/ep/tests/module_tests/evp_store_single_threaded_test.cc

Lines changed: 89 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -228,8 +228,10 @@ void SingleThreadedKVBucketTest::notifyAndStepToCheckpoint(
228228
runNextTask(lpAuxioQ);
229229
// backfill:complete()
230230
runNextTask(lpAuxioQ);
231-
// backfill:finished()
232-
runNextTask(lpAuxioQ);
231+
if (engine->getConfiguration().getBucketType() == "persistent") {
232+
// backfill:finished()
233+
runNextTask(lpAuxioQ);
234+
}
233235
}
234236

235237
// Next step which will process a snapshot marker and then the caller
@@ -1768,16 +1770,18 @@ TEST_F(SingleThreadedEPBucketTest, MB_29861) {
17681770
}
17691771

17701772
/*
1771-
* Test that the DCP processor returns a 'yield' return code when
1772-
* working on a large enough buffer size.
1773+
* Test that the consumer will use the delete time given
17731774
*/
1774-
TEST_F(SingleThreadedEPBucketTest, MB_27457) {
1775+
TEST_P(STParameterizedBucketTest, MB_27457) {
17751776
// We need a replica VB
17761777
setVBucketStateAndRunPersistTask(vbid, vbucket_state_replica);
17771778

17781779
// Create a MockDcpConsumer
17791780
auto consumer = std::make_shared<MockDcpConsumer>(*engine, cookie, "test");
17801781

1782+
// Bump forwards so ep_current_time cannot be 0
1783+
TimeTraveller biff(64000);
1784+
17811785
// Add the stream
17821786
EXPECT_EQ(ENGINE_SUCCESS,
17831787
consumer->addStream(/*opaque*/ 0, vbid, /*flags*/ 0));
@@ -1794,26 +1798,25 @@ TEST_F(SingleThreadedEPBucketTest, MB_27457) {
17941798
/*values*/ {},
17951799
/*priv_bytes*/ 0,
17961800
/*datatype*/ PROTOCOL_BINARY_RAW_BYTES,
1797-
/*cas*/ 0,
1801+
/*cas*/ 1,
17981802
/*vbucket*/ vbid,
17991803
/*bySeqno*/ 1,
18001804
/*revSeqno*/ 0,
18011805
/*deleteTime*/ 0);
18021806

1803-
const uint32_t deleteTime = 10;
1807+
const uint32_t deleteTime = 1958601165;
18041808
consumer->deletionV2(/*opaque*/ 1,
18051809
{"key2", DocNamespace::DefaultCollection},
18061810
/*value*/ {},
18071811
/*priv_bytes*/ 0,
18081812
/*datatype*/ PROTOCOL_BINARY_RAW_BYTES,
1809-
/*cas*/ 0,
1813+
/*cas*/ 2,
18101814
/*vbucket*/ vbid,
18111815
/*bySeqno*/ 2,
18121816
/*revSeqno*/ 0,
18131817
deleteTime);
18141818

1815-
EXPECT_EQ(std::make_pair(false, size_t(2)),
1816-
getEPBucket().flushVBucket(vbid));
1819+
flushVBucketToDiskIfPersistent(vbid, 2);
18171820

18181821
// Drop the stream
18191822
consumer->closeStream(/*opaque*/ 0, vbid);
@@ -1823,48 +1826,85 @@ TEST_F(SingleThreadedEPBucketTest, MB_27457) {
18231826
ItemMetaData metadata;
18241827
uint32_t deleted = 0;
18251828
uint8_t datatype = 0;
1826-
EXPECT_EQ(ENGINE_EWOULDBLOCK,
1827-
store->getMetaData(makeStoredDocKey("key1"),
1828-
vbid,
1829-
cookie,
1830-
metadata,
1831-
deleted,
1832-
datatype));
1829+
uint64_t tombstoneTime;
1830+
if (persistent()) {
1831+
EXPECT_EQ(ENGINE_EWOULDBLOCK,
1832+
store->getMetaData(makeStoredDocKey("key1"),
1833+
vbid,
1834+
cookie,
1835+
metadata,
1836+
deleted,
1837+
datatype));
1838+
1839+
// Manually run the bgfetch task.
1840+
MockGlobalTask mockTask(engine->getTaskable(),
1841+
TaskId::MultiBGFetcherTask);
1842+
store->getVBucket(vbid)->getShard()->getBgFetcher()->run(&mockTask);
1843+
1844+
EXPECT_EQ(ENGINE_SUCCESS,
1845+
store->getMetaData(makeStoredDocKey("key1"),
1846+
vbid,
1847+
cookie,
1848+
metadata,
1849+
deleted,
1850+
datatype));
1851+
tombstoneTime = uint64_t(metadata.exptime);
1852+
} else {
1853+
// Ephemeral tombstone time is not in the expiry field, we can only
1854+
// check the value by directly peeking at the StoredValue
1855+
auto vb = store->getVBucket(vbid);
1856+
auto* sv = vb->ht.find({"key1", DocNamespace::DefaultCollection},
1857+
TrackReference::No,
1858+
WantsDeleted::Yes);
1859+
ASSERT_NE(nullptr, sv);
1860+
deleted = sv->isDeleted();
1861+
tombstoneTime = uint64_t(sv->toOrderedStoredValue()->getDeletedTime());
1862+
}
18331863

1834-
// Manually run the bgfetch task.
1835-
MockGlobalTask mockTask(engine->getTaskable(), TaskId::MultiBGFetcherTask);
1836-
store->getVBucket(vbid)->getShard()->getBgFetcher()->run(&mockTask);
1837-
EXPECT_EQ(ENGINE_SUCCESS,
1838-
store->getMetaData(makeStoredDocKey("key1"),
1839-
vbid,
1840-
cookie,
1841-
metadata,
1842-
deleted,
1843-
datatype));
18441864
EXPECT_EQ(1, deleted);
18451865
EXPECT_EQ(PROTOCOL_BINARY_RAW_BYTES, datatype);
1846-
EXPECT_NE(0, metadata.exptime); // A locally created deleteTime
1866+
EXPECT_GE(tombstoneTime, biff.get())
1867+
<< "Expected a tombstone to have been set which is equal or "
1868+
"greater than our time traveller jump";
18471869

18481870
deleted = 0;
18491871
datatype = 0;
1850-
EXPECT_EQ(ENGINE_EWOULDBLOCK,
1851-
store->getMetaData(makeStoredDocKey("key2"),
1852-
vbid,
1853-
cookie,
1854-
metadata,
1855-
deleted,
1856-
datatype));
1857-
store->getVBucket(vbid)->getShard()->getBgFetcher()->run(&mockTask);
1858-
EXPECT_EQ(ENGINE_SUCCESS,
1859-
store->getMetaData(makeStoredDocKey("key2"),
1860-
vbid,
1861-
cookie,
1862-
metadata,
1863-
deleted,
1864-
datatype));
1872+
if (persistent()) {
1873+
EXPECT_EQ(ENGINE_EWOULDBLOCK,
1874+
store->getMetaData(makeStoredDocKey("key2"),
1875+
vbid,
1876+
cookie,
1877+
metadata,
1878+
deleted,
1879+
datatype));
1880+
// Manually run the bgfetch task.
1881+
MockGlobalTask mockTask(engine->getTaskable(),
1882+
TaskId::MultiBGFetcherTask);
1883+
store->getVBucket(vbid)->getShard()->getBgFetcher()->run(&mockTask);
1884+
1885+
EXPECT_EQ(ENGINE_SUCCESS,
1886+
store->getMetaData(makeStoredDocKey("key2"),
1887+
vbid,
1888+
cookie,
1889+
metadata,
1890+
deleted,
1891+
datatype));
1892+
1893+
tombstoneTime = uint64_t(metadata.exptime);
1894+
} else {
1895+
auto vb = store->getVBucket(vbid);
1896+
auto* sv = vb->ht.find({"key2", DocNamespace::DefaultCollection},
1897+
TrackReference::No,
1898+
WantsDeleted::Yes);
1899+
ASSERT_NE(nullptr, sv);
1900+
deleted = sv->isDeleted();
1901+
tombstoneTime =
1902+
ep_abs_time((sv->toOrderedStoredValue()->getDeletedTime()));
1903+
}
18651904
EXPECT_EQ(1, deleted);
18661905
EXPECT_EQ(PROTOCOL_BINARY_RAW_BYTES, datatype);
1867-
EXPECT_EQ(deleteTime, metadata.exptime); // Our replicated deleteTime!
1906+
EXPECT_EQ(deleteTime, tombstoneTime)
1907+
<< "key2 did not have our replicated deleteTime:" << deleteTime;
18681908
}
18691909

18701910
/*
@@ -2694,15 +2734,16 @@ TEST_F(SingleThreadedEPBucketTest, CreatedItemFreqDecayerTask) {
26942734

26952735
extern uint32_t dcp_last_delete_time;
26962736
extern std::string dcp_last_key;
2697-
// Combine warmup and DCP so we can check deleteTimes come back from disk
2698-
TEST_F(WarmupTest, produce_delete_times) {
2737+
TEST_P(STParameterizedBucketTest, produce_delete_times) {
26992738
setVBucketStateAndRunPersistTask(vbid, vbucket_state_active);
27002739
auto t1 = ep_real_time();
27012740
storeAndDeleteItem(
27022741
vbid, {"KEY1", DocNamespace::DefaultCollection}, "value");
27032742
auto t2 = ep_real_time();
2704-
// Now warmup to ensure that DCP will have to go to disk.
2705-
resetEngineAndWarmup();
2743+
2744+
// Clear checkpoint so DCP will goto backfill
2745+
auto vb = engine->getKVBucket()->getVBucket(vbid);
2746+
vb->checkpointManager->clear(*vb, 2);
27062747

27072748
auto cookie = create_mock_cookie();
27082749
auto producer =

engines/ep/tests/module_tests/test_helpers.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,10 @@ class TimeTraveller {
8787
mock_time_travel(-by);
8888
}
8989

90+
int get() const {
91+
return by;
92+
}
93+
9094
private:
9195
// Amount of time travel.
9296
int by;

0 commit comments

Comments
 (0)