Change Data Capture (CDC) for ERP Integration — Debezium, GoldenGate, and Cloud ERP Patterns

Type: ERP Integration Systems: Debezium 3.4.x, Oracle GoldenGate 23ai, Salesforce CDC v66.0, SAP ODP/SLT Confidence: 0.88 Sources: 7 Verified: 2026-03-02 Freshness: evolving

TL;DR

System Profile

This card covers Change Data Capture as an integration architecture pattern across multiple ERP systems. It compares log-based CDC tools (Debezium, Oracle GoldenGate), vendor-native CDC (Salesforce CDC, SAP ODP/SLT), and explains why cloud SaaS ERPs fundamentally limit what CDC methods are available. CDC method availability depends entirely on whether you have database-level access.

SystemCDC Method AvailableToolDB Access Required?Latency
Oracle EBS (on-prem)Log-based (redo logs)GoldenGate, DebeziumYes — supplemental loggingSub-second
SAP S/4HANA (on-prem)Log-based + ODPSLT, Debezium, ADF SAP CDCYes (DB) or No (ODP via RFC)Seconds
SAP S/4HANA CloudODP onlyAzure Data Factory SAP CDCNo — ODP/RFC onlySeconds to minutes
SalesforceVendor-native event streamSalesforce CDC (Pub/Sub API)No — no DB accessSub-second
NetSuiteNone native; API polling onlyCustom (SuiteTalk/REST)No — no DB accessMinutes
WorkdayNone native; API-based onlyWorkday RaaSNo — no DB accessMinutes to hours
Dynamics 365Dataverse Change TrackingDataverse API / Azure Synapse LinkNo — API-level onlyMinutes
Custom ERP (PostgreSQL)Log-based (WAL)DebeziumYes — logical replicationSub-second
Custom ERP (MySQL)Log-based (binlog)DebeziumYes — binlog accessSub-second

API Surfaces & Capabilities

CDC Tool/MethodProtocolBest ForMax ThroughputLatencyOpen Source?Cost
Debezium (Kafka Connect)Kafka / HTTP (Server)On-prem DB CDC at scale100K+ events/secSub-secondYes (Apache 2.0)Infrastructure only
Oracle GoldenGateTrail files / REST APIOracle-to-Oracle, high-volume100K+ events/secSub-secondNo$17,500/processor
Salesforce CDCPub/Sub gRPC / CometDSF record change trackingEdition-dependentSub-secondN/A (platform feature)Included in Enterprise+
SAP ODP/SLTRFC / ODataSAP table and CDS extractionSLT sizing dependentSecondsNoSAP licensing
ADF SAP CDCODP via RFCSAP-to-Azure delta extractionIR sizing dependentMinutesNoADF pricing
Query-based pollingREST/SOAP APISimple, low-volume, any ERPAPI rate limitedMinutes to hoursN/AAPI call costs
Trigger-based CDCDB triggersLegacy, no log accessLow (trigger overhead)SecondsN/ADev cost

Rate Limits & Quotas

Debezium Limits

Limit TypeValueApplies ToNotes
Max connectors per clusterNo hard limitKafka ConnectBounded by cluster resources
Kafka message max size1 MB defaultAll connectorsConfigure max.message.bytes for large rows
Heartbeat interval300,000 ms defaultAll connectorsReduce for low-traffic tables
Oracle LogMiner batch10,000 rows defaultOracle connectorlog.mining.batch.size.default
Slot replication lagMonitor requiredPostgreSQLUnbounded WAL growth if behind

Oracle GoldenGate Limits

Limit TypeValueApplies ToNotes
Trail file size2 GB defaultExtract/ReplicatConfigurable; split large transactions
Supplemental logging overhead5-15% write increaseSource Oracle DBRequired — cannot be avoided
Max transaction sizeLimited by trail diskLarge batch ops>10M row transactions need tuning

Salesforce CDC Limits

Limit TypeValueWindowEdition Differences
Event retention3 daysRollingSame across all editions
Max entities per channel5 (custom channel)Per channelStandard channel covers all
CometD connectionsEdition-basedConcurrentEnterprise: 2,000 clients

SAP ODP/SLT Limits

Limit TypeValueApplies ToNotes
ODP delta queue retentionConfigurable (default 24h)All subscribersOld deltas purged after consumption
SLT replication tablesResource boundSLT serverEach table requires logging table

Authentication

