mirror of https://github.com/postgres/postgres
To support the upcoming addition of a sequence synchronization worker, this patch extracts common synchronization logic shared by table sync workers and the new sequence sync worker into a dedicated file. This modularization improves code reuse, maintainability, and clarity in the logical workers framework. Author: vignesh C <vignesh21@gmail.com> Author: Hou Zhijie <houzj.fnst@fujitsu.com> Reviewed-by: shveta malik <shveta.malik@gmail.com> Reviewed-by: Dilip Kumar <dilipbalaut@gmail.com> Reviewed-by: Peter Smith <smithpb2250@gmail.com> Reviewed-by: Hayato Kuroda <kuroda.hayato@fujitsu.com> Reviewed-by: Chao Li <li.evan.chao@gmail.com> Reviewed-by: Amit Kapila <amit.kapila16@gmail.com> Discussion: https://postgr.es/m/CAA4eK1LC+KJiAkSrpE_NwvNdidw9F2os7GERUeSxSKv71gXysQ@mail.gmail.compull/245/head
parent
905e932f09
commit
41c674d2e3
@ -0,0 +1,187 @@ |
|||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
* syncutils.c |
||||||
|
* PostgreSQL logical replication: common synchronization code |
||||||
|
* |
||||||
|
* Copyright (c) 2025, PostgreSQL Global Development Group |
||||||
|
* |
||||||
|
* IDENTIFICATION |
||||||
|
* src/backend/replication/logical/syncutils.c |
||||||
|
* |
||||||
|
* NOTES |
||||||
|
* This file contains code common for synchronization workers. |
||||||
|
*------------------------------------------------------------------------- |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "postgres.h" |
||||||
|
|
||||||
|
#include "catalog/pg_subscription_rel.h" |
||||||
|
#include "pgstat.h" |
||||||
|
#include "replication/worker_internal.h" |
||||||
|
#include "storage/ipc.h" |
||||||
|
#include "utils/lsyscache.h" |
||||||
|
#include "utils/memutils.h" |
||||||
|
|
||||||
|
/*
|
||||||
|
* Enum for phases of the subscription relations state. |
||||||
|
* |
||||||
|
* SYNC_RELATIONS_STATE_NEEDS_REBUILD indicates that the subscription relations |
||||||
|
* state is no longer valid, and the subscription relations should be rebuilt. |
||||||
|
* |
||||||
|
* SYNC_RELATIONS_STATE_REBUILD_STARTED indicates that the subscription |
||||||
|
* relations state is being rebuilt. |
||||||
|
* |
||||||
|
* SYNC_RELATIONS_STATE_VALID indicates that the subscription relation state is |
||||||
|
* up-to-date and valid. |
||||||
|
*/ |
||||||
|
typedef enum |
||||||
|
{ |
||||||
|
SYNC_RELATIONS_STATE_NEEDS_REBUILD, |
||||||
|
SYNC_RELATIONS_STATE_REBUILD_STARTED, |
||||||
|
SYNC_RELATIONS_STATE_VALID, |
||||||
|
} SyncingRelationsState; |
||||||
|
|
||||||
|
static SyncingRelationsState relation_states_validity = SYNC_RELATIONS_STATE_NEEDS_REBUILD; |
||||||
|
|
||||||
|
/*
|
||||||
|
* Exit routine for synchronization worker. |
||||||
|
*/ |
||||||
|
pg_noreturn void |
||||||
|
FinishSyncWorker(void) |
||||||
|
{ |
||||||
|
/*
|
||||||
|
* Commit any outstanding transaction. This is the usual case, unless |
||||||
|
* there was nothing to do for the table. |
||||||
|
*/ |
||||||
|
if (IsTransactionState()) |
||||||
|
{ |
||||||
|
CommitTransactionCommand(); |
||||||
|
pgstat_report_stat(true); |
||||||
|
} |
||||||
|
|
||||||
|
/* And flush all writes. */ |
||||||
|
XLogFlush(GetXLogWriteRecPtr()); |
||||||
|
|
||||||
|
StartTransactionCommand(); |
||||||
|
ereport(LOG, |
||||||
|
(errmsg("logical replication table synchronization worker for subscription \"%s\", table \"%s\" has finished", |
||||||
|
MySubscription->name, |
||||||
|
get_rel_name(MyLogicalRepWorker->relid)))); |
||||||
|
CommitTransactionCommand(); |
||||||
|
|
||||||
|
/* Find the leader apply worker and signal it. */ |
||||||
|
logicalrep_worker_wakeup(MyLogicalRepWorker->subid, InvalidOid); |
||||||
|
|
||||||
|
/* Stop gracefully */ |
||||||
|
proc_exit(0); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Callback from syscache invalidation. |
||||||
|
*/ |
||||||
|
void |
||||||
|
InvalidateSyncingRelStates(Datum arg, int cacheid, uint32 hashvalue) |
||||||
|
{ |
||||||
|
relation_states_validity = SYNC_RELATIONS_STATE_NEEDS_REBUILD; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Process possible state change(s) of relations that are being synchronized. |
||||||
|
*/ |
||||||
|
void |
||||||
|
ProcessSyncingRelations(XLogRecPtr current_lsn) |
||||||
|
{ |
||||||
|
switch (MyLogicalRepWorker->type) |
||||||
|
{ |
||||||
|
case WORKERTYPE_PARALLEL_APPLY: |
||||||
|
|
||||||
|
/*
|
||||||
|
* Skip for parallel apply workers because they only operate on |
||||||
|
* tables that are in a READY state. See pa_can_start() and |
||||||
|
* should_apply_changes_for_rel(). |
||||||
|
*/ |
||||||
|
break; |
||||||
|
|
||||||
|
case WORKERTYPE_TABLESYNC: |
||||||
|
ProcessSyncingTablesForSync(current_lsn); |
||||||
|
break; |
||||||
|
|
||||||
|
case WORKERTYPE_APPLY: |
||||||
|
ProcessSyncingTablesForApply(current_lsn); |
||||||
|
break; |
||||||
|
|
||||||
|
case WORKERTYPE_UNKNOWN: |
||||||
|
/* Should never happen. */ |
||||||
|
elog(ERROR, "Unknown worker type"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Common code to fetch the up-to-date sync state info into the static lists. |
||||||
|
* |
||||||
|
* Returns true if subscription has 1 or more tables, else false. |
||||||
|
* |
||||||
|
* Note: If this function started the transaction (indicated by the parameter) |
||||||
|
* then it is the caller's responsibility to commit it. |
||||||
|
*/ |
||||||
|
bool |
||||||
|
FetchRelationStates(bool *started_tx) |
||||||
|
{ |
||||||
|
static bool has_subtables = false; |
||||||
|
|
||||||
|
*started_tx = false; |
||||||
|
|
||||||
|
if (relation_states_validity != SYNC_RELATIONS_STATE_VALID) |
||||||
|
{ |
||||||
|
MemoryContext oldctx; |
||||||
|
List *rstates; |
||||||
|
ListCell *lc; |
||||||
|
SubscriptionRelState *rstate; |
||||||
|
|
||||||
|
relation_states_validity = SYNC_RELATIONS_STATE_REBUILD_STARTED; |
||||||
|
|
||||||
|
/* Clean the old lists. */ |
||||||
|
list_free_deep(table_states_not_ready); |
||||||
|
table_states_not_ready = NIL; |
||||||
|
|
||||||
|
if (!IsTransactionState()) |
||||||
|
{ |
||||||
|
StartTransactionCommand(); |
||||||
|
*started_tx = true; |
||||||
|
} |
||||||
|
|
||||||
|
/* Fetch tables and sequences that are in non-ready state. */ |
||||||
|
rstates = GetSubscriptionRelations(MySubscription->oid, true); |
||||||
|
|
||||||
|
/* Allocate the tracking info in a permanent memory context. */ |
||||||
|
oldctx = MemoryContextSwitchTo(CacheMemoryContext); |
||||||
|
foreach(lc, rstates) |
||||||
|
{ |
||||||
|
rstate = palloc(sizeof(SubscriptionRelState)); |
||||||
|
memcpy(rstate, lfirst(lc), sizeof(SubscriptionRelState)); |
||||||
|
table_states_not_ready = lappend(table_states_not_ready, rstate); |
||||||
|
} |
||||||
|
MemoryContextSwitchTo(oldctx); |
||||||
|
|
||||||
|
/*
|
||||||
|
* Does the subscription have tables? |
||||||
|
* |
||||||
|
* If there were not-READY tables found then we know it does. But if |
||||||
|
* table_states_not_ready was empty we still need to check again to |
||||||
|
* see if there are 0 tables. |
||||||
|
*/ |
||||||
|
has_subtables = (table_states_not_ready != NIL) || |
||||||
|
HasSubscriptionTables(MySubscription->oid); |
||||||
|
|
||||||
|
/*
|
||||||
|
* If the subscription relation cache has been invalidated since we |
||||||
|
* entered this routine, we still use and return the relations we just |
||||||
|
* finished constructing, to avoid infinite loops, but we leave the |
||||||
|
* table states marked as stale so that we'll rebuild it again on next |
||||||
|
* access. Otherwise, we mark the table states as valid. |
||||||
|
*/ |
||||||
|
if (relation_states_validity == SYNC_RELATIONS_STATE_REBUILD_STARTED) |
||||||
|
relation_states_validity = SYNC_RELATIONS_STATE_VALID; |
||||||
|
} |
||||||
|
|
||||||
|
return has_subtables; |
||||||
|
} |
||||||
Loading…
Reference in new issue