Skip to content

Zero-ETL Is Not Two-Way Sync: Aurora PostgreSQL and NetSuite

Aurora PostgreSQL zero-ETL lands data in Amazon Redshift within seconds, in one direction, and AWS DMS bidirectional replication is two independent one-way tasks with loopback prevention. Neither writes back into NetSuite. This guide covers what two-way sync between an Aurora cluster and NetSuite actually has to solve, what each side contributes to the problem, and how the five solution categories compare.

Author
Ruben Burdin · Founder & CEO
Published
July 23, 2026
Read time
11 min read
Zero-ETL Is Not Two-Way Sync: Aurora PostgreSQL and NetSuite
DATA ENGINEERING

Search for two-way sync between AWS Aurora PostgreSQL and NetSuite and the first two answers you get are zero-ETL and AWS DMS. Neither one is a two-way sync. Aurora PostgreSQL zero-ETL is a managed one-way replica into Amazon Redshift or Amazon SageMaker Lakehouse, and what DMS calls bidirectional replication is two independent one-way tasks with loopback prevention. Both are useful. Neither will put a credit hold set in NetSuite back on the customer row in your application database.

This is an evaluation guide rather than a tutorial. It covers what the Aurora cluster contributes to the problem, what NetSuite contributes, and how the five categories of solution compare on the three things that never go away: loop prevention, conflict policy and the replication slot lifecycle. The AWS Aurora PostgreSQL connector page has the short version.

Before and after: one-way pipelines where zero-ETL ships Aurora to Redshift in a single direction, DMS bidirectional is two one-way tasks, nothing writes back into NetSuite and a stalled slot pins WAL on the cluster, versus a two-way sync with Aurora WAL in, SuiteTalk REST writes out, origin tags that drop the engine's own echoes, field-level precedence and slots re-established after a failover

If you are on RDS for PostgreSQL rather than an Aurora cluster, the sibling write-up is two-way sync between Amazon RDS and NetSuite, which covers the EBS-backed surface instead. If you are still choosing a platform, start from the enterprise iPaaS guide for Aurora PostgreSQL; if the other side is a CRM, read how to sync Aurora PostgreSQL with Salesforce. The Aurora origin story explains the shared storage layer underneath all of it.

Zero-ETL is not two-way sync, and neither is DMS bidirectional replication

Zero-ETL moves data in one direction only. Aurora PostgreSQL zero-ETL integrations target Amazon Redshift, generally available since October 2024, and Amazon SageMaker Lakehouse since October 2025, and AWS describes the data as available in the target within seconds of the write. There is no documented write-back path. It is a managed low-latency analytics replica of the cluster, not a link between two systems that both accept writes.

That matters because zero-ETL is very good at the job it does. If the question is how to run analytics on live Aurora data without operating a pipeline, zero-ETL is the right answer. If the question is how a fulfilment status reaches the sales order in the ERP, a one-way analytics replica cannot help, however fast it is.

Side by side comparison: a managed replica where zero-ETL moves Aurora to an analytics target only, with no documented write path back into NetSuite, loopback prevention instead of conflict rules and a slot you rebuild after every switchover, versus a two-way sync with WAL off the writer in, SuiteTalk REST writes out, origin tags on both sides, field-level precedence per record type and a slot rebuilt with replay resumed from a watermark
A managed replica and a two-way sync are not the same shape of thing. The difference is everything on the right.

AWS DMS gets closer without arriving. It does support what it calls bidirectional replication, and the design is worth reading carefully: two independent one-way tasks, A to B and B to A, with loopback prevention so a change replicated in one direction is not sent back the other way. AWS is unusually direct about what that does and does not give you.

"Bidirectional replication in AWS DMS isn't intended as a full multi-master solution including a primary node, conflict resolution, and so on."
AWS Database Migration Service documentation

Two more constraints matter. DMS reads Aurora PostgreSQL through one of two logical decoding plugins: test_decoding, or pglogical when it has been set up on the source. pglogical filters unwanted tables at the slot level, so less write-ahead log volume, CPU and network traffic leave the cluster; test_decoding filters inside DMS and adds latency on long transactions. Separately, CDC from a PostgreSQL source requires primary keys, and PostgreSQL sources do not support a custom CDC start time, because there is no mapping from a wall-clock timestamp to a log sequence number.