ToolAuth MethodCredentialsRefresh?Notes
Debezium (PostgreSQL)DB user + REPLICATION roleUsername/password or SSLN/ADedicated replication user
Debezium (MySQL)REPLICATION SLAVE + CLIENTUsername/passwordN/AAlso needs SELECT
Debezium (Oracle)LogMiner privilegesUsername/passwordN/AV$LOG, V$LOGFILE access
Oracle GoldenGateOS + GG credential storeDB + GG admin credsN/AExtract runs as OS user
Salesforce CDCOAuth 2.0 (JWT/Web Server)Connected App + certYes (2h)Pub/Sub API or CometD
SAP ODP (via ADF)SAP RFC userSAP username/passwordN/ASelf-hosted IR required

Authentication Gotchas

Constraints

Integration Pattern Decision Tree

START — Need CDC from an ERP system
├── Do you have direct database access?
│   ├── YES (on-premise / IaaS with DB admin)
│   │   ├── Oracle → Debezium (LogMiner) or GoldenGate
│   │   │   ├── Budget allows $17,500+/processor? → GoldenGate (best Oracle native)
│   │   │   └── No / heterogeneous targets → Debezium (free, Kafka ecosystem)
│   │   ├── PostgreSQL → Debezium (logical replication, WAL)
│   │   ├── MySQL/MariaDB → Debezium (binlog)
│   │   ├── SQL Server → Debezium (SQL Server CDC feature)
│   │   └── Db2 → Debezium (Db2 connector)
│   └── NO (SaaS / cloud ERP, no DB access)
│       ├── Salesforce → Salesforce CDC (Pub/Sub API)
│       ├── SAP S/4HANA Cloud → ODP via ADF SAP CDC connector
│       ├── SAP ECC → ODP via SLT (requires SLT license)
│       ├── NetSuite → API polling (no native CDC)
│       ├── Workday → RaaS polling or Integration Cloud
│       └── Dynamics 365 → Dataverse Change Tracking API
└── Error tolerance?
    ├── Zero-loss → Exactly-once (Debezium 3.3+) + dead letter queue
    └── At-least-once → Default Debezium / GoldenGate behavior

Quick Reference

CDC Method Comparison

MethodMechanismLatencyDB ImpactDeletes?ComplexityCost
Log-based (Debezium)Transaction log tailingSub-secondMinimalYesMedium-HighFree + Kafka infra
Log-based (GoldenGate)Oracle redo log miningSub-secondMinimalYesHigh$17,500/processor
Vendor-native (SF CDC)Platform event busSub-secondNoneYesLowIncluded
Vendor-native (SAP ODP)ODP delta queueSecondsLowYesMediumSAP + SLT license
Query-based pollingTimestamp/ID filterMinutesHighNOLowAPI call costs
Trigger-basedDB triggers + shadow tableSecondsHIGHYesMediumDev cost

Tool Selection Matrix

FactorDebeziumGoldenGateSalesforce CDCSAP ODP/SLTPolling
License costFree (Apache 2.0)$17,500/processorIncludedSAP licenseFree
InfrastructureKafka clusterGG hubNone (SaaS)SLT serverNone
Supported sources11 databasesOracle primarySalesforce onlySAP onlyAny with API
Exactly-onceYes (v3.3+)Bounded recoveryAt-most-onceAt-least-onceN/A
Schema evolutionAuto-detectDDL replicationAutomaticCDS-dependentN/A
Operational complexityMediumHighLowMediumLow

Step-by-Step Integration Guide

1. Set up Debezium for PostgreSQL-backed ERP

Deploy Debezium via Kafka Connect to capture changes from a PostgreSQL-backed ERP database. [src1]

# Enable logical replication: wal_level=logical, max_replication_slots=4
psql -c "CREATE ROLE debezium_user WITH REPLICATION LOGIN PASSWORD 'secure_password';"
psql -c "GRANT SELECT ON ALL TABLES IN SCHEMA public TO debezium_user;"

Verify: psql -c "SHOW wal_level;" → expected: logical

2. Deploy Debezium PostgreSQL connector

Register the connector with Kafka Connect REST API. [src1]

curl -X POST http://localhost:8083/connectors \
  -H "Content-Type: application/json" \
  -d '{"name":"erp-postgres-cdc","config":{
    "connector.class":"io.debezium.connector.postgresql.PostgresConnector",
    "database.hostname":"erp-db.internal","database.port":"5432",
    "database.user":"debezium_user","database.password":"secure_password",
    "database.dbname":"erp_production","topic.prefix":"erp",
    "table.include.list":"public.orders,public.customers,public.invoices",
    "slot.name":"debezium_erp","plugin.name":"pgoutput",
    "snapshot.mode":"initial","heartbeat.interval.ms":"30000"}}'

