Skip to content

Commit 21ccff0

Browse files
committed
idempotent restore state
Signed-off-by: dorjesinpo <129227380+dorjesinpo@users.noreply.github.com>
1 parent 32f32f3 commit 21ccff0

3 files changed

Lines changed: 188 additions & 150 deletions

File tree

src/groups/mqb/mqbblp/mqbblp_clusterqueuehelper.cpp

Lines changed: 170 additions & 146 deletions
Original file line numberDiff line numberDiff line change
@@ -3778,44 +3778,14 @@ void ClusterQueueHelper::restoreStateCluster(int partitionId)
37783778
const ClusterStatePartitionInfo* pinfo = 0;
37793779

37803780
if (!allPartitions) {
3781-
pinfo = &(d_clusterState_p->partition(partitionId));
3782-
BSLS_ASSERT_SAFE(pinfo);
3783-
if (!hasActiveAvailablePrimary(partitionId)) {
3784-
BALL_LOG_INFO << d_cluster_p->description() << " Partition ["
3785-
<< partitionId
3786-
<< "]: Not restoring partition state because there "
3787-
<< "is no primary or primary isn't ACTIVE. Current "
3788-
<< "primary: "
3789-
<< (pinfo->primaryNode()
3790-
? pinfo->primaryNode()->nodeDescription()
3791-
: "** null **")
3792-
<< ", primary status: " << pinfo->primaryStatus();
3781+
pinfo = activeAvailablePrimaryPartition(&isSelfPrimaryAndLeader,
3782+
partitionId);
3783+
3784+
if (!pinfo) {
37933785
return; // RETURN
37943786
}
3795-
3796-
// Primary for this partitionId is ACTIVE. Check if self is the
3797-
// primary and leader. If self is primary but not leader, this is
3798-
// primary-leader divergence and we should not proceed with state
3799-
// restore.
3800-
3801-
isSelfPrimaryAndLeader =
3802-
pinfo->primaryNode() == d_clusterData_p->membership().selfNode() &&
3803-
d_clusterData_p->electorInfo().isSelfLeader();
38043787
}
38053788

3806-
/// TODO (FSM); remove after switching to FSM
3807-
if (!d_cluster_p->isFSMWorkflow() && isSelfPrimaryAndLeader) {
3808-
// Note that this fails if there are data
3809-
mqbc::ClusterState::AssignmentVisitor doubleAssignmentVisitor =
3810-
bdlf::BindUtil::bindS(d_allocator_p,
3811-
&mqbi::StorageManager::unregisterQueue,
3812-
d_storageManager_p,
3813-
bdlf::PlaceHolders::_1, // uri
3814-
bdlf::PlaceHolders::_2); // partitionId),
3815-
3816-
d_clusterState_p->iterateDoubleAssignments(partitionId,
3817-
doubleAssignmentVisitor);
3818-
}
38193789
ConditionalAdvance<QueueContextMapConstIter> conditional;
38203790
for (QueueContextMapConstIter cit = d_queues.cbegin();
38213791
cit != d_queues.cend();
@@ -3850,134 +3820,139 @@ void ClusterQueueHelper::restoreStateCluster(int partitionId)
38503820

38513821
continue; // CONTINUE
38523822
}
3853-
}
3854-
else {
3855-
// A specific partitionId is specified. Attempt to re-issue
3856-
// open-queue requests for all appropriate queues assigned to that
3857-
// partition.
38583823

3859-
if (queueContext->partitionId() != partitionId) {
3860-
// Skip the queue as its assigned to a different partitionId.
3861-
continue; // CONTINUE;
3824+
// Proceed as if a specific partitionId is specified.
3825+
3826+
pinfo = activeAvailablePrimaryPartition(
3827+
&isSelfPrimaryAndLeader,
3828+
queueContext->partitionId());
3829+
if (!pinfo) {
3830+
continue;
38623831
}
3832+
}
3833+
else if (queueContext->partitionId() != partitionId) {
3834+
// Skip the queue as its assigned to a different partitionId.
3835+
continue; // CONTINUE;
3836+
}
3837+
3838+
// Attempt to re-issue open-queue requests for all appropriate queues
3839+
// assigned to that partition.
38633840

3864-
BSLS_ASSERT_SAFE(isQueueAssigned(*queueContext));
3865-
BSLS_ASSERT_SAFE(isQueuePrimaryAvailable(*queueContext));
3841+
BSLS_ASSERT_SAFE(isQueueAssigned(*queueContext));
3842+
BSLS_ASSERT_SAFE(isQueuePrimaryAvailable(*queueContext));
38663843

38673844
// Start restore cycle
38683845
bsl::shared_ptr<PartitionReopenCycle> cycle =
38693846
startPartitionReopen(partitionId, pinfo->primaryLeaseId());
38703847

3871-
// Verify the CSL if needed by comparing it with the Domain config
3872-
if (liveQInfo.d_queue_sp) {
3873-
if (isSelfPrimaryAndLeader) {
3874-
// We are assuming that it is not possible for a node to be
3875-
// primary, lose primary-ship and regain primary-ship;
3876-
// unless eventually the node went down in which case it
3877-
// will start from fresh.
3878-
3879-
// Moreover, since self node is now the primary, it is
3880-
// important for it to register the queue with the
3881-
// StorageManager. This is logically equivalent to
3882-
// registering the queue with StorageManager when a primary
3883-
// node creates a local queue instance (see
3884-
// 'createQueueFactory').
3885-
3886-
bsl::vector<bsl::string> added(d_allocator_p);
3887-
bsl::vector<bsl::string> removed(d_allocator_p);
3888-
mqbi::Domain* domain = liveQInfo.d_queue_sp->domain();
3889-
3890-
match(&added,
3891-
&removed,
3892-
*queueContext->d_stateQInfo_sp,
3893-
domain->config().mode());
3894-
3895-
if (!removed.empty() || !added.empty()) {
3896-
VoidFunctor park = bdlf::BindUtil::bindS(
3897-
d_allocator_p,
3898-
&ClusterQueueHelper::convertToLocal,
3899-
this,
3900-
queueContext,
3901-
domain);
3902-
3903-
// Add to 'd_pendingUpdates' before calling
3904-
// 'updateAppIds' which is asynchronous (CSL commit)
3905-
liveQInfo.d_pendingUpdates.push_back(park);
3906-
3907-
mqbi::ClusterErrorCode::Enum result =
3908-
d_clusterStateManager_p->updateAppIds(
3909-
added,
3910-
removed,
3911-
domain->name(),
3912-
"");
3913-
3914-
if (mqbi::ClusterErrorCode::e_OK == result) {
3915-
// Cannot continue until 'onQueueUpdated'
3916-
// Send QueueUpdateAdvisory and _wait_ for commit
3917-
3918-
continue; // CONTINUE
3919-
}
3920-
3921-
// An update error is CSL error (in
3922-
// 'ClusterStateLedger::apply'). This queue cannot
3923-
// convertToLocal
3924-
// ('RootQueueEngine::initializeAppId' would assert
3925-
// if there is no storage for some app).
3926-
3927-
BSLS_ASSERT_SAFE(
3928-
false &&
3929-
"Failure to update Apps before convertToLocal");
3930-
}
3931-
else {
3932-
convertToLocal(queueContext, domain);
3848+
// Verify the CSL if needed by comparing it with the Domain config
3849+
if (liveQInfo.d_queue_sp) {
3850+
if (isSelfPrimaryAndLeader) {
3851+
// We are assuming that it is not possible for a node to be
3852+
// primary, lose primary-ship and regain primary-ship;
3853+
// unless eventually the node went down in which case it
3854+
// will start from fresh.
3855+
3856+
// Moreover, since self node is now the primary, it is
3857+
// important for it to register the queue with the
3858+
// StorageManager. This is logically equivalent to
3859+
// registering the queue with StorageManager when a primary
3860+
// node creates a local queue instance (see
3861+
// 'createQueueFactory').
3862+
3863+
bsl::vector<bsl::string> added(d_allocator_p);
3864+
bsl::vector<bsl::string> removed(d_allocator_p);
3865+
mqbi::Domain* domain = liveQInfo.d_queue_sp->domain();
3866+
3867+
match(&added,
3868+
&removed,
3869+
*queueContext->d_stateQInfo_sp,
3870+
domain->config().mode());
3871+
3872+
if (!removed.empty() || !added.empty()) {
3873+
VoidFunctor park = bdlf::BindUtil::bindS(
3874+
d_allocator_p,
3875+
&ClusterQueueHelper::convertToLocal,
3876+
this,
3877+
queueContext,
3878+
domain);
3879+
3880+
// Add to 'd_pendingUpdates' before calling
3881+
// 'updateAppIds' which is asynchronous (CSL commit)
3882+
liveQInfo.d_pendingUpdates.push_back(park);
3883+
3884+
mqbi::ClusterErrorCode::Enum result =
3885+
d_clusterStateManager_p->updateAppIds(added,
3886+
removed,
3887+
domain->name(),
3888+
"");
3889+
3890+
if (mqbi::ClusterErrorCode::e_OK == result) {
3891+
// Cannot continue until 'onQueueUpdated'
3892+
// Send QueueUpdateAdvisory and _wait_ for commit
3893+
3894+
continue; // CONTINUE
39333895
}
3896+
3897+
// An update error is CSL error (in
3898+
// 'ClusterStateLedger::apply'). This queue cannot
3899+
// convertToLocal
3900+
// ('RootQueueEngine::initializeAppId' would assert
3901+
// if there is no storage for some app).
3902+
3903+
BSLS_ASSERT_SAFE(
3904+
false &&
3905+
"Failure to update Apps before convertToLocal");
39343906
}
39353907
else {
3936-
if (queueContext->d_liveQInfo.d_numQueueHandles != 0) {
3937-
// In the case of a cluster member, queues are deleted
3938-
// 'lazily' when receiving a notification from the
3939-
// primary. This replica may have fully closed the
3940-
// queue, but the queue has not been deleted by the
3941-
// primary if another replica still uses it; however
3942-
// from this replica's perspective, we don't want to
3943-
// reopen the queue.
3944-
const bmqt::GenericResult::Enum rc =
3945-
restoreStateHelper(queueContext.get(),
3946-
pinfo->primaryNode(),
3908+
convertToLocal(queueContext, domain);
3909+
}
3910+
}
3911+
else {
3912+
if (queueContext->d_liveQInfo.d_numQueueHandles != 0) {
3913+
const bmqt::GenericResult::Enum rc = restoreStateHelper(
3914+
queueContext.get(),
3915+
pinfo->primaryNode(),
39473916
cycle);
39483917

3949-
if (rc == bmqt::GenericResult::e_NOT_CONNECTED) {
3950-
// Abort restore of the state: the channel is no
3951-
// longer valid or we hit high water mark. For the
3952-
// case of invalid channel, we'll wait for a new
3953-
// one to be active and will restart restoring the
3954-
// state from the beginning.
3955-
return; // RETURN
3956-
}
3957-
// In case of other type of failure, just continue
3958-
// processing other queues instead of stopping the
3959-
// 'state restore' sequence.
3960-
3961-
// REVISIT: this code sends pending Open Queue requests
3962-
// without waiting for the Reopen Queue Response.
3963-
}
3964-
else {
3965-
BMQ_LOGTHROTTLE_INFO
3966-
<< d_cluster_p->description()
3967-
<< ": Skipping restore of " << queueContext->uri()
3968-
<< " because it has no active queue handles";
3918+
if (rc == bmqt::GenericResult::e_NOT_CONNECTED) {
3919+
// Abort restore of the state: the channel is no
3920+
// longer valid or we hit high water mark. For the
3921+
// case of invalid channel, we'll wait for a new
3922+
// one to be active and will restart restoring the
3923+
// state from the beginning.
3924+
return; // RETURN
39693925
}
3926+
// In case of other type of failure, just continue
3927+
// processing other queues instead of stopping the
3928+
// 'state restore' sequence.
39703929

3971-
// We also need to issue requests for any pending contexts:
3972-
// when a primary fails over, the queue may have been
3973-
// already open on this node, and all clients which were
3974-
// connected to the old primary will immediately reconnect,
3975-
// some might connect to this node and will issue an open
3976-
// queue. Because primary just got lost, those open queue
3977-
// requests were not processed, but appended to the pending
3978-
// context list, so once we have an active primary, we
3979-
// should process them.
3930+
// REVISIT: this code sends pending Open Queue requests
3931+
// without waiting for the Reopen Queue Response.
39803932
}
3933+
else {
3934+
// In the case of a cluster member, queues are deleted
3935+
// 'lazily' when receiving a notification from the
3936+
// primary. This replica may have fully closed the
3937+
// queue, but the queue has not been deleted by the
3938+
// primary if another replica still uses it; however
3939+
// from this replica's perspective, we don't want to
3940+
// reopen the queue.
3941+
BMQ_LOGTHROTTLE_INFO
3942+
<< d_cluster_p->description()
3943+
<< ": Skipping restore of " << queueContext->uri()
3944+
<< " because it has no active queue handles";
3945+
}
3946+
3947+
// We also need to issue requests for any pending contexts:
3948+
// when a primary fails over, the queue may have been
3949+
// already open on this node, and all clients which were
3950+
// connected to the old primary will immediately reconnect,
3951+
// some might connect to this node and will issue an open
3952+
// queue. Because primary just got lost, those open queue
3953+
// requests were not processed, but appended to the pending
3954+
// context list, so once we have an active primary, we
3955+
// should process them.
39813956
}
39823957
// else, Queue instance is not created, but the queue is assigned.
39833958
// Proceed ahead.
@@ -6265,5 +6240,54 @@ void ClusterQueueHelper::match(bsl::vector<bsl::string>* added,
62656240
}
62666241
}
62676242

6243+
const mqbc::ClusterStatePartitionInfo*
6244+
ClusterQueueHelper::activeAvailablePrimaryPartition(
6245+
bool* isSelfPrimaryAndLeader,
6246+
int partitionId)
6247+
{
6248+
BSLS_ASSERT_SAFE(isSelfPrimaryAndLeader);
6249+
6250+
const ClusterStatePartitionInfo* pinfo = &(
6251+
d_clusterState_p->partition(partitionId));
6252+
BSLS_ASSERT_SAFE(pinfo);
6253+
if (!hasActiveAvailablePrimary(partitionId)) {
6254+
BALL_LOG_INFO << d_cluster_p->description() << " Partition ["
6255+
<< partitionId
6256+
<< "]: Not restoring partition state because there "
6257+
<< "is no primary or primary isn't ACTIVE. Current "
6258+
<< "primary: "
6259+
<< (pinfo->primaryNode()
6260+
? pinfo->primaryNode()->nodeDescription()
6261+
: "** null **")
6262+
<< ", primary status: " << pinfo->primaryStatus();
6263+
return 0; // RETURN
6264+
}
6265+
6266+
// Primary for this partitionId is ACTIVE. Check if self is the
6267+
// primary and leader. If self is primary but not leader, this is
6268+
// primary-leader divergence and we should not proceed with state
6269+
// restore.
6270+
6271+
*isSelfPrimaryAndLeader = pinfo->primaryNode() ==
6272+
d_clusterData_p->membership().selfNode() &&
6273+
d_clusterData_p->electorInfo().isSelfLeader();
6274+
6275+
/// TODO (FSM); remove after switching to FSM
6276+
if (!d_cluster_p->isFSMWorkflow() && *isSelfPrimaryAndLeader) {
6277+
// Note that this fails if there are data
6278+
mqbc::ClusterState::AssignmentVisitor doubleAssignmentVisitor =
6279+
bdlf::BindUtil::bindS(d_allocator_p,
6280+
&mqbi::StorageManager::unregisterQueue,
6281+
d_storageManager_p,
6282+
bdlf::PlaceHolders::_1, // uri
6283+
bdlf::PlaceHolders::_2); // partitionId),
6284+
6285+
d_clusterState_p->iterateDoubleAssignments(partitionId,
6286+
doubleAssignmentVisitor);
6287+
}
6288+
6289+
return pinfo;
6290+
}
6291+
62686292
} // close package namespace
62696293
} // close enterprise namespace

src/groups/mqb/mqbblp/mqbblp_clusterqueuehelper.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -906,6 +906,9 @@ class ClusterQueueHelper BSLS_KEYWORD_FINAL
906906

907907
void setAsClosed(const QueueContextSp& queueContextSp);
908908

909+
const ClusterStatePartitionInfo*
910+
activeAvailablePrimaryPartition(bool* isSelfPrimaryAndLeader, int id);
911+
909912
// PRIVATE MANIPULATORS
910913
// (virtual: mqbc::ClusterMembershipObserver)
911914

@@ -1286,10 +1289,6 @@ inline bool ClusterQueueHelper::hasActiveAvailablePrimary(
12861289
return false; // RETURN
12871290
}
12881291

1289-
if (d_cluster_p->isFSMWorkflow()) {
1290-
return true; // RETURN
1291-
}
1292-
12931292
mqbc::ClusterNodeSession* ns =
12941293
d_clusterData_p->membership().getClusterNodeSession(
12951294
pinfo.primaryNode());

src/groups/mqb/mqbblp/mqbblp_queue.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,22 @@ void Queue::convertToLocalDispatched()
363363

364364
// PRECONDITIONS
365365
BSLS_ASSERT_SAFE(inDispatcherThread());
366+
367+
if (d_localQueue_mp) {
368+
BSLS_ASSERT_SAFE(!d_remoteQueue_mp);
369+
370+
BALL_LOG_INFO << d_state.uri() << ": has already converted to local "
371+
<< "[handlesCount: "
372+
<< d_state.handleCatalog().handlesCount()
373+
<< ", handle parameters: " << d_state.handleParameters()
374+
<< ", stream parameters: "
375+
<< d_state.subQueuesParameters() << "]";
376+
377+
return; // RETURN
378+
}
379+
366380
BSLS_ASSERT_SAFE(d_remoteQueue_mp);
381+
BSLS_ASSERT_SAFE(!d_localQueue_mp);
367382

368383
BALL_LOG_INFO << d_state.uri() << ": converting to local "
369384
<< "[handlesCount: "

0 commit comments

Comments
 (0)