Organizations utilizing Debezium Change Data Capture (CDC) connectors on Amazon Aurora PostgreSQL-Compatible Edition encounter particular challenges during major database version upgrades. While an in-place upgrade or an Amazon Aurora blue/green deployment is typically recommended, both methods can disrupt the CDC event pipelines that downstream systems rely on. An in-place upgrade necessitates downtime, interrupting active CDC consumers, while a blue/green deployment results in the dropping of logical replication slots at switchover, forcing Debezium connectors into a full re-snapshot that can take hours per database. This can pose significant risks and operational burdens for teams managing multiple clusters with active CDC connectors.
This article addresses customers who cannot afford a full connector re-snapshot during an upgrade. We present a method to perform a migration with an upgrade using PostgreSQL’s native logical replication, effectively bridging a source cluster (your current version) and a separate target cluster (the new major version), rather than upgrading a single cluster in place. Following the documented procedure allows you to transition your Debezium connectors to the new version while minimizing the risk of data loss. Our testing revealed that the database-side cutover completed in mere seconds, resulting in a brief pause for Kafka consumers before event delivery resumed. We also discuss potential failure scenarios and the necessary post-cutover monitoring to detect them.
Solution overview
This approach employs Aurora PostgreSQL’s native logical replication, which you configure to synchronize data from a source cluster (PostgreSQL 14 in this example) to a target cluster running a newer major version (PostgreSQL 17). This pattern can be applied to any supported major-version pair. Throughout this phase, your existing Debezium connector continues to operate on the source. Once the target is fully synchronized, a brief cutover sequence is executed: disabling the replication subscription, synchronizing sequences, creating a new Debezium replication slot on the target, and redirecting the connector.
By creating the target cluster’s Debezium slot at the current log sequence number (LSN), the connector can start without any backlog to process. Setting snapshot.mode=never allows Debezium to skip the initial snapshot, reading only new changes from the slot. Kafka consumers continue reading from the same topics with the same topic.prefix, experiencing only a brief pause before events resume.
This article builds upon AWS guidance for using logical replication to perform a major version upgrade for Aurora PostgreSQL. While that documentation focuses on the database upgrade itself, this article emphasizes operational tasks that are not covered, such as maintaining active Debezium CDC connectors during the upgrade, swapping the connector at cutover without a re-snapshot, synchronizing sequences, and addressing failure scenarios like connector stalls and in-flight schema changes.
Network considerations
This pattern requires low-latency connectivity between the source and target clusters. In our tests, we positioned the clusters within the same virtual private cloud (VPC) and Availability Zone. Cross-AZ replication typically introduces only single-digit milliseconds of round-trip latency, making it a viable option. However, it is essential to measure the actual latency in your environment before relying on it. Cross-Region replication is not recommended due to the increased risk of lag accumulation during the synchronization phase.
Prerequisites
Before proceeding, ensure the following conditions are met:
- Source cluster: Aurora PostgreSQL with
rds.logical_replication(a static parameter that requires a reboot to take effect) set to1in the cluster parameter group (a custom parameter group is necessary). This walkthrough uses Aurora PostgreSQL 14 as the source, but the pattern is applicable to any major version supporting logical replication. - Target cluster: A newer Aurora PostgreSQL major version, provisioned in the same VPC as the source, with cross-cluster security group access. This example upgrades to Aurora PostgreSQL 17, but any supported newer major version will suffice (e.g., 14 to 15 or 15 to 16).
- Primary keys: Ensure that tables participating in CDC have a primary key (or an explicitly set REPLICA IDENTITY). Logical replication uses this replica identity to uniquely match rows when applying
UPDATEandDELETEevents on the target. To identify tables in your CDC schema lacking a primary key:
SELECT n.nspname AS schema, c.relname AS table_name
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind = 'r' AND n.nspname = 'test_cdc'
AND NOT EXISTS (SELECT 1 FROM pg_index i WHERE i.indrelid = c.oid AND i.indisprimary);
For any returned table, add a primary key or set an explicit REPLICA IDENTITY before proceeding.
- Debezium: Version 2.x or later with the PostgreSQL connector, running on Amazon Managed Streaming for Apache Kafka (Amazon MSK) Connect, Amazon Elastic Container Service (Amazon ECS), or self-managed Kafka Connect.
- Permissions: A database user with the
rds_replicationrole on both clusters (the Amazon Relational Database Service (Amazon RDS) and Aurora role that grants the necessary privileges to manage and stream from logical replication slots), plusCREATEprivilege on the target database. - Tools:
psqlor an equivalent SQL client,pg_dumpfor schema export, and access to the Kafka Connect REST API or the AWS Management Console.
Parameter group settings
Configure the following parameters on both source and target cluster parameter groups:
| Parameter | Default | Recommended starting point | Apply type |
rds.logical_replication |
0 |
1 |
Static (reboot) |
max_replication_slots |
20 |
count(databases) + 3 |
Static (reboot) |
max_wal_senders |
10 |
max_replication_slots + 3 |
Static (reboot) |
max_logical_replication_workers |
engine-default | number of databases, plus reserve for table-sync and parallel-apply workers | Static (reboot) |
max_worker_processes |
GREATEST(vCPU * 2, 8) |
max_logical_replication_workers + 1 (or higher) |
Static (reboot) |
Use the Recommended starting point column as guidance. The default values are provided for reference, and for max_logical_replication_workers and max_worker_processes, these are computed by the engine rather than fixed numbers. These values are starting points, not fixed limits. Adjust them according to your workload. The bridge subscription requires one replication slot and one write-ahead log (WAL) sender in addition to what your existing Debezium connectors consume, so set max_replication_slots and max_wal_senders with headroom above your current usage. The parameters max_logical_replication_workers and max_worker_processes determine how many tables can be synchronized in parallel during the initial copy, so increase them if you have many tables to synchronize.
The parameter max_slot_wal_keep_size acts as a safety guard against unbounded WAL growth. If your replication slot becomes inactive during the transition (for instance, due to subscriber downtime or a paused bridge), the system will accumulate WAL files indefinitely by default. Setting this cap allows for storage reclamation even if a slot stalls. Monitor replication slot lag through the Amazon CloudWatch metric OldestReplicationSlotLag. This is a dynamic parameter that can be applied without a reboot.
If the source database handles large transactions or maintains a high sustained write volume, consider increasing the value of rds.logical_wal_cache. This write-through cache minimizes reliance on the Aurora storage layer. Instead of consistently writing to and reading from this layer, Aurora PostgreSQL utilizes a buffer to cache the logical WAL stream during replication, reducing the need for disk access. Monitor this metric using the following SQL query:
SELECT * FROM aurora_stat_logical_wal_cache();
Note: Some of the parameters mentioned above are static and require a cluster reboot to take effect. Plan these changes during a maintenance window before initiating the activity.
To configure the source cluster for logical replication
- Verify that logical replication is enabled on the source:
SHOW rds.logical_replication;
SHOW wal_level;
-- wal_level returns "logical"
- Create a publication that includes tables participating in CDC. On PostgreSQL 14, use an explicit table list (the
FOR ALL TABLES IN SCHEMAsyntax requires PostgreSQL 15 or later):
CREATE PUBLICATION debezium_pub FOR TABLE
test_cdc.orders,
test_cdc.customers,
test_cdc.inventory;
- Verify the publication includes the expected tables:
SELECT schemaname, tablename
FROM pg_publication_tables
WHERE pubname = 'debezium_pub';
- Check that your existing Debezium replication slot is active and consuming:
SELECT slot_name, plugin, active, restart_lsn, confirmed_flush_lsn
FROM pg_replication_slots
WHERE slot_name = 'debezium_slot';
To create and configure the target cluster
- Create the target Aurora PostgreSQL cluster on the desired major version:
aws rds create-db-cluster
--db-cluster-identifier pg17-target
--engine aurora-postgresql
--engine-version 17.4
--master-username admin
--master-user-password
--vpc-security-group-ids
--db-subnet-group-name
--db-cluster-parameter-group-name
Here, --master-username and --master-user-password are the literal Amazon RDS API parameter names that set the cluster’s administrative user and password.
- Create a writer instance in the cluster:
aws rds create-db-instance
--db-instance-identifier pg17-target-instance
--db-cluster-identifier pg17-target
--db-instance-class db.r6g.large
--engine aurora-postgresql
Select an instance class that matches or exceeds the capacity of your source cluster, as the target will eventually handle production traffic. This example uses db.r6g.large for demonstration purposes. Adjust based on your workload. Wait for the cluster and instance to reach available status before proceeding:
aws rds wait db-instance-available --db-instance-identifier pg17-target-instance
- Export the schema from the source and apply it to the target. Before applying the schema, install any PostgreSQL extensions that your schema depends on (for example,
pgcrypto) on the target, aspg_dump --schema-onlyemitsCREATE EXTENSIONstatements that will fail if the extension is not already available on the target cluster):
# On the target, pre-install any required extensions, for example:
# psql -h -U admin -d testdb -c "CREATE EXTENSION IF NOT EXISTS pgcrypto;"
pg_dump -h -U admin -d testdb
--schema-only --schema=test_cdc > schema.sql
psql -h -U admin -d testdb -f schema.sql
- Verify that all tables exist on the target with matching structures:
SELECT * FROM aurora_stat_logical_wal_cache();
- Create the replication user (used by the logical replication subscription) and the Debezium user (used by the connector) on the target, if you aren’t using the admin user:
SELECT * FROM aurora_stat_logical_wal_cache();
To establish logical replication
- On the target cluster, create a subscription that connects to the source:
SELECT * FROM aurora_stat_logical_wal_cache();
Setting synchronous_commit = 'off' on the subscription accelerates the initial copy and steady-state apply by not waiting for the target’s local flush on every transaction. This is suitable here, as the source remains the system of record until cutover. It is a subscription-level setting for this migration, not a change to the source database’s durability.
- On the target, verify that all tables have reached the
readystate. PostgreSQL logical replication first copies a snapshot of existing rows (the initial data copy) and then switches each table to streaming ongoing changes;srsubstate = 'r'(“ready”) indicates that a table has completed the copy and is now streaming:
SELECT * FROM aurora_stat_logical_wal_cache();
Each replicated table reports r (ready) once its initial copy is complete:
SELECT * FROM aurora_stat_logical_wal_cache();
- On the source, monitor the subscription’s replication lag until it reaches zero:
SELECT * FROM aurora_stat_logical_wal_cache();
As the target catches up, lag_bytes trends toward zero:
SELECT * FROM aurora_stat_logical_wal_cache();
- Validate data consistency between source and target by comparing row counts:
SELECT * FROM aurora_stat_logical_wal_cache();
- Verify that ongoing data manipulation language (DML) operations replicate correctly by inserting a test row on the source and checking that it appears on the target:
SELECT * FROM aurora_stat_logical_wal_cache();
To prepare for cutover
Perform these steps during a scheduled maintenance window. The subscription should show zero lag before proceeding.
- Confirm that replication lag is consistently at or near zero:
SELECT * FROM aurora_stat_logical_wal_cache();
Proceed only when lag_bytes holds at or near zero across several consecutive checks. If it does not drain (for example, if it plateaus at a non-trivial value or continues to grow), do not begin the cutover. A non-draining subscription indicates that the target is not fully caught up, and cutting over would risk losing un-replicated changes. Set an abort threshold that suits your workload. For instance, if lag has not reached near-zero within a few minutes of write traffic quiescing, stop and investigate before retrying. A common cause is a long-running or idle-in-transaction session on the source, as discussed in Scenario 5.
- Prepare the Debezium connector configuration for the target (copy from your existing source connector, changing only
database.hostnameandsnapshot.mode):
SHOW rds.logical_replication;
SHOW wal_level;
-- wal_level returns "logical"
Important: Maintain the same topic.prefix value as your source connector. This allows Kafka consumers to continue reading from the same topics without reconfiguration.
- Create the publication on the target cluster (Debezium requires a publication to filter WAL events):
SHOW rds.logical_replication;
SHOW wal_level;
-- wal_level returns "logical"
- Notify downstream consumer teams of the upcoming pause in event delivery. In our testing, the database-side cutover took approximately 2 seconds. The total consumer-visible pause also includes the time required to start the connector on the target, which depends on your Kafka Connect setup.
To execute the cutover
This sequence briefly pauses application writes while the subscription drains and the Debezium slot is created on the target. In our test environment, which utilized Aurora PostgreSQL 14.15 upgraded to 17.4 on db.t4g.medium instances in us-east-1, the workload consisted of 65,000 rows across three tables with approximately 100 transactions per second (TPS) of sustained write load. Under these conditions, the database-side cutover completed in 2.21 seconds. Replication lag remained at 0 bytes during steady-state synchronization, and the initial data copy finished within seconds. Your actual duration will depend on database size, write throughput, and network latency between the source and target clusters.
The cutover duration is primarily influenced by how quickly the final replication lag drains to zero, which is affected by the in-flight transaction volume at cutover rather than the total database size. The fixed steps (disabling the subscription, synchronizing sequences, and creating the slot) also contribute to the timeline. Since the bulk data is already synchronized during the bridge phase, database size impacts the synchronization phase, not the cutover window. High write rates or long-running transactions at cutover can extend the lag-drain step.
Critical: To prevent duplicate events, pause the source connector and allow it to commit its offsets before starting the target connector. Do not allow both connectors to stream simultaneously; doing so, even briefly, may result in duplicate events as both process the same LSN range.
- Stop application writes on the source cluster:
SHOW rds.logical_replication;
SHOW wal_level;
-- wal_level returns "logical"
Production consideration: The
REVOKEapproach is suitable for single-user applications. For production systems with connection poolers or multiple database users:
- Utilize application-level feature flags to halt writes at the service layer.
- Alternatively, set
default_transaction_read_only = onin the parameter group (this requires a brief connection drain). - Or issue a
PAUSEcommand on your connection pooler (such as PgBouncer) to freeze all connections.
The
REVOKEmethod demonstrated here is appropriate for testing and single-tenant applications.
- Wait for the subscription lag to reach zero, then disable the subscription and allow the apply worker to drain:
SHOW rds.logical_replication;
SHOW wal_level;
-- wal_level returns "logical"
- Synchronize sequences on the target. Logical replication does not replicate sequence values, so you must reset them to avoid primary key conflicts:
SHOW rds.logical_replication;
SHOW wal_level;
-- wal_level returns "logical"
- Drop the subscription cleanly and create the Debezium replication slot on the target:
SHOW rds.logical_replication;
SHOW wal_level;
-- wal_level returns "logical"
Validation gate: Before proceeding to Step 5, ensure that the source connector is healthy and has consumed up to the current source LSN. Pausing it allows for a complete set of offsets to be committed to Kafka Connect’s
__connect_offsetstopic. Check the connector status:
SHOW rds.logical_replication;
SHOW wal_level;
-- wal_level returns "logical"
- Stop the existing Debezium connector on the source and start the new connector pointed at the target:
SHOW rds.logical_replication;
SHOW wal_level;
-- wal_level returns "logical"
Rollback procedure
If the cutover fails before Step 5 (connector swap), roll back to the source:
- Re-enable the subscription on the target to maintain data synchronization:
SHOW rds.logical_replication;
SHOW wal_level;
-- wal_level returns "logical"
- Restore write access on the source:
SHOW rds.logical_replication;
SHOW wal_level;
-- wal_level returns "logical"
- Verify that the source Debezium connector resumes normal operation by checking that
flush_lagreturns to your baseline.
Important: Rollback is only safe if you have not yet started the target connector (Step 5). Once the target connector begins writing to Kafka with the same
topic.prefix, rolling back risks duplicate events in downstream consumers.
To validate the cutover
- Verify the Debezium slot is active on the target:
CREATE PUBLICATION debezium_pub FOR TABLE
test_cdc.orders,
test_cdc.customers,
test_cdc.inventory;
After the target connector attaches, the slot should show active = t:
CREATE PUBLICATION debezium_pub FOR TABLE
test_cdc.orders,
test_cdc.customers,
test_cdc.inventory;
- Resume application writes on the target and verify that events flow to Kafka:
CREATE PUBLICATION debezium_pub FOR TABLE
test_cdc.orders,
test_cdc.customers,
test_cdc.inventory;
Check that the event appears in your Kafka topic:
CREATE PUBLICATION debezium_pub FOR TABLE
test_cdc.orders,
test_cdc.customers,
test_cdc.inventory;
- Monitor
flush_lagon the target to ensure the connector is consuming steadily:
CREATE PUBLICATION debezium_pub FOR TABLE
test_cdc.orders,
test_cdc.customers,
test_cdc.inventory;
To verify end-to-end continuity in our validation, we inserted 100 marker rows on the target after the cutover. We then consumed the topic from the beginning. The 100 events appeared in the Kafka topic with distinct primary keys and no duplicates, confirming that the new connector resumed delivery without gaps or replays.
Handling failure scenarios
This section outlines common failure scenarios associated with this pattern, along with detection and resolution guidance for each. We reproduced the large-message (Scenario 3) and DDL-during-synchronization (Scenario 4) scenarios directly during testing. The remaining items describe expected PostgreSQL and Aurora behavior that should be validated in your environment before relying on it in production.
Scenario 1: Aurora writer failover during synchronization
Symptoms: The subscription on the target stops receiving updates. pg_stat_subscription.last_msg_receipt_time becomes stale.
Detection: Monitor last_msg_receipt_time on the target. If it exceeds 60 seconds without an update, the subscription may have lost its connection.
Resolution: An Aurora writer failover triggers an automatic DNS endpoint update. The subscription reconnects automatically once the new writer becomes reachable. Data loss is not expected under normal circumstances, as the subscription is designed to resume from its last confirmed LSN. However, validate the reconnection time and behavior in your environment, as it depends on your DNS caching and failover settings.
Prevention: Consider increasing wal_sender_timeout on the source (for example, to 120 seconds) during the migration window to avoid premature sender shutdown across a failover transition. Avoid disabling it entirely (0) outside the migration window, as this also suppresses the detection of genuinely dead connections.
Scenario 2: Debezium connector stall (network interruption)
Symptoms: Connector reports RUNNING in Kafka Connect, but flush_lag on the database grows without bound.
Detection: Query pg_stat_replication.flush_lag and alert if it exceeds 5 minutes. Do not rely solely on the connector RUNNING state, as this can mask the failure.
Resolution: Brief network interruptions typically recover on their own once connectivity returns. For a sustained stall, delete and recreate the connector. During testing, we observed that a recreated connector resumes cleanly from the slot’s confirmed_flush_lsn with no data loss.
Prevention: Configure heartbeat.interval.ms (set to 10000 or lower) and monitor flush_lag with automated alerts.
Scenario 3: Large messages exceed Kafka size limits
Debezium serializes each changed row into a single Kafka record, so a single wide row can exceed your Kafka producer or broker size limits. In our testing, a row payload approaching 1 MB failed to produce under the default max.request.size of 1,048,576 bytes (1 MB): rows up to roughly 1 MB delivered normally, while a row of approximately 1.05 MB produced a RecordTooLargeException. The Debezium JSON envelope (operation, source metadata, before and after images) adds overhead on top of the row data, causing the serialized record to exceed the limit slightly before the raw column data does.
Symptoms: The connector task transitions to FAILED. The Kafka Connect log displays a RecordTooLargeException. The connector does not advance past the oversized record, leading to the accumulation of WAL in the database replication slot.
Detection: The connector task state shows FAILED with a RecordTooLargeException in the trace. The replication slot’s confirmed_flush_lsn ceases to advance while pg_current_wal_lsn() continues to move, resulting in retained WAL growth.
Resolution: Increase max.message.bytes at both the broker and topic levels, as well as max.request.size in the connector’s producer overrides to accommodate the row, and then restart the connector. In our tests, the previously stuck record was delivered immediately upon restart, allowing the slot to resume draining. The connector cannot skip the oversized event, so the limits must be raised rather than circumvented.
Prevention: Before cutover, audit JSONB, TEXT, and bytea columns that may approach 1 MB when serialized, and size max.message.bytes and max.request.size with sufficient headroom above your largest expected row.
Scenario 4: DDL changes applied during synchronization
Symptoms: After a DDL change on the source (e.g., ALTER TABLE ADD COLUMN), replication for all tables in the publication halts. The subscription worker enters an error state.
Detection: pg_stat_subscription.last_msg_receipt_time stops advancing. Subscription worker logs indicate a schema mismatch.
Resolution: Manually apply the same DDL on the target, then re-enable the subscription. Logical replication does not replicate DDL statements.
Prevention: Freeze schema changes during the synchronization window. If DDL is unavoidable, apply it on the target first, followed by the source.
Scenario 5: Long-running transactions block lag drain
Symptoms: After stopping application writes (Step 1 of cutover), replication lag remains non-zero for more than 30 seconds. The lag_bytes value does not drain to zero because an open transaction on the source is obstructing the replication stream.
Detection: Monitor pg_stat_activity for long-running or idle-in-transaction sessions. If lag remains non-zero, it indicates that an open transaction is blocking the replication stream.
Resolution: Identify the blocking session and, if safe to do so, terminate it using pg_terminate_backend. This action forcibly ends the backend and rolls back its in-flight transaction, so confirm with the application owner that it is safe to terminate before executing this command:
CREATE PUBLICATION debezium_pub FOR TABLE
test_cdc.orders,
test_cdc.customers,
test_cdc.inventory;
Once the session ends, lag should drain to zero within seconds, allowing you to proceed with Step 2.
Prevention: Before initiating Step 1 (stopping writes), audit pg_stat_activity for long-running or idle-in-transaction sessions. Resolve these proactively, as a lag = 0 signal is reliable only when no open transactions remain on the source.
Post-migration monitoring
After completing the cutover, establish ongoing monitoring for these three signals:
AuroraReplicaLag and flush_lag growth
Monitor the AuroraReplicaLag Amazon CloudWatch metric alongside pg_stat_replication.flush_lag for comprehensive visibility into replication health:
CREATE PUBLICATION debezium_pub FOR TABLE
test_cdc.orders,
test_cdc.customers,
test_cdc.inventory;
Set alerts if flush_lag exceeds your heartbeat interval (default 10 seconds) for more than 5 minutes. A connector may report RUNNING while flush_lag grows indefinitely, leading to silent WAL accumulation on Aurora storage. For instance, at an assumed write rate of 1 MB/s, 24 hours of this failure could accumulate approximately 86 GB of unreclaimed WAL. Since flush_lag is not a native CloudWatch metric, publish it as a custom metric (for example, via a scheduled AWS Lambda function that queries pg_stat_replication) and create a CloudWatch alarm on it. For VolumeBytesUsed, set alarms directly on the metric published by Amazon RDS.
WAL growth rate
Monitor the CloudWatch metric VolumeBytesUsed for the target cluster. Alert if the growth rate exceeds your measured application write rate, indicating that the replication slot is retaining WAL faster than it is being consumed.
Kafka consumer offset divergence
Compare the latest committed offset of the Debezium connector with the Kafka topic’s high watermark. Increasing divergence indicates that the connector is writing to Kafka more slowly than events arrive, suggesting downstream backpressure or connector degradation.
Important: The Aurora CloudWatch metric
AuroraReplicaLagmonitors writer-to-reader replica lag, not subscription lag. Custom monitoring (e.g., an AWS Lambda function queryingpg_stat_replication) is necessary for subscription-side visibility.
Automation at scale
For organizations managing numerous clusters, encapsulating this pattern in an AWS Cloud Development Kit (AWS CDK) construct can streamline the process. The core automation components include:
- Cluster provisioning: A CDK stack that creates the target cluster with matching parameter group, security groups, and subnet configuration.
- Schema replication: A Lambda function that executes
pg_dump --schema-onlyand applies the output to the target. - Cutover orchestrator: An AWS Step Functions state machine that manages the cutover sequence (disable subscription → drain → sync sequences → drop → create slot → swap connector) with gate checks between each step.
- Monitoring stack: CloudWatch alarms for
AuroraReplicaLag,flush_lag,VolumeBytesUsedrate, and connector status.
Each cluster migration operates as an independent state machine execution. The Step Functions workflow provides built-in retry logic, timeout handling, and rollback triggers if any gate check fails.
Note: Multi-connector orchestration (4–8 connectors per database, as is common in large deployments) has not been validated in this pattern. If you operate multiple Debezium connectors per cluster, conduct a staging test in your environment before applying it to production.
The SQL statements, connector configuration, and cutover sequence detailed in this article are provided inline for adaptation to your environment. This walkthrough does not include a separate code repository. The automation components described here serve as a reference design rather than a packaged construct.
Clean up
After verifying 24 hours of stable operation on the target cluster:
- Delete the old Debezium connector pointed at the source:
CREATE PUBLICATION debezium_pub FOR TABLE
test_cdc.orders,
test_cdc.customers,
test_cdc.inventory;
- Drop the Debezium replication slot on the source (this stops WAL retention):
CREATE PUBLICATION debezium_pub FOR TABLE
test_cdc.orders,
test_cdc.customers,
test_cdc.inventory;
- Drop the publication on the source:
SELECT schemaname, tablename
FROM pg_publication_tables
WHERE pubname = 'debezium_pub';
- Once the target has operated stably in production for a validation window (we recommend at least several days, such as 7–14 days, to allow for rollback if needed), decommission the source cluster. Begin by deleting the DB instances in the cluster, followed by deleting the cluster itself:
SELECT schemaname, tablename
FROM pg_publication_tables
WHERE pubname = 'debezium_pub';
Important caveats
Keep these limitations in mind when planning your migration:
- PostgreSQL 17 failover slots aren’t available on Aurora PostgreSQL. The community PostgreSQL 17 feature
sync_replication_slotsis not exposed as a modifiable parameter on Aurora. After a writer failover on Aurora PostgreSQL 17, replication slots must still be recreated, and Debezium must be reconnected, maintaining the same operational behavior as PostgreSQL 14. - TOAST columns with partial updates. When Debezium processes an
UPDATEthat does not modify a TOASTed column (largeTEXTorJSONB), it emits__debezium_unavailable_valueas a placeholder. Ensure that your downstream consumers handle this sentinel value appropriately. FOR ALL TABLES IN SCHEMArequires PostgreSQL 15 or later. On PostgreSQL 14, you must explicitly enumerate tables in your publication. Generate the table list dynamically frominformation_schema.tables.- Sequences are not replicated. PostgreSQL logical replication does not synchronize sequence values. The
setval()step during cutover is mandatory to prevent primary key conflicts when writes resume on the target. - Silent WAL bloat risk. A Debezium connector can report a
RUNNINGstate while itsflush_laggrows indefinitely. Monitorpg_stat_replication.flush_lagdirectly, rather than relying solely on the connector state.