Verify: curl http://localhost:8083/connectors/erp-postgres-cdc/status → expected: "state":"RUNNING"

3. Consume CDC events from Kafka

Read change events from Debezium-created Kafka topics. [src1]

kafka-console-consumer.sh --bootstrap-server localhost:9092 \
  --topic erp.public.orders --from-beginning --max-messages 5

Verify: Output contains JSON with "op": "c" (create), "op": "u" (update), or "op": "d" (delete).

4. Monitor replication slot health

Prevent WAL disk exhaustion by monitoring replication slot lag. [src1]

SELECT slot_name, active,
  pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS lag_size
FROM pg_replication_slots WHERE slot_name = 'debezium_erp';

Verify: lag_size should be < 1 GB under normal operation.

Code Examples

Python: Subscribe to Salesforce CDC events via Pub/Sub API

# Input:  Salesforce Connected App credentials
# Output: Stream of change events for subscribed Salesforce objects

import requests, json

def get_salesforce_token(client_id, client_secret, username, password, security_token):
    resp = requests.post("https://login.salesforce.com/services/oauth2/token", data={
        "grant_type": "password", "client_id": client_id,
        "client_secret": client_secret,
        "username": username, "password": password + security_token
    })
    resp.raise_for_status()
    data = resp.json()
    return data["access_token"], data["instance_url"]

def process_change_event(event):
    header = event.get("ChangeEventHeader", {})
    return {
        "entity": header.get("entityName"),
        "operation": header.get("changeType"),
        "record_ids": header.get("recordIds"),
        "changed_fields": header.get("changedFields"),
    }

JavaScript/Node.js: Debezium CDC event consumer via Kafka

// Input:  Kafka cluster, Debezium topic
// Output: Processed change events with operation type

const { Kafka } = require('kafkajs'); // [email protected]
const kafka = new Kafka({ clientId: 'erp-cdc', brokers: ['kafka:9092'] });
const consumer = kafka.consumer({ groupId: 'erp-sync-group' });

async function consumeCDCEvents() {
  await consumer.connect();
  await consumer.subscribe({ topics: [/^erp\.public\./], fromBeginning: false });
  await consumer.run({
    eachMessage: async ({ topic, message }) => {
      const value = JSON.parse(message.value.toString());
      const { op, before, after, source } = value;
      // op: 'c'=create, 'u'=update, 'd'=delete, 'r'=snapshot
      if (op === 'd') await deleteFromTarget(source.table, before);
      else await upsertToTarget(source.table, after);
    },
  });
}

cURL: Check Debezium connector status

# List all connectors
curl -s http://localhost:8083/connectors | jq .

# Check specific connector
curl -s http://localhost:8083/connectors/erp-postgres-cdc/status | jq .

# Pause / Resume / Delete
curl -X PUT http://localhost:8083/connectors/erp-postgres-cdc/pause
curl -X PUT http://localhost:8083/connectors/erp-postgres-cdc/resume
# WARNING: Delete does NOT drop replication slot
curl -X DELETE http://localhost:8083/connectors/erp-postgres-cdc

Data Mapping

Debezium Event Structure

FieldTypeDescriptionGotcha
opString (c/u/d/r)Operation typeSnapshot events use 'r', not 'c'
beforeObjectRecord state BEFORE changeNeeds REPLICA IDENTITY FULL for PG
afterObjectRecord state AFTER changeNull for deletes
source.ts_msLongSource DB timestampUse for ordering, NOT Kafka timestamp
source.lsnLongLog Sequence NumberMonotonic — use for deduplication
ts_msLongDebezium processing timeDelta from source.ts_ms = replication lag

Data Type Gotchas

Error Handling & Failure Points

Common Error Codes

ErrorSourceCauseResolution
replication slot already existsDebezium/PGConnector re-created without dropping slotSELECT pg_drop_replication_slot('slot_name');
ORA-01291: missing logfileDebezium/Oracle or GGArchive log purged before CDC readIncrease archive log retention
INSUFFICIENT_ACCESSSalesforce CDCMissing CDC permissionAssign "Subscribe to CDC Events" perm set
Task is in FAILED stateDebeziumConnector task crashedCheck status endpoint; restart task
WAL segment removedDebezium/PGSlot fell too far behindFull re-snapshot required
GG-01031 Extract abendedGoldenGateExtract process crashedCheck error log; START EXTRACT

Failure Points in Production

Anti-Patterns

Wrong: Polling an ERP database on a timer

