Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
268 changes: 142 additions & 126 deletions src/groups/mqb/mqbblp/mqbblp_clusterqueuehelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -765,14 +765,12 @@ void ClusterQueueHelper::onQueueContextAssigned(

haveActivePrimary = false;
}
else if (!d_clusterState_p->isSelfPrimary(pid)) {
// This is a replica node, guaranteed.

// Note: It's possible that the queue has already been registered
// in the StorageMgr if it was a queue found during storage
// recovery. Therefore, we will allow for duplicate registration
// which will simply result in a no-op.

// Register queue storage on replicas regardless of primary status.
// The storage must exist in FileStore before the primary sends QLIST
// records; otherwise writeQueueCreationRecord fails with
// rc_QUEUE_CREATION_FAILURE. Duplicate registration is a no-op.
if (!d_clusterState_p->isSelfPrimary(pid)) {
const mqbc::ClusterStateQueueInfo& info =
*queueContext->d_stateQInfo_sp;

Expand Down Expand Up @@ -3850,134 +3848,152 @@ void ClusterQueueHelper::restoreStateCluster(int partitionId)

continue; // CONTINUE
}
}
else {
// A specific partitionId is specified. Attempt to re-issue
// open-queue requests for all appropriate queues assigned to that
// partition.

if (queueContext->partitionId() != partitionId) {
// Skip the queue as its assigned to a different partitionId.
continue; // CONTINUE;
// Proceed as if a specific partitionId is specified.
partitionId = queueContext->partitionId();
pinfo = &(d_clusterState_p->partition(partitionId));

if (!hasActiveAvailablePrimary(partitionId)) {
BMQ_LOGTHROTTLE_INFO
<< d_cluster_p->description()
<< " Not performing restore of queue ["
<< queueContext->uri()
<< "] because there is no primary or primary isn't "
"ACTIVE. Current primary: "
<< (pinfo->primaryNode()
? pinfo->primaryNode()->nodeDescription()
: "** null **")
<< ", primary status: " << pinfo->primaryStatus();
continue; // CONTINUE
}
isSelfPrimaryAndLeader =
pinfo->primaryNode() ==
d_clusterData_p->membership().selfNode() &&
d_clusterData_p->electorInfo().isSelfLeader();
}
else if (queueContext->partitionId() != partitionId) {
// Skip the queue as its assigned to a different partitionId.
continue; // CONTINUE;
}

BSLS_ASSERT_SAFE(isQueueAssigned(*queueContext));
BSLS_ASSERT_SAFE(isQueuePrimaryAvailable(*queueContext));

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

// Verify the CSL if needed by comparing it with the Domain config
if (liveQInfo.d_queue_sp) {
if (isSelfPrimaryAndLeader) {
// We are assuming that it is not possible for a node to be
// primary, lose primary-ship and regain primary-ship;
// unless eventually the node went down in which case it
// will start from fresh.

// Moreover, since self node is now the primary, it is
// important for it to register the queue with the
// StorageManager. This is logically equivalent to
// registering the queue with StorageManager when a primary
// node creates a local queue instance (see
// 'createQueueFactory').

bsl::vector<bsl::string> added(d_allocator_p);
bsl::vector<bsl::string> removed(d_allocator_p);
mqbi::Domain* domain = liveQInfo.d_queue_sp->domain();

match(&added,
&removed,
*queueContext->d_stateQInfo_sp,
domain->config().mode());

if (!removed.empty() || !added.empty()) {
VoidFunctor park = bdlf::BindUtil::bindS(
d_allocator_p,
&ClusterQueueHelper::convertToLocal,
this,
queueContext,
domain);

// Add to 'd_pendingUpdates' before calling
// 'updateAppIds' which is asynchronous (CSL commit)
liveQInfo.d_pendingUpdates.push_back(park);

mqbi::ClusterErrorCode::Enum result =
d_clusterStateManager_p->updateAppIds(
added,
removed,
domain->name(),
"");

if (mqbi::ClusterErrorCode::e_OK == result) {
// Cannot continue until 'onQueueUpdated'
// Send QueueUpdateAdvisory and _wait_ for commit

continue; // CONTINUE
}

// An update error is CSL error (in
// 'ClusterStateLedger::apply'). This queue cannot
// convertToLocal
// ('RootQueueEngine::initializeAppId' would assert
// if there is no storage for some app).

BSLS_ASSERT_SAFE(
false &&
"Failure to update Apps before convertToLocal");
}
else {
convertToLocal(queueContext, domain);
// Attempt to re-issue open-queue requests for all appropriate queues
// assigned to that partition.

BSLS_ASSERT_SAFE(isQueueAssigned(*queueContext));
BSLS_ASSERT_SAFE(isQueuePrimaryAvailable(*queueContext));

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

// Verify the CSL if needed by comparing it with the Domain config
if (liveQInfo.d_queue_sp) {
if (isSelfPrimaryAndLeader) {
// We are assuming that it is not possible for a node to be
// primary, lose primary-ship and regain primary-ship;
// unless eventually the node went down in which case it
// will start from fresh.

// Moreover, since self node is now the primary, it is
// important for it to register the queue with the
// StorageManager. This is logically equivalent to
// registering the queue with StorageManager when a primary
// node creates a local queue instance (see
// 'createQueueFactory').

bsl::vector<bsl::string> added(d_allocator_p);
bsl::vector<bsl::string> removed(d_allocator_p);
mqbi::Domain* domain = liveQInfo.d_queue_sp->domain();

match(&added,
&removed,
*queueContext->d_stateQInfo_sp,
domain->config().mode());

if (!removed.empty() || !added.empty()) {
VoidFunctor park = bdlf::BindUtil::bindS(
d_allocator_p,
&ClusterQueueHelper::convertToLocal,
this,
queueContext,
domain);

// Add to 'd_pendingUpdates' before calling
// 'updateAppIds' which is asynchronous (CSL commit)
liveQInfo.d_pendingUpdates.push_back(park);

mqbi::ClusterErrorCode::Enum result =
d_clusterStateManager_p->updateAppIds(added,
removed,
domain->name(),
"");

if (mqbi::ClusterErrorCode::e_OK == result) {
// Cannot continue until 'onQueueUpdated'
// Send QueueUpdateAdvisory and _wait_ for commit

continue; // CONTINUE
}

// An update error is CSL error (in
// 'ClusterStateLedger::apply'). This queue cannot
// convertToLocal
// ('RootQueueEngine::initializeAppId' would assert
// if there is no storage for some app).

BSLS_ASSERT_SAFE(
false &&
"Failure to update Apps before convertToLocal");
}
else {
if (queueContext->d_liveQInfo.d_numQueueHandles != 0) {
// In the case of a cluster member, queues are deleted
// 'lazily' when receiving a notification from the
// primary. This replica may have fully closed the
// queue, but the queue has not been deleted by the
// primary if another replica still uses it; however
// from this replica's perspective, we don't want to
// reopen the queue.
const bmqt::GenericResult::Enum rc =
restoreStateHelper(queueContext.get(),
pinfo->primaryNode(),
cycle);

if (rc == bmqt::GenericResult::e_NOT_CONNECTED) {
// Abort restore of the state: the channel is no
// longer valid or we hit high water mark. For the
// case of invalid channel, we'll wait for a new
// one to be active and will restart restoring the
// state from the beginning.
return; // RETURN
}
// In case of other type of failure, just continue
// processing other queues instead of stopping the
// 'state restore' sequence.

// REVISIT: this code sends pending Open Queue requests
// without waiting for the Reopen Queue Response.
}
else {
BMQ_LOGTHROTTLE_INFO
<< d_cluster_p->description()
<< ": Skipping restore of " << queueContext->uri()
<< " because it has no active queue handles";
convertToLocal(queueContext, domain);
}
}
else {
if (queueContext->d_liveQInfo.d_numQueueHandles != 0) {
const bmqt::GenericResult::Enum rc = restoreStateHelper(
queueContext.get(),
pinfo->primaryNode(),
cycle);

if (rc == bmqt::GenericResult::e_NOT_CONNECTED) {
// Abort restore of the state: the channel is no
// longer valid or we hit high water mark. For the
// case of invalid channel, we'll wait for a new
// one to be active and will restart restoring the
// state from the beginning.
return; // RETURN
}
// In case of other type of failure, just continue
// processing other queues instead of stopping the
// 'state restore' sequence.

// We also need to issue requests for any pending contexts:
// when a primary fails over, the queue may have been
// already open on this node, and all clients which were
// connected to the old primary will immediately reconnect,
// some might connect to this node and will issue an open
// queue. Because primary just got lost, those open queue
// requests were not processed, but appended to the pending
// context list, so once we have an active primary, we
// should process them.
// REVISIT: this code sends pending Open Queue requests
// without waiting for the Reopen Queue Response.
}
else {
// In the case of a cluster member, queues are deleted
// 'lazily' when receiving a notification from the
// primary. This replica may have fully closed the
// queue, but the queue has not been deleted by the
// primary if another replica still uses it; however
// from this replica's perspective, we don't want to
// reopen the queue.
BMQ_LOGTHROTTLE_INFO
<< d_cluster_p->description()
<< ": Skipping restore of " << queueContext->uri()
<< " because it has no active queue handles";
}

// We also need to issue requests for any pending contexts:
// when a primary fails over, the queue may have been
// already open on this node, and all clients which were
// connected to the old primary will immediately reconnect,
// some might connect to this node and will issue an open
// queue. Because primary just got lost, those open queue
// requests were not processed, but appended to the pending
// context list, so once we have an active primary, we
// should process them.
}
// else, Queue instance is not created, but the queue is assigned.
// Proceed ahead.
Expand Down
4 changes: 0 additions & 4 deletions src/groups/mqb/mqbblp/mqbblp_clusterqueuehelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -1286,10 +1286,6 @@ inline bool ClusterQueueHelper::hasActiveAvailablePrimary(
return false; // RETURN
}

if (d_cluster_p->isFSMWorkflow()) {
return true; // RETURN
}

mqbc::ClusterNodeSession* ns =
d_clusterData_p->membership().getClusterNodeSession(
pinfo.primaryNode());
Expand Down
15 changes: 15 additions & 0 deletions src/groups/mqb/mqbblp/mqbblp_queue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,22 @@ void Queue::convertToLocalDispatched()

// PRECONDITIONS
BSLS_ASSERT_SAFE(inDispatcherThread());

if (d_localQueue_mp) {
BSLS_ASSERT_SAFE(!d_remoteQueue_mp);

BALL_LOG_INFO << d_state.uri() << ": has already converted to local "
<< "[handlesCount: "
<< d_state.handleCatalog().handlesCount()
<< ", handle parameters: " << d_state.handleParameters()
<< ", stream parameters: "
<< d_state.subQueuesParameters() << "]";

return; // RETURN
}

BSLS_ASSERT_SAFE(d_remoteQueue_mp);
BSLS_ASSERT_SAFE(!d_localQueue_mp);

BALL_LOG_INFO << d_state.uri() << ": converting to local "
<< "[handlesCount: "
Expand Down
Loading