Skip to content

Commit 0abff77

Browse files
committed
Fix[mqbs::FileStore]: Track write-head leaseId on applied records
Signed-off-by: Yuan Jing Vincent Yan <yyan82@bloomberg.net>
1 parent 9433136 commit 0abff77

2 files changed

Lines changed: 192 additions & 21 deletions

File tree

src/groups/mqb/mqbs/mqbs_filestore.cpp

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -548,7 +548,7 @@ int FileStore::openInRecoveryMode(bsl::ostream& errorDescription,
548548
if (0 == jit.lastSyncPointPosition()) {
549549
// Scenario (3) from above.
550550

551-
needTruncation = true;
551+
needTruncation = true;
552552
d_writeHeadLeaseId = 0;
553553

554554
journalOffset = (FileStoreProtocolUtil::bmqHeader(journalFd)
@@ -6485,23 +6485,34 @@ void FileStore::processStorageEvent(const bsl::shared_ptr<bdlbb::Blob>& blob,
64856485
// successfully, raise alarm otherwise.
64866486

64876487
if (BSLS_PERFORMANCEHINT_PREDICT_LIKELY(0 == rc)) {
6488-
d_highestSeqNums[recHeader->primaryLeaseId()] =
6489-
recHeader->sequenceNumber();
6488+
// Advance the write cursor to the record just applied.
64906489

6491-
if (isPartitionSyncEvent) {
6492-
// If we are processing a partition-sync event, we have to
6493-
// bump up the leaseId to that of the message, because we don't
6494-
// get a separate notification about leaseId (unlike in steady
6495-
// state when StorageMgr invokes fs.setActivePrimary()).
6496-
6497-
d_writeHeadLeaseId = recHeader->primaryLeaseId();
6490+
const bool bumpedLeaseId = d_writeHeadLeaseId <
6491+
recHeader->primaryLeaseId();
6492+
setWriteHead(recHeader->primaryLeaseId(),
6493+
recHeader->sequenceNumber());
64986494

6499-
BALL_LOG_INFO
6500-
<< partitionDesc()
6501-
<< "Bumped up primaryLeaseId to: " << d_writeHeadLeaseId
6502-
<< " while processing a "
6503-
<< "partition-sync storage message. " << "New PSN: "
6504-
<< printPSN(d_writeHeadLeaseId, writeHeadSeqNum()) << ")";
6495+
if (bumpedLeaseId) {
6496+
if (isPartitionSyncEvent) {
6497+
BALL_LOG_INFO
6498+
<< partitionDesc()
6499+
<< "Bumped up leaseId while processing a "
6500+
<< "partition-sync event. New PSN: "
6501+
<< printPSN(d_writeHeadLeaseId, writeHeadSeqNum())
6502+
<< ".";
6503+
}
6504+
else {
6505+
// Apply the record to stay in sync, but alarm since the
6506+
// ordering was violated.
6507+
BMQTSK_ALARMLOG_ALARM("REPLICATION")
6508+
<< partitionDesc()
6509+
<< "Applied a record whose leaseId is higher than the "
6510+
<< "write head in a non partition-sync event, before "
6511+
<< "the primary's active-status advisory was "
6512+
<< "processed. New PSN: "
6513+
<< printPSN(d_writeHeadLeaseId, writeHeadSeqNum())
6514+
<< "." << BMQTSK_ALARMLOG_END;
6515+
}
65056516
}
65066517
}
65076518
else {
@@ -6906,7 +6917,7 @@ void FileStore::setActivePrimary(mqbnet::ClusterNode* primaryNode,
69066917
d_highestSeqNums[primaryLeaseId] = 0;
69076918
}
69086919
d_writeHeadLeaseId = primaryLeaseId;
6909-
d_primaryNode_p = primaryNode;
6920+
d_primaryNode_p = primaryNode;
69106921

69116922
BALL_LOG_INFO << partitionDesc() << "Primary node is now "
69126923
<< primaryNode->nodeDescription() << " with PSN: "

src/groups/mqb/mqbs/mqbs_filestore.t.cpp

Lines changed: 164 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,10 @@
3636
#include <bmqp_blobpoolutil.h>
3737
#include <bmqp_crc32c.h>
3838
#include <bmqp_ctrlmsg_messages.h>
39+
#include <bmqp_protocol.h>
40+
#include <bmqp_storageeventbuilder.h>
3941
#include <bmqt_messageguid.h>
42+
#include <bmqt_resultcode.h>
4043
#include <bmqt_uri.h>
4144

4245
#include <bmqu_memoutstream.h>
@@ -160,6 +163,67 @@ void recoveredQueuesCb(
160163
static_cast<void>(queueKeyInfoMap);
161164
}
162165

166+
/// Build a storage event carrying a single regular SyncPt journal record with
167+
/// the specified `leaseId`, `seqNum`, `journalOffsetWords`, `dataOffsetDwords`
168+
/// and `qlistOffsetWords`, and apply it to the specified `fs` as a
169+
/// partition-sync event received from `source`. Use the specified
170+
/// `blobSpPool`, `bufferFactory`, `partitionId` and `allocator`.
171+
void applyReplicatedSyncPoint(mqbs::FileStore* fs,
172+
bmqp::BlobPoolUtil::BlobSpPool* blobSpPool,
173+
bdlbb::BlobBufferFactory* bufferFactory,
174+
mqbnet::ClusterNode* source,
175+
int partitionId,
176+
unsigned int leaseId,
177+
bsls::Types::Uint64 seqNum,
178+
unsigned int journalOffsetWords,
179+
unsigned int dataOffsetDwords,
180+
unsigned int qlistOffsetWords,
181+
bslma::Allocator* allocator)
182+
{
183+
// Lay out a regular SyncPt journal record in a fresh blob buffer. The
184+
// RecordHeader carries the PSN that 'processStorageEvent' validates.
185+
bdlbb::BlobBuffer recBuf;
186+
bufferFactory->allocate(&recBuf);
187+
bsl::memset(recBuf.data(),
188+
0,
189+
mqbs::FileStoreProtocol::k_JOURNAL_RECORD_SIZE);
190+
191+
mqbs::JournalOpRecord* rec = new (recBuf.data())
192+
mqbs::JournalOpRecord(mqbs::JournalOpType::e_SYNCPOINT,
193+
mqbs::SyncPointType::e_REGULAR,
194+
seqNum,
195+
k_NODE_ID,
196+
leaseId,
197+
dataOffsetDwords,
198+
qlistOffsetWords,
199+
mqbs::RecordHeader::k_MAGIC);
200+
rec->header()
201+
.setPrimaryLeaseId(leaseId)
202+
.setSequenceNumber(seqNum)
203+
.setTimestamp(
204+
bdlt::EpochUtil::convertToTimeT64(bdlt::CurrentTime::utc()));
205+
206+
bdlbb::BlobBuffer journalRecBuf(
207+
recBuf.buffer(),
208+
mqbs::FileStoreProtocol::k_JOURNAL_RECORD_SIZE);
209+
210+
bmqp::StorageEventBuilder builder(mqbs::FileStoreProtocol::k_VERSION,
211+
bmqp::EventType::e_PARTITION_SYNC,
212+
blobSpPool,
213+
allocator);
214+
bmqt::EventBuilderResult::Enum brc = builder.packMessage(
215+
bmqp::StorageMessageType::e_JOURNAL_OP,
216+
static_cast<unsigned int>(partitionId),
217+
0, // flags
218+
journalOffsetWords,
219+
journalRecBuf);
220+
BMQTST_ASSERT_EQ(brc, bmqt::EventBuilderResult::e_SUCCESS);
221+
222+
fs->processStorageEvent(builder.blob(),
223+
true, // isPartitionSyncEvent
224+
source);
225+
}
226+
163227
// CLASSES
164228
// ============
165229
// class Tester
@@ -910,7 +974,8 @@ static void test1_breathingTest()
910974
// TBD: verify
911975
}
912976

913-
fs.close();
977+
rc = fs.close();
978+
BMQTST_ASSERT_EQ(0, rc);
914979

915980
BMQTST_ASSERT_EQ(false, fs.isOpen());
916981

@@ -1009,7 +1074,8 @@ static void test2_printTest()
10091074
stream << fsIt;
10101075
BMQTST_ASSERT_EQ(stream.str(), "INVALID");
10111076

1012-
fs.close();
1077+
const int rc = fs.close();
1078+
BMQTST_ASSERT_EQ(0, rc);
10131079
}
10141080

10151081
static void test3_partitionFullAlarm()
@@ -1153,7 +1219,8 @@ static void test3_partitionFullAlarm()
11531219
poster.postMessage() == mqbi::StorageResult::e_SUCCESS);
11541220

11551221
fs.unregisterStorage(storage_sp.get());
1156-
fs.close();
1222+
rc = fs.close();
1223+
BMQTST_ASSERT_EQ(0, rc);
11571224
}
11581225

11591226
static void test4_recoverMessagesAcrossLeaseIds()
@@ -1271,7 +1338,99 @@ static void test4_recoverMessagesAcrossLeaseIds()
12711338
// All records from both leaseIds should have been recovered.
12721339
BMQTST_ASSERT_EQ(fs.numRecords(), numRecords);
12731340

1274-
fs.close();
1341+
rc = fs.close();
1342+
BMQTST_ASSERT_EQ(0, rc);
1343+
}
1344+
1345+
static void test5_writeHeadFollowsAppliedLease()
1346+
// ------------------------------------------------------------------------
1347+
// WRITE HEAD FOLLOWS APPLIED LEASE ID
1348+
//
1349+
// Concerns:
1350+
// When a replica applies a record whose primary leaseId is higher than its
1351+
// current write head via a partition-sync event, the write head must advance
1352+
// to that leaseId (together with the sequence number).
1353+
//
1354+
// Testing:
1355+
// processStorageEvent
1356+
// ------------------------------------------------------------------------
1357+
{
1358+
bmqtst::TestHelperUtil::ignoreCheckDefAlloc() = true;
1359+
1360+
Tester tester("./test-cluster123-5");
1361+
mqbs::FileStore& fs = tester.fileStore();
1362+
1363+
int rc = fs.open(0);
1364+
BMQTST_ASSERT_EQ(0, rc);
1365+
1366+
bdlbb::PooledBlobBufferFactory bufferFactory(
1367+
1024,
1368+
bmqtst::TestHelperUtil::allocator());
1369+
bmqp::BlobPoolUtil::BlobSpPoolSp blobSpPool =
1370+
bmqp::BlobPoolUtil::createBlobPool(
1371+
&bufferFactory,
1372+
bmqtst::TestHelperUtil::allocator());
1373+
1374+
const int k_PARTITION_ID = 0;
1375+
const unsigned int dataOffsetDwords = k_SIZEOF_HEADERS_DATA_FILE /
1376+
bmqp::Protocol::k_DWORD_SIZE;
1377+
const unsigned int qlistOffsetWords = k_SIZEOF_HEADERS_QLIST_FILE /
1378+
bmqp::Protocol::k_WORD_SIZE;
1379+
1380+
unsigned int journalOffsetWords = k_SIZEOF_HEADERS_JOURNAL_FILE /
1381+
bmqp::Protocol::k_WORD_SIZE;
1382+
const unsigned int k_RECORD_WORDS =
1383+
mqbs::FileStoreProtocol::k_JOURNAL_RECORD_SIZE /
1384+
bmqp::Protocol::k_WORD_SIZE;
1385+
1386+
fs.setActivePrimary(tester.node(), 1);
1387+
// A sync point is issued -> [1, 1]
1388+
journalOffsetWords += k_RECORD_WORDS;
1389+
BMQTST_ASSERT_EQ(1U, fs.writeHeadLeaseId());
1390+
BMQTST_ASSERT_EQ(1ULL, fs.writeHeadSeqNum());
1391+
1392+
applyReplicatedSyncPoint(&fs,
1393+
blobSpPool.get(),
1394+
&bufferFactory,
1395+
tester.node(),
1396+
k_PARTITION_ID,
1397+
1, // leaseId
1398+
2, // seqNum
1399+
journalOffsetWords,
1400+
dataOffsetDwords,
1401+
qlistOffsetWords,
1402+
bmqtst::TestHelperUtil::allocator());
1403+
journalOffsetWords += k_RECORD_WORDS;
1404+
BMQTST_ASSERT_EQ(1U, fs.writeHeadLeaseId());
1405+
BMQTST_ASSERT_EQ(2ULL, fs.writeHeadSeqNum());
1406+
1407+
for (bsls::Types::Uint64 seqNum = 1; seqNum <= 4; ++seqNum) {
1408+
applyReplicatedSyncPoint(&fs,
1409+
blobSpPool.get(),
1410+
&bufferFactory,
1411+
tester.node(),
1412+
k_PARTITION_ID,
1413+
2, // leaseId
1414+
seqNum,
1415+
journalOffsetWords,
1416+
dataOffsetDwords,
1417+
qlistOffsetWords,
1418+
bmqtst::TestHelperUtil::allocator());
1419+
journalOffsetWords += k_RECORD_WORDS;
1420+
BMQTST_ASSERT_EQ(2U, fs.writeHeadLeaseId());
1421+
BMQTST_ASSERT_EQ(seqNum, fs.writeHeadSeqNum());
1422+
}
1423+
1424+
// The delayed 'setActivePrimary' for leaseId 2 now arrives. Verify it
1425+
// does not reset the sequence number of leaseId 2 back to zero.
1426+
fs.setActivePrimary(tester.node(), 2);
1427+
// A sync point is issued -> [2, 5]
1428+
BMQTST_ASSERT_EQ(2U, fs.writeHeadLeaseId());
1429+
BMQTST_ASSERT_GE(5ULL, fs.writeHeadSeqNum());
1430+
BMQTST_ASSERT_EQ(tester.node(), fs.primaryNode());
1431+
1432+
rc = fs.close();
1433+
BMQTST_ASSERT_EQ(0, rc);
12751434
}
12761435

12771436
} // close unnamed namespace
@@ -1288,6 +1447,7 @@ int main(int argc, char* argv[])
12881447

12891448
switch (_testCase) {
12901449
case 0:
1450+
case 5: test5_writeHeadFollowsAppliedLease(); break;
12911451
case 4: test4_recoverMessagesAcrossLeaseIds(); break;
12921452
case 3: test3_partitionFullAlarm(); break;
12931453
case 2: test2_printTest(); break;

0 commit comments

Comments
 (0)