# BAD — Constant load, misses deletes, timestamp gaps lose data
def poll_for_changes():
    cur.execute("SELECT * FROM orders WHERE updated_at > NOW() - INTERVAL '60s'")
    # Misses deletes, adds constant query load, 60s latency minimum

Correct: Log-based CDC with Debezium

# GOOD — Zero query load, captures deletes, sub-second latency
for message in kafka_consumer:
    event = message.value
    op = event['op']  # c=create, u=update, d=delete
    if op in ('c', 'u', 'r'): sync_to_target(event['after'])
    elif op == 'd': delete_from_target(event['before'])

Wrong: Using GoldenGate for non-Oracle targets

# BAD — 8 processors * $17,500 = $140,000 + 22% annual support
# Paying Oracle license for non-Oracle target database

Correct: Debezium for heterogeneous CDC

# GOOD — Oracle -> Debezium (LogMiner) -> Kafka -> JDBC Sink -> PostgreSQL
# Total license cost: $0 (infrastructure costs only)

Wrong: Treating Salesforce CDC as durable event stream

# BAD — Events retained 3 days only; downtime > 3 days = data loss
subscribe_to("/data/AccountChangeEvent")  # No durability guarantee

Correct: Buffer Salesforce CDC into Kafka

# GOOD — Persist to Kafka immediately for infinite retention
for event in sf_cdc_events:
    kafka_producer.send("sf-account-changes", event)
    # Now events durable beyond 3-day SF retention

Common Pitfalls

Diagnostic Commands

# Check all Debezium connector statuses
curl -s http://localhost:8083/connectors | jq -r '.[]' | while read c; do
  curl -s "http://localhost:8083/connectors/$c/status" | jq '{name: "'$c'", state: .connector.state}'
done

# PostgreSQL replication slot health
psql -c "SELECT slot_name, active, pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS lag FROM pg_replication_slots;"

# Kafka consumer group lag
kafka-consumer-groups.sh --bootstrap-server localhost:9092 --group erp-sync-group --describe

# Salesforce CDC event delivery check
curl -s "https://instance.salesforce.com/services/data/v66.0/limits" \
  -H "Authorization: Bearer $TOKEN" | jq '.DailyDeliveredPlatformEvents'

# SAP: Transaction ODQMON (delta queues), SLT_DASHBOARD (replication status)

Version History & Compatibility

ToolVersionReleaseStatusKey Changes
Debezium3.4.02025-12CurrentMariaDB GA, CockroachDB incubating
Debezium3.3.02025-10SupportedExactly-once for all core connectors
Debezium3.0.02024-10SupportedJava 17+, Kafka 3.x baseline
Debezium2.7.x2024-06EOLLast Java 11 compatible
GoldenGate23ai2024CurrentMicroservices architecture, REST API
GoldenGate21c2021SupportedClassic + Microservices
Salesforce CDCAPI v66.02026-02CurrentSpring '26; Pub/Sub API preferred
ADF SAP CDC2025-022025-02GAODP framework, CDS views, SLT

When to Use / When Not to Use

Use WhenDon't Use WhenUse Instead
Real-time replication from on-prem ERP with DB accessSaaS ERP with no DB accessAPI polling with timestamp filters
Need to capture DELETEsOnly need INSERT/UPDATE trackingTimestamp-based polling
High change volume (>10K/day)Low volume (<100/day)Simple REST API polling
Kafka already deployedNo Kafka and no budget for itDebezium Server to Kinesis/Pub/Sub
Oracle-to-Oracle with Oracle support neededHeterogeneous targetsDebezium (free, multi-target)
Salesforce record change trackingNeed >3 day replay windowBuffer SF CDC to Kafka

Cross-System Comparison

CapabilityDebeziumGoldenGateSalesforce CDCSAP ODP/SLTPolling
CDC MethodLog-basedLog-basedVendor eventsApp-layer deltaQuery-based
LatencySub-secondSub-secondSub-secondSecondsMinutes
Captures DeletesYesYesYesYesNo
DeliveryExactly-once (3.3+)At-least-onceAt-most-onceAt-least-onceBest-effort
DB ImpactMinimalMinimalNoneLowHigh
License CostFree$17,500/procIncludedSAP + SLTFree
Sources11 databasesOracle primarySalesforce onlySAP onlyAny with API
InfrastructureKafka clusterGG hubNoneSLT serverNone
Event RetentionConfigurableDisk-bound3 daysConfigurableN/A
Multi-TargetNative (Kafka)LimitedLimitedOne per queueN/A

Important Caveats

Related Units