The Ripper validates every CDC TABSCHEMA record against the live DESCRIBE snapshot it took at startup. The check is run in three layers, walked in order — the cheap structural test first, deeper checks only when the previous layer matches:
| Layer | What it catches |
|---|---|
| (total_cols, var_cols, fix_bytes) triple | Added or dropped columns and most type changes that move the totals. |
| Per-column NAME (parses the coldesc text) | RENAME COLUMN and any same-shape change that preserves the totals. |
| Per-column TYPE keyword | Same-byte-width type swaps like INT↔DATE (4 bytes) or BIGINT↔FLOAT (8 bytes). |
| Situation | Outcome |
|---|---|
| Configured table missing from source DB at startup | [ARCHIVE-FALLBACK] — load layout from <dir>/<db>_<tab>.sql and continue startup. If the live table is truly absent, cdc_set_fullrowlogging fails and the table is [CRITICAL] skip_capture'd; the rest of the worker carries on. The operator can drop an acquire_<tab> sentinel once the table is recreated to re-register it. |
| First TABSCHEMA mismatches | Stale config — worker aborts. |
| Subsequent mismatch | ALTER mid-capture — try archive recovery. |
| Archive matches stream | Swap tab->columns to archived layout, keep capturing. |
| Neither archive nor live match | [CRITICAL] set skip_capture=1, drop further events for that table only. |
| DDL changed multiple times mid-run | The archive directory may carry several historical layouts per table named <db>_<tab>.<YYYYMMDDTHHMMSSZ>.sql (UTC timestamp = the moment that DDL became effective on the source). The plain <db>_<tab>.sql is treated as "current / +∞". When the operator triggers acquire_<tab> after a DDL change, the Ripper picks the file with the largest timestamp ≤ the most recent BEGINTX time the worker has seen and loads that one as the archive layout. |
Set schema_archive_dir in the YAML to point at a directory of <db>_<tab>.sql files. On drift, the Ripper walks through the recovery steps in order:
| Step | Action |
|---|---|
| 1 | Find the archive matching the affected table. |
| 2 | Run the archived DDL in the source DB to reconstruct the archived layout. |
| 3 | DESCRIBE the reconstructed table and compare against the incoming TABSCHEMA. |
| 4 | If it matches, swap tab->columns to the archived layout and resume capture. |
At startup, when an archive is present the Ripper additionally walks it column-by-column against the live DESCRIBE as a third safety net. Pure rename or type swaps that preserve the (cols, var, fix_bytes) signature and the per-column NAMES are flagged as [ARCHIVE-CMP] divergences and escalated to [CRITICAL] when the snapshot signatures match exactly.
To ALTER a single table without stopping the ripper, drop sentinel files in control_dir and the per-worker poll picks them up:
# Pause capture on t_customer (ripper drops cdc_endcapture + # cdc_set_fullrowlogging(0)) oni_logripper_control -d /var/lib/oni_ripper/control release t_customer # Now ALTER TABLE t_customer ... succeeds because the table is released # Resume capture under the new schema (re-DESCRIBE + cdc_startcapture) oni_logripper_control -d /var/lib/oni_ripper/control acquire t_customer
Every other table in the capture set keeps capturing uninterrupted. The ripper itself never restarts.
When the acquire path detects a column-level diff between the pre-release and post-acquire schemas, the ripper emits the corresponding ALTER TABLE as a synthetic single-record transaction (txid=-1) directly into the captured SQL stream before the next DML record under the new schema arrives. The replay target re-applies the same migration in order and remains schema-compatible with the source.
Two text sources, tried in order. (1) If the archive DDL file contains literal ALTER TABLE statements, they are extracted and emitted verbatim — operator-authored ALTERs preserve nuance the auto-differ cannot reconstruct (column rename, table-level constraint changes, index-only changes). (2) Otherwise the auto- differ compares the old and new cdc_column_t arrays and emits column-level ADD / DROP / MODIFY.
Example output for a real release/ALTER/acquire cycle that added location VARCHAR(60) to t_drift_b:
-- Transaction -1 (worker 0) -- Status: complete -- Worker: 0 -- User: (schema-migration) (uid=0) -- Started: (unknown) -- Committed: (unknown) -- Begin LSN: 0 -- Commit LSN: 0 -- Records: 1 BEGIN WORK; -- LSN: 0 ALTER TABLE t_drift_b ADD location VARCHAR(60); COMMIT WORK;
Filename convention <ts>_w<worker>_tx_-1.sql makes synthetic schema-migration files easy to grep for separately from real captured transactions. Direct-DB targets (informix / odbc / postgres / mysql / mariadb / db2) execute the ALTER bare — no BEGIN/COMMIT framing — because DDL auto-commits in most engines and rejects framing in some.
Auto-differ limitations: column rename is undetectable (looks like DROP+ADD); table-level constraints (PK/FK/CHECK) are not in cdc_column_t and so are not reproduced; non-default DATETIME / INTERVAL qualifiers fall through to YEAR TO SECOND / DAY TO SECOND. For any of these, place an explicit ALTER TABLE statement in the archive file and the verbatim path runs.
If neither the live DESCRIBE nor the archive can parse the incoming stream, the ripper logs [CRITICAL], sets tab->skip_capture=1, and drops further row events for that table only. The other tables keep capturing. The ripper exits only when every monitored table is in skip_capture.
The Ripper's main CDC capture path is unaffected by Informix's Encryption At Rest (EAR) feature — the engine decrypts pages transparently at the buffer-pool layer before CDC ever sees them. EAR-encrypted dbspaces (V14 EAR; bit 0x10000000 on sysmaster:sysdbspaces.flags) capture exactly like unencrypted dbspaces for every CDC-supported column type.
The exception is the raw-log fallback path used to recover column types that cdc_startcapture rejects (TEXT / BYTE / BLOB / CLOB / SET / MULTISET / LIST / ROW / SQLUDTVAR) by reading the logical-log pages directly via pread(O_RDONLY) against the chunk file. This path bypasses the engine's buffer pool and therefore its transparent decrypt step; on an encrypted chunk pread returns ciphertext that fails every downstream parse. The Ripper supports encrypted log chunks on this path by decrypting them page-by-page itself, gated on the operator configuring the Informix keystore.
Point the Ripper at the Informix keystore base path:
security: decrypt_keystore: /home/informix/<server>/etc/db_keystore
The path is the base — no .p12 or .sth suffix; the underlying library appends both. Per-dbspace decrypt contexts open lazily on first encrypted-page access, so the keystore-unwrap cost is paid only when an encrypted chunk is actually read.
At startup the log-fallback pre-flight scans every log-bearing chunk's dbspace for the EAR bit. Three outcomes:
| Situation | Outcome |
|---|---|
| No encrypted log-bearing chunks | Proceed; no decrypt configured. |
| Encrypted chunk(s) + decrypt_keystore set | Proceed; INFO line names the chunks and the keystore; per-dbspace contexts open on first access. |
| Encrypted chunk(s) + decrypt_keystore unset | CRITICAL refuse-to-start naming the offending chunk and three operator-actionable choices: (a) configure the keystore; (b) remove recover_unsupported_via_log: true from the affected tables (falls back to skip_unsupported_columns behaviour); (c) move the captured tables to a non-encrypted dbspace. |
The main CDC path is untouched by all of this. Even when the fallback refuses to start, an operator who removes recover_unsupported_via_log: true from the affected tables can still capture every CDC-supported column type from the same encrypted dbspaces without configuring a keystore at all.
On graceful shutdown the ripper writes min(last_committed_lsn) across all workers to the configured state_file. The next startup reads it and resumes via cdc_activatesess at that position — at-least-once semantics around the boundary, never data loss. -R on the CLI forces a clean restart for one run while still rewriting the file at exit.
To discuss how Oninit ® can assist please call on +1-913-732-8892 or alternatively just send an email specifying your requirements.
You get all this for free.. think about what you get if you pay us