And DMS does not speak NetSuite. Its targets are databases, warehouses and streams, so the ERP half is still a custom build: SuiteTalk REST calls, an auth flow, dependency ordering, retries, echo suppression and a reconciliation report, all of it yours to own.

What the Aurora cluster contributes to the problem

Aurora's contribution is the writer. Logical replication is enabled with rds.logical_replication, a DB cluster parameter that defaults to 0, and since you cannot edit a default parameter group, step one is a custom cluster parameter group. Set it to 1, reboot the writer instance, and wal_level then reads logical. You never set wal_level yourself.

Two consequences follow immediately. Turning the flag on increases WAL generation even if no replication slot is ever created, which shows up on the bill as higher VolumeWriteIOPS. And slots live on the publisher, which on Aurora means the writer instance and only the writer instance. AWS puts it plainly: PostgreSQL 16 added support for logical decoding from read replicas, and that feature is not supported on Aurora PostgreSQL. Change capture cannot be moved onto a reader, so it shares the writer with production traffic. A write-through WAL cache, added in Aurora PostgreSQL 14.5, 13.8, 12.12 and 11.17, cuts its disk I/O without removing that cost.

Then four ordinary cluster events each leave your slot in a different state than you left it.

  • Failover. The cluster endpoint is one DNS name that always resolves to the current primary, and Aurora repoints it when the primary changes. Aurora DNS zones use a 5 second TTL, failover is typically restored in under 60 seconds and often under 30, and AWS names DNS propagation as the largest contributor. RDS Proxy can cut failover time by up to 66%.
  • Blue/Green deployments. Each database needs a logical replication slot for the green environment, and AWS's documented guidance is to drop your own self-managed slots and subscriptions before the switchover and recreate them afterwards. Your CDC slot is not carried through.
  • Major version upgrades. Aurora requires every logical replication slot to be dropped, including inactive ones, before the upgrade will proceed.
  • A consumer that stops. An inactive slot keeps retaining WAL, which can eventually lead to insufficient storage, and it blocks autovacuum on the catalog tables. OldestReplicationSlotLag, ReplicationSlotDiskUsage, TransactionLogsDiskUsage and FreeStorageSpace in CloudWatch are the metrics to alarm on.

Treat that list as a design requirement. Slot survival across an Aurora failover is not a documented, solved behaviour, so anything you run in production has to assume the slot can disappear, rebuild it, and resume from a durable watermark it stored itself.

One cost note on Aurora Serverless v2. Scale-to-zero (the 0 to 256 ACU range on Aurora PostgreSQL 16.3, 15.7, 14.12 and 13.15 and later, where 1 ACU is roughly 2 GiB of memory) pauses after an idle interval you set from 300 to 86,400 seconds. Logical replication changes that budget: the writer and the readers in failover tiers 0 and 1 do not auto-pause, because Aurora keeps a minimal amount of activity going to health-check the replication connection. The sync does not break. The scale-to-zero saving you planned for goes away.

What NetSuite contributes to the problem

NetSuite has no write-ahead log and no native change-data-capture feed of any kind. The standard way to find what changed is scheduled polling: a SuiteQL query or a saved search filtered on lastmodifieddate. For genuine push you need a SuiteScript User Event script making an outbound HTTPS call, or NetSuite's Event Subscriptions feature, which is trigger-based notification rather than a log you can rewind and replay.

lastmodifieddate is a full datetime with sub-second precision, which is good news and a trap in the same sentence. Polling windows are built as >= and < comparisons against it, and the boundary is exactly where records go missing or arrive twice. Account time zone and data centre handling differ from your job's clock, so a window that looks airtight in one region drops or duplicates records around midnight and at daylight-saving transitions. Overlap the windows and make the write idempotent instead.

Then there is governance, where most integrations actually fail. NetSuite meters concurrent requests from a single unified pool shared across SuiteTalk SOAP, SuiteTalk REST and RESTlets, so your sync competes with every other integration in the account. Base limits are 5 concurrent requests on Standard, 15 on Premium, 20 on Enterprise and 20 on Ultimate, with each SuiteCloud Plus licence adding 10; development and partner accounts are fixed at 5. Cross the line and REST returns HTTP 429 while SOAP and SuiteScript raise SSS_REQUEST_LIMIT_EXCEEDED. A backfill that ignores the pool takes the account's other integrations with it.

