Skip to content

Commit acafdc6

Browse files
committed
fixes
Signed-off-by: dorjesinpo <129227380+dorjesinpo@users.noreply.github.com>
1 parent a942ba5 commit acafdc6

10 files changed

Lines changed: 343 additions & 138 deletions

src/groups/mqb/mqbblp/mqbblp_queueengineutil.cpp

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1179,24 +1179,28 @@ QueueEngineUtil_AppState::processDeliveryList(bsls::TimeInterval* delay,
11791179
// Retrieve message from the storage
11801180
reader->reset(*it);
11811181

1182-
if (!reader->hasReceipt()) {
1183-
// TODO: remove extra logging
1184-
BMQ_LOGTHROTTLE_INFO << "#STORAGE_UNKNOWN_MESSAGE " << "Queue: '"
1185-
<< d_queue_p->description() << "', app: '"
1186-
<< appId() << "' has unconfirmed GUID: '"
1187-
<< *it << "'. Stopping the redelivery now.";
1188-
break;
1189-
}
1190-
11911182
if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(reader->atEnd())) {
11921183
BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
11931184

1194-
// The message got gc'ed or purged
1185+
// The message got gc'ed or purged. Do not stop the redelivery:
1186+
// fall through and treat it as sent so it is erased from the list
1187+
// and the remaining messages still get a chance to be delivered.
11951188
BMQ_LOGTHROTTLE_INFO << "#STORAGE_UNKNOWN_MESSAGE " << "Queue: '"
11961189
<< d_queue_p->description() << "', app: '"
11971190
<< appId() << "' could not redeliver GUID: '"
11981191
<< *it << "' (not in the storage)";
11991192
}
1193+
else if (!reader->hasReceipt()) {
1194+
// The message is in the storage but not yet committed/receipted
1195+
// (e.g. Raft has not replicated it to a majority yet). Stop here
1196+
// and retry on the next flush.
1197+
// TODO: remove extra logging
1198+
BMQ_LOGTHROTTLE_INFO << "#STORAGE_UNKNOWN_MESSAGE " << "Queue: '"
1199+
<< d_queue_p->description() << "', app: '"
1200+
<< appId() << "' has unconfirmed GUID: '"
1201+
<< *it << "'. Stopping the redelivery now.";
1202+
break;
1203+
}
12001204
else if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(
12011205
!reader->appMessageView(ordinal()).isPending())) {
12021206
BMQ_LOGTHROTTLE_INFO

src/groups/mqb/mqbc/mqbc_clusterutil.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1119,7 +1119,17 @@ bool ClusterUtil::assignQueue(ClusterState* clusterState,
11191119
status,
11201120
allocator);
11211121

1122-
if (status->category() == bmqp_ctrlmsg::StatusCategory::E_SUCCESS) {
1122+
// Only apply to the CSL if 'startQueueAssignment' actually produced an
1123+
// advisory. When the queue is already ASSIGNING or ASSIGNED (e.g. a
1124+
// concurrent request for the same queue is already in flight or
1125+
// committed), 'startQueueAssignment' returns success without populating
1126+
// 'queueAdvisory'. Applying that empty advisory (LSN [ 0, 0 ]) would be
1127+
// rejected by the ledger as stale and surface to the requester as a
1128+
// spurious "CSL failure". The already-pending advisory will still commit
1129+
// and resolve all pending contexts for the queue, so replying success here
1130+
// is correct.
1131+
if (status->category() == bmqp_ctrlmsg::StatusCategory::E_SUCCESS &&
1132+
!queueAdvisory.queues().empty()) {
11231133
BSLS_ASSERT_SAFE(result);
11241134

11251135
// Apply 'queueAssignmentAdvisory' to CSL

src/groups/mqb/mqbnet/mqbnet_channel.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,7 @@ Channel::writeBufferedItem(bool* isConsumed,
489489
case bmqp::EventType::e_AUTHENTICATION:
490490
case bmqp::EventType::e_RAFT_CLUSTER:
491491
case bmqp::EventType::e_RAFT_PARTITION:
492+
case bmqp::EventType::e_RAFT_SNAPSHOT:
492493
default: {
493494
ControlArgs x(item);
494495
rc = writeImmediate(isConsumed,

src/groups/mqb/mqbraft/mqbraft_clusterstateraft.cpp

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -242,25 +242,19 @@ void ClusterStateRaft::applyCommittedEntry(const LogEntry& entry)
242242
if (clusterMessage.choice().selectionId() ==
243243
bmqp_ctrlmsg::ClusterMessageChoice::
244244
SELECTION_ID_PARTITION_PRIMARY_ADVISORY) {
245-
// Skip 'ClusterUtil::apply': for this message type it routes to
246-
// 'applyPartitionPrimary', which is an unconditional no-op in Raft
247-
// mode (the data-partition Raft, not the CSL, owns primary/leaseId --
248-
// 'isRaftEnabled()' early-return) -- true for both our own artificial
249-
// advisory and any stale recovered legacy 'partitionPrimaryAdvisory'.
250-
// This is Raft-only code (ClusterStateRaft), so that branch is always
251-
// taken; nothing is lost by not calling it. Persistence is already
252-
// guaranteed by the Raft commit itself, independent of this call.
245+
// Don't apply to ClusterState: under Raft the data-partition Raft, not
246+
// the CSL, owns partition primary/leaseId, so 'ClusterUtil::apply' ->
247+
// 'applyPartitionPrimary' is a no-op ('isRaftEnabled()' early-return)
248+
// and the Raft commit already guarantees persistence.
253249
//
254-
// Record the CSL-side half of the two independent signals the
255-
// orchestrator's readiness check compares (this committed advisory
256-
// vs. local data-partition Raft leadership) in a dedicated field
257-
// ('advisoryConfirmedLeaseId'), separate from 'primaryLeaseId' (which
258-
// reflects only the locally-observed side), so it can be recorded
259-
// regardless of whether this node knows the partition's primary yet.
260-
// Monotonic: a stale recovered legacy entry must not regress a
261-
// leaseId already confirmed by a fresher commit. Deciding
262-
// activation/availability from this is the orchestrator's job (via
263-
// the readiness check below), not this method's.
250+
// The orchestrator declares a partition ready only once two leaseIds
251+
// agree: the one this CSL advisory just committed, and the one
252+
// observed locally from data-partition Raft leadership
253+
// ('primaryLeaseId'). The two can arrive in either order, so store
254+
// the advisory's leaseId in its own field, 'advisoryConfirmedLeaseId',
255+
// rather than overwriting 'primaryLeaseId'. Update it monotonically
256+
// so a stale advisory replayed at recovery can't lower a leaseId a
257+
// later commit confirmed.
264258
const bmqp_ctrlmsg::PartitionPrimaryAdvisory& adv =
265259
clusterMessage.choice().partitionPrimaryAdvisory();
266260
for (bsl::vector<bmqp_ctrlmsg::PartitionPrimaryInfo>::const_iterator

src/groups/mqb/mqbraft/mqbraft_cslraftlog.t.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -151,9 +151,6 @@ class Tester {
151151
}
152152

153153
CslRaftLog& raftLog() { return d_cslRaftLog; }
154-
bsl::shared_ptr<mqbsi::Log>& log() { return d_log_sp; }
155-
bdlbb::PooledBlobBufferFactory& factory() { return d_bufferFactory; }
156-
CslRaftLog::BlobSpPool* blobPool() { return d_blobSpPool_sp.get(); }
157154
bslma::Allocator* allocator() { return d_allocator_p; }
158155
};
159156

src/groups/mqb/mqbraft/mqbraft_partitionraft.cpp

Lines changed: 79 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -928,6 +928,7 @@ int PartitionRaft::propose(
928928
const bsl::shared_ptr<mqbs::FileStore::PendingWrite>& pw)
929929
{
930930
// executed by the partition *DISPATCHER* thread
931+
enum { rc_UNAVAILABLE = -1 };
931932

932933
// Compute the rollover footprint (DATA and QLIST bytes) from the write.
933934
// The JOURNAL reserve is always checked by 'rolloverIfNeeded'; only
@@ -946,17 +947,45 @@ int PartitionRaft::propose(
946947
*pw->d_appIdKeyPairs_p);
947948
}
948949

949-
int rc = rolloverIfNeeded(dataBytes, qlistBytes);
950-
if (0 != rc) {
951-
return rc; // RETURN
950+
if (!d_fileStore_sp->isFileSetAvailable()) {
951+
// The journal is read-only: a rollover could not reclaim enough space
952+
// (outstanding records exceed the policy threshold). Mirror legacy
953+
// 'FileStore::writeQueueOpRecord': the ONLY write still permitted is a
954+
// full-queue PURGE, written into the reserved PURGE area, which frees
955+
// outstanding records and lets 'onPurgeComplete' roll over to recover.
956+
// A per-appId purge (non-null appKey) writes per-message deletion
957+
// records, for which there is no room -- reject it, like any other
958+
// write, so the partition stays read-only.
959+
const bool isPurge = pw->d_recordType ==
960+
mqbs::RecordType::e_QUEUE_OP &&
961+
pw->d_queueOpType == mqbs::QueueOpType::e_PURGE &&
962+
pw->d_appKey.isNull();
963+
if (!isPurge || !d_fileStore_sp->primaryHasPurgeReserve()) {
964+
return rc_UNAVAILABLE; // RETURN
965+
}
966+
967+
BALL_LOG_WARN << "Partition [" << d_partitionId
968+
<< "] Writing PURGE record for queueKey ["
969+
<< pw->d_queueKey
970+
<< "] into the reserved journal area despite the "
971+
<< "partition being read-only (unavailable).";
972+
// Fall through: append the PURGE directly (no rollover); the reserved
973+
// area guarantees room.
952974
}
975+
else {
976+
int rc = rolloverIfNeeded(dataBytes, qlistBytes);
977+
if (0 != rc) {
978+
return rc; // RETURN
979+
}
953980

954-
// If a rollover is in flight, buffer the write for replay into the new
955-
// file once the rollover commits ('bufferPendingWrite' reserves
956-
// 'pw->d_handle').
957-
if (d_isRolloverPending) {
958-
return d_raftLog_mp->bufferPendingWrite(pw,
959-
d_raftNode_mp->currentTerm());
981+
// If a rollover is in flight, buffer the write for replay into the new
982+
// file once the rollover commits ('bufferPendingWrite' reserves
983+
// 'pw->d_handle').
984+
if (d_isRolloverPending) {
985+
return d_raftLog_mp->bufferPendingWrite(
986+
pw,
987+
d_raftNode_mp->currentTerm());
988+
}
960989
}
961990

962991
// Otherwise enqueue it for 'append()'; the record's sequence number
@@ -967,9 +996,9 @@ int PartitionRaft::propose(
967996
d_raftLog_mp->setPendingWrite(pw);
968997

969998
RaftNodeOutput output(d_allocator_p);
970-
rc = d_raftNode_mp->propose(&output,
971-
bsl::shared_ptr<bdlbb::Blob>(),
972-
pw->d_id);
999+
int rc = d_raftNode_mp->propose(&output,
1000+
bsl::shared_ptr<bdlbb::Blob>(),
1001+
pw->d_id);
9731002
if (rc != 0) {
9741003
return rc;
9751004
}
@@ -1121,23 +1150,37 @@ int PartitionRaft::rolloverIfNeeded(bsls::Types::Uint64 dataBytes,
11211150
bsls::Types::Uint64 qlistBytes)
11221151
{
11231152
// executed by the partition *DISPATCHER* thread
1124-
enum { rc_SUCCESS = 0 };
1153+
enum { rc_SUCCESS = 0, rc_READONLY = -1 };
11251154

1126-
if (!isLeader() ||
1127-
!d_fileStore_sp->primaryNeedsRollover(dataBytes, qlistBytes)) {
1155+
if (!isLeader()) {
11281156
return rc_SUCCESS; // RETURN
11291157
}
11301158

1131-
// A rollover is required. Rather than NACK the triggering write, propose
1132-
// 'e_ROLLOVER' (unless one is already in flight -- at most one uncommitted
1133-
// rollover at a time) and return success. 'setPendingWrite()' then
1134-
// buffers this triggering write, and every subsequent one, until the
1135-
// rollover commits.
1136-
if (!d_isRolloverPending) {
1137-
proposeRollover();
1159+
switch (d_fileStore_sp->primaryRolloverNeed(dataBytes, qlistBytes)) {
1160+
case mqbs::FileStore::e_ROLLOVER_NONE: {
1161+
return rc_SUCCESS; // RETURN
1162+
}
1163+
case mqbs::FileStore::e_ROLLOVER_READONLY: {
1164+
// A rollover cannot reclaim enough space -- the partition is full.
1165+
// 'primaryRolloverNeed' has already marked it read-only and panicked;
1166+
// NACK the triggering write rather than roll over into a same-size
1167+
// file and overflow it. The partition recovers only via a full purge
1168+
// (see 'onPurgeComplete').
1169+
return rc_READONLY; // RETURN
1170+
}
1171+
case mqbs::FileStore::e_ROLLOVER_NEEDED:
1172+
default: {
1173+
// A rollover is required and will reclaim enough space. Rather than
1174+
// NACK the triggering write, propose 'e_ROLLOVER' (unless one is
1175+
// already in flight -- at most one uncommitted rollover at a time).
1176+
// 'setPendingWrite()' then buffers this triggering write, and every
1177+
// subsequent one, until the rollover commits.
1178+
if (!d_isRolloverPending) {
1179+
proposeRollover();
1180+
}
1181+
return rc_SUCCESS; // RETURN
1182+
}
11381183
}
1139-
1140-
return rc_SUCCESS;
11411184
}
11421185

11431186
void PartitionRaft::drainPendingWrites()
@@ -1658,9 +1701,18 @@ void PartitionRaft::onPurgeComplete()
16581701
// would drive 'rolloverImpl' directly -- outside the Raft log. Only the
16591702
// leader proposes, and only if the file set is at capacity;
16601703
// 'rolloverIfNeeded' is a no-op otherwise, so this is safe on replicas too
1661-
// (they roll over via the committed 'e_ROLLOVER' apply hook). The legacy
1662-
// 'd_journalFileAvailable' re-enable is not needed here: its false-setters
1663-
// (legacy 'rolloverIfNeeded' / 'setActivePrimary') are legacy-only.
1704+
// (they roll over via the committed 'e_ROLLOVER' apply hook).
1705+
//
1706+
// If a full purge just recovered a read-only partition, the journal is
1707+
// marked unavailable. Re-enable it (mirrors legacy
1708+
// 'FileStore::onPurgeComplete') so the reclaiming rollover's sync-point
1709+
// marker can be written -- 'writeSyncPointRecord' refuses on an
1710+
// unavailable journal. 'rolloverIfNeeded' re-disables it if the freed
1711+
// space is still insufficient (a rollover would still overflow).
1712+
if (isLeader() && !d_fileStore_sp->isFileSetAvailable()) {
1713+
d_fileStore_sp->setAvailabilityStatus(true);
1714+
}
1715+
16641716
rolloverIfNeeded(0, 0);
16651717
}
16661718

src/groups/mqb/mqbraft/mqbraft_raftnode.t.cpp

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -261,14 +261,6 @@ class TestCluster {
261261
}
262262
}
263263

264-
/// Tick only node 'id', deliver messages until quiet.
265-
void tickNode(int id)
266-
{
267-
RaftNodeOutput output(d_allocator_p);
268-
d_nodes[id]->tick(&output);
269-
runUntilQuiet(&output);
270-
}
271-
272264
/// Find the leader node. Return -1 if none.
273265
int findLeader() const
274266
{
@@ -284,18 +276,6 @@ class TestCluster {
284276
return leader;
285277
}
286278

287-
/// Return count of nodes that think 'nodeId' is the leader.
288-
int leaderAgreement(int nodeId) const
289-
{
290-
int count = 0;
291-
for (int i = 0; i < d_numNodes; ++i) {
292-
if (d_nodes[i]->leaderId() == nodeId) {
293-
++count;
294-
}
295-
}
296-
return count;
297-
}
298-
299279
bsl::shared_ptr<bdlbb::Blob> makeBlob(const char* data)
300280
{
301281
bsl::shared_ptr<bdlbb::Blob> blob =
@@ -309,7 +289,6 @@ class TestCluster {
309289
// ACCESSORS
310290
RaftNode* node(int id) { return d_nodes[id]; }
311291
MemoryRaftLog* log(int id) { return d_logs[id]; }
312-
int numNodes() const { return d_numNodes; }
313292
};
314293

315294
/// Tick the cluster until a leader emerges or maxTicks is reached.

0 commit comments

Comments
 (0)