|
|
|
@ -84,6 +84,8 @@ typedef struct NodeMetadata
|
|
|
|
|
bool isActive;
|
|
|
|
|
Oid nodeRole;
|
|
|
|
|
bool shouldHaveShards;
|
|
|
|
|
uint32 nodeprimarynodeid;
|
|
|
|
|
bool nodeisreplica;
|
|
|
|
|
char *nodeCluster;
|
|
|
|
|
} NodeMetadata;
|
|
|
|
|
|
|
|
|
@ -120,7 +122,6 @@ static char * NodeMetadataSyncedUpdateCommand(uint32 nodeId, bool metadataSynced
|
|
|
|
|
static void ErrorIfCoordinatorMetadataSetFalse(WorkerNode *workerNode, Datum value,
|
|
|
|
|
char *field);
|
|
|
|
|
static WorkerNode * SetShouldHaveShards(WorkerNode *workerNode, bool shouldHaveShards);
|
|
|
|
|
static WorkerNode * FindNodeAnyClusterByNodeId(uint32 nodeId);
|
|
|
|
|
static void ErrorIfAnyNodeNotExist(List *nodeList);
|
|
|
|
|
static void UpdateLocalGroupIdsViaMetadataContext(MetadataSyncContext *context);
|
|
|
|
|
static void SendDeletionCommandsForReplicatedTablePlacements(
|
|
|
|
@ -140,6 +141,10 @@ static BackgroundWorkerHandle * CheckBackgroundWorkerToObtainLocks(int32 lock_co
|
|
|
|
|
static BackgroundWorkerHandle * LockPlacementsWithBackgroundWorkersInPrimaryNode(
|
|
|
|
|
WorkerNode *workerNode, bool force, int32 lock_cooldown);
|
|
|
|
|
|
|
|
|
|
static void EnsureValidStreamingReplica(WorkerNode *primaryWorkerNode,
|
|
|
|
|
char* replicaHostname,
|
|
|
|
|
int replicaPort);
|
|
|
|
|
|
|
|
|
|
/* Function definitions go here */
|
|
|
|
|
|
|
|
|
|
/* declarations for dynamic loading */
|
|
|
|
@ -168,6 +173,7 @@ PG_FUNCTION_INFO_V1(citus_coordinator_nodeid);
|
|
|
|
|
PG_FUNCTION_INFO_V1(citus_is_coordinator);
|
|
|
|
|
PG_FUNCTION_INFO_V1(citus_internal_mark_node_not_synced);
|
|
|
|
|
PG_FUNCTION_INFO_V1(citus_is_primary_node);
|
|
|
|
|
PG_FUNCTION_INFO_V1(citus_add_replica_node);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* DefaultNodeMetadata creates a NodeMetadata struct with the fields set to
|
|
|
|
@ -183,6 +189,8 @@ DefaultNodeMetadata()
|
|
|
|
|
nodeMetadata.nodeRack = WORKER_DEFAULT_RACK;
|
|
|
|
|
nodeMetadata.shouldHaveShards = true;
|
|
|
|
|
nodeMetadata.groupId = INVALID_GROUP_ID;
|
|
|
|
|
nodeMetadata.nodeisreplica = false;
|
|
|
|
|
nodeMetadata.nodeprimarynodeid = 0; /* 0 typically means InvalidNodeId */
|
|
|
|
|
|
|
|
|
|
return nodeMetadata;
|
|
|
|
|
}
|
|
|
|
@ -1422,6 +1430,149 @@ master_update_node(PG_FUNCTION_ARGS)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* citus_add_replica_node function adds a new node as a replica of an existing primary node.
|
|
|
|
|
* It records the replica's hostname, port, and links it to the primary node's ID.
|
|
|
|
|
* The replica is initially marked as inactive and not having shards.
|
|
|
|
|
*/
|
|
|
|
|
Datum
|
|
|
|
|
citus_add_replica_node(PG_FUNCTION_ARGS)
|
|
|
|
|
{
|
|
|
|
|
CheckCitusVersion(ERROR);
|
|
|
|
|
EnsureSuperUser();
|
|
|
|
|
EnsureCoordinator();
|
|
|
|
|
|
|
|
|
|
text *replicaHostnameText = PG_GETARG_TEXT_P(0);
|
|
|
|
|
int32 replicaPort = PG_GETARG_INT32(1);
|
|
|
|
|
text *primaryHostnameText = PG_GETARG_TEXT_P(2);
|
|
|
|
|
int32 primaryPort = PG_GETARG_INT32(3);
|
|
|
|
|
|
|
|
|
|
char *replicaHostname = text_to_cstring(replicaHostnameText);
|
|
|
|
|
char *primaryHostname = text_to_cstring(primaryHostnameText);
|
|
|
|
|
|
|
|
|
|
WorkerNode *primaryWorkerNode = FindWorkerNodeAnyCluster(primaryHostname, primaryPort);
|
|
|
|
|
|
|
|
|
|
if (primaryWorkerNode == NULL)
|
|
|
|
|
{
|
|
|
|
|
ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
|
|
|
|
errmsg("primary node %s:%d not found in pg_dist_node",
|
|
|
|
|
primaryHostname, primaryPort)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Future-proofing: Ideally, a primary node should not itself be a replica.
|
|
|
|
|
* This check might be more relevant once replica promotion logic exists.
|
|
|
|
|
* For now, pg_dist_node.nodeisreplica defaults to false for existing nodes.
|
|
|
|
|
*/
|
|
|
|
|
if (primaryWorkerNode->nodeisreplica)
|
|
|
|
|
{
|
|
|
|
|
ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
|
|
|
|
errmsg("primary node %s:%d is itself a replica and cannot have replicas",
|
|
|
|
|
primaryHostname, primaryPort)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!primaryWorkerNode->shouldHaveShards)
|
|
|
|
|
{
|
|
|
|
|
ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
|
|
|
|
errmsg("primary node %s:%d does not have shards, node without shards cannot have replicas",
|
|
|
|
|
primaryHostname, primaryPort)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WorkerNode *existingReplicaNode = FindWorkerNodeAnyCluster(replicaHostname, replicaPort);
|
|
|
|
|
if (existingReplicaNode != NULL)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* Idempotency check: If the node already exists, is it already correctly
|
|
|
|
|
* registered as a replica for THIS primary?
|
|
|
|
|
*/
|
|
|
|
|
if (existingReplicaNode->nodeisreplica &&
|
|
|
|
|
existingReplicaNode->nodeprimarynodeid == primaryWorkerNode->nodeId)
|
|
|
|
|
{
|
|
|
|
|
ereport(NOTICE, (errmsg("node %s:%d is already registered as a replica for primary %s:%d (nodeid %d)",
|
|
|
|
|
replicaHostname, replicaPort,
|
|
|
|
|
primaryHostname, primaryPort, primaryWorkerNode->nodeId)));
|
|
|
|
|
PG_RETURN_INT32(existingReplicaNode->nodeId);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
|
|
|
|
errmsg("a different node %s:%d (nodeid %d) already exists or is a replica for a different primary",
|
|
|
|
|
replicaHostname, replicaPort, existingReplicaNode->nodeId)));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
EnsureValidStreamingReplica(primaryWorkerNode, replicaHostname, replicaPort);
|
|
|
|
|
|
|
|
|
|
NodeMetadata nodeMetadata = DefaultNodeMetadata();
|
|
|
|
|
|
|
|
|
|
nodeMetadata.nodeisreplica = true;
|
|
|
|
|
nodeMetadata.nodeprimarynodeid = primaryWorkerNode->nodeId;
|
|
|
|
|
nodeMetadata.isActive = false; /* Replicas start as inactive */
|
|
|
|
|
nodeMetadata.shouldHaveShards = false; /* Replicas do not directly own primary shards */
|
|
|
|
|
nodeMetadata.groupId = primaryWorkerNode->groupId; /* Same group as primary */
|
|
|
|
|
nodeMetadata.nodeRole = UnavailableNodeRoleId(); /* The node role is set to 'unavailable' */
|
|
|
|
|
nodeMetadata.nodeCluster = primaryWorkerNode->nodeCluster; /* Same cluster as primary */
|
|
|
|
|
/* Other fields like hasMetadata, metadataSynced will take defaults from DefaultNodeMetadata
|
|
|
|
|
* (typically true, true for hasMetadata and metadataSynced if it's a new node,
|
|
|
|
|
* or might need adjustment based on replica strategy)
|
|
|
|
|
* For now, let's assume DefaultNodeMetadata provides suitable defaults for these
|
|
|
|
|
* or they will be set by AddNodeMetadata/ActivateNodeList if needed.
|
|
|
|
|
* Specifically, hasMetadata is often true, and metadataSynced true after activation.
|
|
|
|
|
* Since this replica is inactive, metadata sync status might be less critical initially.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
bool nodeAlreadyExists = false;
|
|
|
|
|
bool localOnly = false; /* Propagate change to other workers with metadata */
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* AddNodeMetadata will take an ExclusiveLock on pg_dist_node.
|
|
|
|
|
* It also checks again if the node already exists after acquiring the lock.
|
|
|
|
|
*/
|
|
|
|
|
int replicaNodeId = AddNodeMetadata(replicaHostname, replicaPort, &nodeMetadata,
|
|
|
|
|
&nodeAlreadyExists, localOnly);
|
|
|
|
|
|
|
|
|
|
if (nodeAlreadyExists)
|
|
|
|
|
{
|
|
|
|
|
/* This case should ideally be caught by the FindWorkerNodeAnyCluster check above,
|
|
|
|
|
* but AddNodeMetadata does its own check after locking.
|
|
|
|
|
* If it already exists and is correctly configured, we might have returned NOTICE above.
|
|
|
|
|
* If it exists but is NOT correctly configured as our replica, an ERROR would be more appropriate.
|
|
|
|
|
* AddNodeMetadata returns the existing node's ID if it finds one.
|
|
|
|
|
* We need to ensure it is the *correct* replica.
|
|
|
|
|
*/
|
|
|
|
|
WorkerNode *fetchedExistingNode = FindNodeAnyClusterByNodeId(replicaNodeId);
|
|
|
|
|
if (fetchedExistingNode != NULL && fetchedExistingNode->nodeisreplica &&
|
|
|
|
|
fetchedExistingNode->nodeprimarynodeid == primaryWorkerNode->nodeId)
|
|
|
|
|
{
|
|
|
|
|
ereport(NOTICE, (errmsg("node %s:%d was already correctly registered as a replica for primary %s:%d (nodeid %d)",
|
|
|
|
|
replicaHostname, replicaPort,
|
|
|
|
|
primaryHostname, primaryPort, primaryWorkerNode->nodeId)));
|
|
|
|
|
/* Intentional fall-through to return replicaNodeId */
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* This state is less expected if our initial check passed or errored. */
|
|
|
|
|
ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR),
|
|
|
|
|
errmsg("node %s:%d already exists but is not correctly configured as a replica for primary %s:%d",
|
|
|
|
|
replicaHostname, replicaPort, primaryHostname, primaryPort)));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TransactionModifiedNodeMetadata = true;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Note: Replicas added this way are inactive.
|
|
|
|
|
* A separate mechanism or UDF (e.g., citus_activate_replica_node or citus_promote_replica_node)
|
|
|
|
|
* would be needed to activate them, potentially after verifying replication lag, etc.
|
|
|
|
|
* Activation would typically involve:
|
|
|
|
|
* 1. Setting isActive = true.
|
|
|
|
|
* 2. Ensuring metadata is synced (hasMetadata=true, metadataSynced=true).
|
|
|
|
|
* 3. Potentially other logic like adding to specific scheduler lists.
|
|
|
|
|
* For now, citus_add_node_replica just registers it.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
PG_RETURN_INT32(replicaNodeId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* SetLockTimeoutLocally sets the lock_timeout to the given value.
|
|
|
|
|
* This setting is local.
|
|
|
|
@ -1871,7 +2022,7 @@ FindWorkerNodeAnyCluster(const char *nodeName, int32 nodePort)
|
|
|
|
|
* FindNodeAnyClusterByNodeId searches pg_dist_node and returns the node with
|
|
|
|
|
* the nodeId. If the node can't be found returns NULL.
|
|
|
|
|
*/
|
|
|
|
|
static WorkerNode *
|
|
|
|
|
WorkerNode *
|
|
|
|
|
FindNodeAnyClusterByNodeId(uint32 nodeId)
|
|
|
|
|
{
|
|
|
|
|
bool includeNodesFromOtherClusters = true;
|
|
|
|
@ -2924,6 +3075,10 @@ InsertNodeRow(int nodeid, char *nodeName, int32 nodePort, NodeMetadata *nodeMeta
|
|
|
|
|
values[Anum_pg_dist_node_nodecluster - 1] = nodeClusterNameDatum;
|
|
|
|
|
values[Anum_pg_dist_node_shouldhaveshards - 1] = BoolGetDatum(
|
|
|
|
|
nodeMetadata->shouldHaveShards);
|
|
|
|
|
values[Anum_pg_dist_node_nodeisreplica - 1] = BoolGetDatum(
|
|
|
|
|
nodeMetadata->nodeisreplica);
|
|
|
|
|
values[Anum_pg_dist_node_nodeprimarynodeid - 1] = Int32GetDatum(
|
|
|
|
|
nodeMetadata->nodeprimarynodeid);
|
|
|
|
|
|
|
|
|
|
Relation pgDistNode = table_open(DistNodeRelationId(), RowExclusiveLock);
|
|
|
|
|
|
|
|
|
@ -3014,8 +3169,7 @@ TupleToWorkerNode(TupleDesc tupleDescriptor, HeapTuple heapTuple)
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* This function can be called before "ALTER TABLE ... ADD COLUMN nodecluster ...",
|
|
|
|
|
* therefore heap_deform_tuple() won't set the isNullArray for this column. We
|
|
|
|
|
* initialize it true to be safe in that case.
|
|
|
|
|
* and other columns. We initialize isNullArray to true to be safe.
|
|
|
|
|
*/
|
|
|
|
|
memset(isNullArray, true, sizeof(isNullArray));
|
|
|
|
|
|
|
|
|
@ -3043,6 +3197,25 @@ TupleToWorkerNode(TupleDesc tupleDescriptor, HeapTuple heapTuple)
|
|
|
|
|
datumArray[Anum_pg_dist_node_shouldhaveshards -
|
|
|
|
|
1]);
|
|
|
|
|
|
|
|
|
|
/* nodeisreplica and nodeprimarynodeid might be null if row is from older version */
|
|
|
|
|
if (!isNullArray[Anum_pg_dist_node_nodeisreplica - 1])
|
|
|
|
|
{
|
|
|
|
|
workerNode->nodeisreplica = DatumGetBool(datumArray[Anum_pg_dist_node_nodeisreplica - 1]);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
workerNode->nodeisreplica = false; /* Default value */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!isNullArray[Anum_pg_dist_node_nodeprimarynodeid - 1])
|
|
|
|
|
{
|
|
|
|
|
workerNode->nodeprimarynodeid = DatumGetInt32(datumArray[Anum_pg_dist_node_nodeprimarynodeid - 1]);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
workerNode->nodeprimarynodeid = 0; /* Default value for InvalidNodeId */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* nodecluster column can be missing. In the case of extension creation/upgrade,
|
|
|
|
|
* master_initialize_node_metadata function is called before the nodecluster
|
|
|
|
@ -3285,3 +3458,173 @@ SyncNodeMetadata(MetadataSyncContext *context)
|
|
|
|
|
*/
|
|
|
|
|
SendOrCollectCommandListToActivatedNodes(context, recreateNodeSnapshotCommandList);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* EnsureValidStreamingReplica checks if the given replica is a valid streaming
|
|
|
|
|
* replica. It connects to the replica and checks if it is in recovery mode.
|
|
|
|
|
* If it is not, it errors out.
|
|
|
|
|
* It also checks the system identifier of the replica and the primary
|
|
|
|
|
* to ensure they match.
|
|
|
|
|
*/
|
|
|
|
|
static void
|
|
|
|
|
EnsureValidStreamingReplica(WorkerNode *primaryWorkerNode, char* replicaHostname, int replicaPort)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
int connectionFlag = FORCE_NEW_CONNECTION;
|
|
|
|
|
MultiConnection *replicaConnection = GetNodeConnection(connectionFlag, replicaHostname,
|
|
|
|
|
replicaPort);
|
|
|
|
|
|
|
|
|
|
const char *replica_recovery_query = "SELECT pg_is_in_recovery()";
|
|
|
|
|
|
|
|
|
|
int resultCode = SendRemoteCommand(replicaConnection, replica_recovery_query);
|
|
|
|
|
|
|
|
|
|
if (resultCode == 0)
|
|
|
|
|
{
|
|
|
|
|
CloseConnection(replicaConnection);
|
|
|
|
|
ereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE),
|
|
|
|
|
errmsg("could not connect to %s:%d to execute pg_is_in_recovery()",
|
|
|
|
|
replicaHostname, replicaPort)));
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PGresult *result = GetRemoteCommandResult(replicaConnection, true);
|
|
|
|
|
|
|
|
|
|
if (result == NULL)
|
|
|
|
|
{
|
|
|
|
|
CloseConnection(replicaConnection);
|
|
|
|
|
|
|
|
|
|
ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
|
|
|
|
errmsg("node %s:%d does not support pg_is_in_recovery()",
|
|
|
|
|
replicaHostname, replicaPort)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
List *sizeList = ReadFirstColumnAsText(result);
|
|
|
|
|
if (list_length(sizeList) != 1)
|
|
|
|
|
{
|
|
|
|
|
PQclear(result);
|
|
|
|
|
ClearResults(replicaConnection, true);
|
|
|
|
|
CloseConnection(replicaConnection);
|
|
|
|
|
|
|
|
|
|
ereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE),
|
|
|
|
|
errmsg("cannot parse pg_is_in_recovery() result from %s:%d",
|
|
|
|
|
replicaHostname,
|
|
|
|
|
replicaPort)));
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
StringInfo isInRecoveryQueryResInfo = (StringInfo) linitial(sizeList);
|
|
|
|
|
char *isInRecoveryQueryResStr = isInRecoveryQueryResInfo->data;
|
|
|
|
|
|
|
|
|
|
if (strcmp(isInRecoveryQueryResStr, "t") != 0 && strcmp(isInRecoveryQueryResStr, "true") != 0)
|
|
|
|
|
{
|
|
|
|
|
PQclear(result);
|
|
|
|
|
ClearResults(replicaConnection, true);
|
|
|
|
|
CloseConnection(replicaConnection);
|
|
|
|
|
|
|
|
|
|
ereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE),
|
|
|
|
|
errmsg("%s:%d is not a streaming replica",
|
|
|
|
|
replicaHostname,
|
|
|
|
|
replicaPort)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PQclear(result);
|
|
|
|
|
ForgetResults(replicaConnection);
|
|
|
|
|
|
|
|
|
|
/* Step2: Get the system identifier from replica */
|
|
|
|
|
const char *sysidQuery =
|
|
|
|
|
"SELECT system_identifier FROM pg_control_system()";
|
|
|
|
|
|
|
|
|
|
resultCode = SendRemoteCommand(replicaConnection, sysidQuery);
|
|
|
|
|
|
|
|
|
|
if (resultCode == 0)
|
|
|
|
|
{
|
|
|
|
|
CloseConnection(replicaConnection);
|
|
|
|
|
ereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE),
|
|
|
|
|
errmsg("could not connect to %s:%d to get system identifier",
|
|
|
|
|
replicaHostname, replicaPort)));
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result = GetRemoteCommandResult(replicaConnection, true);
|
|
|
|
|
if (!IsResponseOK(result))
|
|
|
|
|
{
|
|
|
|
|
ReportResultError(replicaConnection, result, ERROR);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
List *sysidList = ReadFirstColumnAsText(result);
|
|
|
|
|
if (list_length(sysidList) != 1)
|
|
|
|
|
{
|
|
|
|
|
PQclear(result);
|
|
|
|
|
ClearResults(replicaConnection, true);
|
|
|
|
|
CloseConnection(replicaConnection);
|
|
|
|
|
|
|
|
|
|
ereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE),
|
|
|
|
|
errmsg("cannot parse get system identifier result from %s:%d",
|
|
|
|
|
replicaHostname,
|
|
|
|
|
replicaPort)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
StringInfo sysidQueryResInfo = (StringInfo) linitial(sysidList);
|
|
|
|
|
char *sysidQueryResStr = sysidQueryResInfo->data;
|
|
|
|
|
|
|
|
|
|
ereport (NOTICE, (errmsg("system identifier of %s:%d is %s",
|
|
|
|
|
replicaHostname, replicaPort, sysidQueryResStr)));
|
|
|
|
|
|
|
|
|
|
/* We do not need the connection anymore */
|
|
|
|
|
PQclear(result);
|
|
|
|
|
ForgetResults(replicaConnection);
|
|
|
|
|
CloseConnection(replicaConnection);
|
|
|
|
|
|
|
|
|
|
/* Step3: Get system identifier from primary */
|
|
|
|
|
int primaryConnectionFlag = 0;
|
|
|
|
|
MultiConnection *primaryConnection = GetNodeConnection(primaryConnectionFlag,
|
|
|
|
|
primaryWorkerNode->workerName,
|
|
|
|
|
primaryWorkerNode->workerPort);
|
|
|
|
|
int primaryResultCode = SendRemoteCommand(primaryConnection, sysidQuery);
|
|
|
|
|
if (primaryResultCode == 0)
|
|
|
|
|
{
|
|
|
|
|
CloseConnection(primaryConnection);
|
|
|
|
|
ereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE),
|
|
|
|
|
errmsg("could not connect to %s:%d to get system identifier",
|
|
|
|
|
primaryWorkerNode->workerName, primaryWorkerNode->workerPort)));
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
PGresult *primaryResult = GetRemoteCommandResult(primaryConnection, true);
|
|
|
|
|
if (primaryResult == NULL)
|
|
|
|
|
{
|
|
|
|
|
CloseConnection(primaryConnection);
|
|
|
|
|
|
|
|
|
|
ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
|
|
|
|
errmsg("node %s:%d does not support pg_control_system queries",
|
|
|
|
|
primaryWorkerNode->workerName, primaryWorkerNode->workerPort)));
|
|
|
|
|
}
|
|
|
|
|
List *primarySizeList = ReadFirstColumnAsText(primaryResult);
|
|
|
|
|
if (list_length(primarySizeList) != 1)
|
|
|
|
|
{
|
|
|
|
|
PQclear(primaryResult);
|
|
|
|
|
ClearResults(primaryConnection, true);
|
|
|
|
|
CloseConnection(primaryConnection);
|
|
|
|
|
|
|
|
|
|
ereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE),
|
|
|
|
|
errmsg("cannot parse get system identifier result from %s:%d",
|
|
|
|
|
primaryWorkerNode->workerName,
|
|
|
|
|
primaryWorkerNode->workerPort)));
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
StringInfo primarySysidQueryResInfo = (StringInfo) linitial(primarySizeList);
|
|
|
|
|
char *primarySysidQueryResStr = primarySysidQueryResInfo->data;
|
|
|
|
|
|
|
|
|
|
ereport (NOTICE, (errmsg("system identifier of %s:%d is %s",
|
|
|
|
|
primaryWorkerNode->workerName, primaryWorkerNode->workerPort, primarySysidQueryResStr)));
|
|
|
|
|
/* verify both identifiers */
|
|
|
|
|
if (strcmp(sysidQueryResStr, primarySysidQueryResStr) != 0)
|
|
|
|
|
{
|
|
|
|
|
ereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE),
|
|
|
|
|
errmsg("system identifiers do not match: %s (replica) vs %s (primary)",
|
|
|
|
|
sysidQueryResStr, primarySysidQueryResStr)));
|
|
|
|
|
}
|
|
|
|
|
PQclear(primaryResult);
|
|
|
|
|
ClearResults(primaryConnection, true);
|
|
|
|
|
CloseConnection(primaryConnection);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|