The last NetSuite input is a clock. 2025.2 is the last release with full SOAP feature parity, from 2026.1 new integrations are expected to use REST with OAuth 2.0, from 2027.1 no new SOAP or token-based authentication integrations can be created, and in 2028.2 the SOAP endpoints are switched off entirely. Anything you scope today on SuiteTalk SOAP has a published end date, and that belongs in the build-versus-buy arithmetic.

What actually happens to one record

Both directions are easier to hold in your head as the states one record passes through than as a set of jobs and schedules. Take a sales order that exists as a row in Aurora and as a transaction in NetSuite.

State diagram of one record moving between Aurora PostgreSQL and NetSuite: detected from a WAL slot on the writer or a lastmodifieddate window, screened against the engine's own origin tag, mapped, applied to the target, then confirmed, in conflict, backing off on an HTTP 429 from the NetSuite pool, or resuming from a stored LSN after the slot is lost in a failover
The states one record moves through, including the three ways a write ends up somewhere other than confirmed.

Going out of Aurora, a commit lands in the write-ahead log, the change is decoded off a slot on the writer, the row is mapped to a NetSuite record type and field set, and the write goes out over SuiteTalk REST inside the sync's share of the concurrency pool. Dependencies have to be ordered, because a sales order line referring to a customer that does not exist yet is a validation error, not a transient failure to retry. That write then bumps lastmodifieddate, which the inbound poll sees on its next pass and has to recognise as the sync's own echo rather than a user edit.

Coming the other way, a change found by a lastmodifieddate window or pushed by a User Event script is applied as an INSERT ... ON CONFLICT DO UPDATE keyed on the NetSuite internal id, so a retry updates rather than duplicates. That write lands in the Aurora WAL and comes straight back out of the slot, indistinguishable from a user edit unless the engine tagged its own origin. Without the tag the record bounces between the two systems until somebody notices the API bill.

Three states carry the whole argument. Conflict is the same field moving on both sides inside one window, and the honest answer is not last writer wins, which makes the outcome depend on which job ran last; it is field-level precedence agreed once per record type. Backing off treats a 429 from the NetSuite pool as a normal event to absorb, not an incident to page on. Resuming accepts that the slot can vanish underneath you, and that the durable watermark is what makes the restart safe. Bi-directional sync explained works through the same three states on other pairs.

Book a Stacksync demo: two-way sync between AWS Aurora PostgreSQL and NetSuite with echo suppression and conflict rules you set

The five ways teams build this, and what each is genuinely good at

There are five recognisable shapes, and each is the right answer to some question. The useful question is which one owns the loop prevention, the conflict policy and the slot lifecycle, because those three exist whether or not anybody has been assigned to them.

  • AWS-native: DMS, Glue and custom Lambda functions. Best when both endpoints are databases or AWS services and one direction is enough. IAM-native, VPC-native, cheap at rest. It does not speak NetSuite, so the ERP half is a custom build you own permanently.
  • A generic iPaaS with a JDBC PostgreSQL connector. Best for orchestration across many applications, approvals and human-in-the-loop steps. Two-way sync is usually assembled as two scheduled flows plus a mapping table, and the connector polls rather than reading the WAL, which is fine at low volume and expensive at high change rates.
  • Self-managed logical replication tooling: pglogical, SymmetricDS. Best when both endpoints are relational databases you control. SymmetricDS is a real bidirectional engine with conflict handlers. Neither knows anything about a SaaS ERP, so NetSuite still needs a custom adapter, and you own the slot lifecycle across every failover and upgrade.
  • Custom code. Best when the scope is genuinely small and one named person owns it. It also consumes the most engineering time over three years, because the interesting work is never the first sync. It is the echo suppression, the ordering, the backoff, the replay and the reconciliation report.
  • A managed real-time two-way sync platform. Best when both sides accept writes, the pair is a database and a SaaS system rather than two databases, and you would rather configure the ownership rules than build the engine that enforces them. The trade is less control over the internals.
