Active-active replication — where two or more databases accept writes simultaneously and replicate to each other — is one of the most powerful but also most operationally complex GoldenGate topologies. Without a robust conflict detection and resolution (CDR) strategy, you will silently corrupt data on one or both sides. This article covers CDR mechanics, built-in GoldenGate resolution handlers, and architectural patterns that minimise conflict occurrence in the first place.

Why Conflicts Occur

In a bidirectional topology, any row can be updated on either site concurrently. A conflict occurs when:

  1. The same row is modified at Site A and Site B between replication cycles.
  2. Replicat at Site A tries to apply Site B’s change but the row at Site A has already diverged from the before-image Site B knew about.

Without CDR, the default Replicat behaviour is to abend — halting replication entirely. This is the safest default but the wrong operational posture for production active-active.


GoldenGate CDR Architecture

GoldenGate CDR is configured in the Replicat parameter file. There are two phases:

  1. Conflict Detection — Replicat detects that the row at the target differs from what the source expected (before-image mismatch).
  2. Conflict Resolution — Replicat applies a configured resolution handler to decide which value wins.

Enabling CDR

-- Replicat parameter file
REPLICAT REP_SITEB

USERID ggadmin@siteb, PASSWORD xxxxxxxx
ASSUMETARGETDEFS

-- Enable CDR globally for all mapped tables
CDR ENABLE ON

-- Resolution handlers (applied in order)
CDR DEFAULTCONFLICTRESOLUTION
  INSERT: USEMAX(LAST_UPDATED_TS),
  UPDATE: USEMAX(LAST_UPDATED_TS),
  DELETE: DISCARD

MAP SCHEMA1.ORDERS, TARGET SCHEMA1.ORDERS,
  &
  RESOLVECONFLICT (UPDATEROWEXISTS, (DEFAULT, USEMAX (LAST_UPDATED_TS)),
                                     (MISSING_COL, USEDELTA)),
  &
  RESOLVECONFLICT (DELETEROWEXISTS, (DEFAULT, DISCARD));

Resolution Handlers in Detail

USEMAX / USEMIN

The most common handler. GoldenGate compares a designated timestamp or version column and applies the row with the higher (or lower) value. This works correctly only if:

  • All sites have synchronised clocks (use NTP/Chrony tightly).
  • The timestamp column is always updated on every DML operation (use a trigger or application logic).
-- Trigger to maintain last_updated_ts on every UPDATE
CREATE OR REPLACE TRIGGER trg_orders_ts
BEFORE UPDATE ON schema1.orders
FOR EACH ROW
BEGIN
  :NEW.last_updated_ts := SYSTIMESTAMP;
END;
/

USEDELTA

For numeric columns that represent a running total (balances, counters), use USEDELTA to apply the delta rather than the absolute value. This avoids lost-update problems on additive fields.

RESOLVECONFLICT (UPDATEROWEXISTS,
  (balance_col, USEDELTA))

Example: Site A increases balance by 100, Site B increases it by 50 concurrently. USEDELTA applies both changes correctly (+100 and +50) rather than one overwriting the other.

DISCARD

The losing change is written to the discard file for audit purposes and the winning value is kept. Always configure a discard file — it is your audit trail for conflicts.

-- In Replicat parameter file
DISCARDFILE ./dirrpt/rep_siteb.dsc, APPEND, MEGABYTES 500
DISCARDROLLOVER AT 02:00 ON SUNDAY

OVERWRITE / IGNORE

  • OVERWRITE — the incoming change always wins (source-biased resolution).
  • IGNORE — the existing target value always wins (target-biased resolution).

These are rarely correct for general use but appropriate for specific tables where one site is always authoritative.


Detecting Conflicts in the Discard File

Discard files are written in a structured format. Parse them regularly to understand conflict rates:

# Count conflicts by type in the discard file
grep -c "OGG-01004\|OGG-01403" /u01/ogg/dirrpt/rep_siteb.dsc

GoldenGate also exposes conflict statistics via STATS REPLICAT:

GGSCI> STATS REPLICAT REP_SITEB, REPORTDETAIL, RESET

For Microservices architecture, query the REST API:

curl -u oggadmin:xxxxxxxx \
  http://localhost:9011/services/v2/replicats/REP_SITEB/statistics

Architectural Patterns to Minimise Conflicts

CDR is a last resort. A well-designed active-active topology minimises conflict probability through application design.

1. Row-Level Partitioning by Site

Assign each site ownership of a range of primary keys or customer IDs. Site A owns odd-numbered customer IDs, Site B owns even-numbered ones. Cross-site writes are not permitted by the application. Conflicts become impossible by construction.

-- Application-level routing constraint
-- Route INSERT for customer_id based on MOD()
-- Site A: MOD(customer_id, 2) = 1
-- Site B: MOD(customer_id, 2) = 0

2. Table-Level Ownership

Assign certain tables as writable only on one site. Reference data, lookup tables, and configuration tables are typically owned by a primary site and replicated read-only to others.

3. Loop Prevention

In bidirectional replication, each Replicat at a site will replicate its changes back to the originating site, causing an infinite loop unless prevented. Use the SOURCEDEFS or loop-prevention parameter:

-- In Replicat parameter file: tag changes with a GoldenGate marker
-- so the Extract on this site ignores them
DBOPTIONS SUPPRESSTRIGGERS
TRANLOGOPTIONS EXCLUDEUSER ggadmin

The cleanest approach: replicate only changes that did not originate from GoldenGate. Tag all GoldenGate-applied transactions and exclude them in Extract:

-- Extract parameter file on Site A
TRANLOGOPTIONS EXCLUDEUSER ggadmin

This prevents Extract from capturing changes that Replicat wrote, breaking the loop.


Monitoring and Alerting for Conflicts

Conflicts should be rare if the architecture is sound. Set up automated alerting:

-- Custom table to log CDR events via GoldenGate user exit or SQLEXEC
CREATE TABLE ggadmin.cdr_audit (
  conflict_ts   TIMESTAMP DEFAULT SYSTIMESTAMP,
  table_name    VARCHAR2(128),
  operation     VARCHAR2(10),
  resolution    VARCHAR2(50),
  pk_values     VARCHAR2(4000)
);

For Microservices GoldenGate, the deployment server exposes health metrics in Prometheus format — integrate these into your monitoring stack to alert on non-zero conflict rates.


Summary

Active-active GoldenGate is powerful but demands deliberate design. The hierarchy of decisions is:

  1. Partition data ownership to make conflicts structurally impossible.
  2. Configure USEMAX on timestamp columns for tables where cross-site writes are permitted.
  3. Configure USEDELTA on additive numeric columns.
  4. Always configure a discard file and monitor it.
  5. Implement loop prevention via TRANLOGOPTIONS EXCLUDEUSER.

CDR is the safety net, not the primary design tool.