DMS plus LambdaGeneric iPaaSpglogical / SymmetricDSCustom codeStacksync
Aurora change captureLogical slot on the writerUsually JDBC pollingLogical slot on the writerYours to buildLogical slot on the writer
Writes into NetSuiteNo NetSuite targetPrebuilt connectorNo NetSuite targetSuiteTalk REST you writeSuiteTalk REST, managed
Loop preventionLoopback between tasksYour mapping tableOrigin filtering, per toolYours to buildOrigin tags on both sides
Conflict resolutionExplicitly out of scopeLast writer wins, typicallyHandlers you codeYours to defineField-level precedence
Slot lifecycle at failoverYou rebuild the taskNot applicableYou rebuild the slotYours to handleRebuilt, resumed from a watermark
NetSuite concurrency poolNot applicableShared, often unmanagedNot applicableYours to handlePool-aware, backs off on 429
LatencySeconds, one wayBound to the scheduleSeconds, database to databaseWhatever you buildReal time, both directions
Who operates itYour teamYour teamYour teamYour teamManaged
Genuinely best atAWS to AWS, one wayMulti-app orchestrationDatabase to databaseOne small scopeDatabase and SaaS, both writing

Five real options. The three rows that decide the project are loop prevention, conflict resolution and the slot lifecycle.

The row almost nobody costs correctly is the slot lifecycle. It is invisible on day one and it is what pages someone in month eight, because a slot orphaned by a switchover keeps retaining WAL on a cluster whose storage quietly grows in 10 GiB segments up to 128 TiB. On why scheduled pipelines and live sync are different products rather than different settings, see real-time sync versus batch ETL.

How to choose, and what to ask before you sign anything

Start by writing down every field that will sync and putting exactly one owner's name next to it. That list is your conflict policy, and producing it usually turns a twelve-object plan into a four-object plan, which is the largest cost saving available on this project. If you cannot name an owner for a field, it should not be bidirectional yet. Then take these seven questions to whoever is proposing to build or sell you the thing.

  • 01Where does change capture read from, and does it hold a logical replication slot on the Aurora writer? If the answer is polling, ask what that query costs against a table with 200 million rows.
  • 02What happens to that slot when the cluster fails over, when a Blue/Green switchover completes, and during a major version upgrade? A good answer names a stored watermark.
  • 03Does it alarm on OldestReplicationSlotLag and ReplicationSlotDiskUsage, or is that monitoring mine to build?
  • 04How does it recognise its own writes on both sides, given that a NetSuite write bumps lastmodifieddate and an Aurora write lands in the WAL a moment later?
  • 05How is a conflict resolved, per field or per record, and where is that rule written down somewhere an auditor can read it?
  • 06How does it stay inside the NetSuite concurrency pool during an initial backfill, and what does it do on an HTTP 429?
  • 07Is it built on SuiteTalk REST with OAuth 2.0, given that the SOAP endpoints are switched off in 2028.2?

If the answer to most of those is that you build it, the honest total cost is the engine and not the connector. To see Aurora PostgreSQL and NetSuite kept in step in both directions, look at the Aurora PostgreSQL and NetSuite integration, read how the two-way sync engine handles ownership, echoes and retries, or book a demo and bring the object you expect to be difficult. The same engine covers NetSuite against the rest of your stack, so the ERP side is modelled once.

Start syncing AWS Aurora PostgreSQL and NetSuite in both directions with Stacksync

FAQ

Frequently asked questions

Does Aurora PostgreSQL zero-ETL sync both ways?
No. Aurora PostgreSQL zero-ETL integrations run in one direction only, from the Aurora cluster into Amazon Redshift (generally available since October 2024) or Amazon SageMaker Lakehouse (October 2025). AWS describes the data as available in the target within seconds of the write, and there is no documented write-back path from the target to Aurora. It is a managed low-latency analytics replica, not a bidirectional integration, so it cannot carry a change made in NetSuite or any other application back to your database.
Does AWS DMS support bidirectional replication with Aurora PostgreSQL?
It supports something called bidirectional replication, which is two independent one-way tasks (A to B and B to A) with loopback prevention so a replicated change is not sent back. AWS states that it "isn't intended as a full multi-master solution including a primary node, conflict resolution, and so on". Two further limits matter: CDC from a PostgreSQL source requires primary keys on the replicated tables, and PostgreSQL sources do not support a custom CDC start time because there is no timestamp to LSN mapping. DMS also has no NetSuite target.
Can an Aurora PostgreSQL read replica host a logical replication slot?
No. Replication slots live on the publisher, which on Aurora means the writer instance only. AWS states that PostgreSQL 16 added support for logical decoding from read replicas and that this feature is not supported on Aurora PostgreSQL. You cannot offload change capture to a reader, so CDC reads share the writer with production write traffic. Since Aurora PostgreSQL 14.5, 13.8, 12.12 and 11.17 a write-through WAL cache reduces the disk I/O of logical decoding, which softens that cost without removing it.
What happens to an Aurora logical replication slot during a failover?
Design for the slot not surviving. The cluster endpoint is a single DNS name that Aurora repoints at the new primary, the DNS zones use a 5 second TTL, and failover is typically restored in under 60 seconds and often under 30, with AWS naming DNS propagation as the largest contributor. Slot survival across an Aurora failover is not a documented, solved behaviour, and both Blue/Green switchovers and major version upgrades explicitly require you to drop and recreate your own slots. A production sync rebuilds the slot and resumes from a durable watermark it stored itself.
How do you capture changes from NetSuite when it has no write-ahead log?
NetSuite has no native change-data-capture feed, so the standard pattern is scheduled polling with a SuiteQL query or a saved search filtered on lastmodifieddate. For push you need a SuiteScript User Event script making an outbound HTTPS call, or NetSuite's Event Subscriptions feature, which is trigger-based notification rather than a replayable log. Because lastmodifieddate is a full datetime and account time zone and data centre handling differ from your job's clock, polling windows should overlap slightly and writes should be idempotent instead of relying on an exact boundary.
How many concurrent API requests can a NetSuite sync use?
NetSuite meters concurrency from a single unified pool shared across SuiteTalk SOAP, SuiteTalk REST and RESTlets, so a sync competes with every other integration in the account. Base limits are 5 concurrent requests on Standard, 15 on Premium, 20 on Enterprise and 20 on Ultimate, and each SuiteCloud Plus licence adds 10; development and partner accounts are fixed at 5. Over the limit, REST returns HTTP 429 and SOAP or SuiteScript raises SSS_REQUEST_LIMIT_EXCEEDED. A backfill that ignores the pool takes the account's other integrations down with it.
Does Aurora PostgreSQL or RDS for PostgreSQL matter for a two-way sync?
Yes, mostly on the operational side. Aurora keeps six copies of your data across three Availability Zones on shared storage that grows in 10 GiB segments up to 128 TiB, and replica lag is typically under 100 ms. RDS for PostgreSQL is EBS-backed with Provisioned IOPS you size yourself, storage capped at 64 TiB, and read replica lag measured in seconds. The sync logic is the same, but the failure modes around slots, cluster endpoints, Blue/Green switchovers and Serverless v2 scaling are Aurora-specific.
Does enabling logical replication stop Aurora Serverless v2 scaling to zero?
Effectively yes. With logical replication enabled, the writer and the readers in failover tiers 0 and 1 do not auto-pause, because Aurora keeps a minimal amount of activity going to health-check the replication connection. Scale-to-zero (the 0 to 256 ACU range on Aurora PostgreSQL 16.3, 15.7, 14.12 and 13.15 and later, with an idle interval configurable from 300 to 86,400 seconds) is therefore off the table while CDC is on. The sync does not break; the saving you budgeted for goes away.

About the author

Ruben Burdin
Ruben Burdin
Founder & CEO

Ruben Burdin is the Founder and CEO of Stacksync, the first real-time and two-way sync for enterprise data at scale. Ruben is a Y Combinator alumni with a strong background in software engineering and business.

All posts by Ruben Burdin

About Stacksync

Stacksync powers real-time, two-way sync between CRMs, ERPs, and databases. Engineers sync data at scale and automate workflows, not dirty API plumbing.

Coworkers laughing in front of a laptop in a casual office setting

Your last integration took months.
Your next one takes a prompt.