The Ripper has four levers that move throughput, plus a handful of secondary knobs that trade latency for I/O. Most deployments need nothing more than the defaults; this page is for cases where the [LSN] line shows growing lag or operators want to plan capacity before going live.
Each worker thread holds a CDC session and round-robins through the tables assigned to it. Worker count is derived at startup:
total_workers = min(max_threads,
ceil(ntables / sessions_per_thread))
So the two threading knobs together set both the parallelism ceiling and the per-worker fan-out. If the startup log warns that some tables didn't get assigned, the cap fired — raise max_threads or pack more tables per worker.
| Knob | Default | Raise when | Cost |
|---|---|---|---|
| threading.max_threads | 4 | Lag is rising and CPU on the Ripper host is mostly idle; or startup warns about tables not getting a worker. | One CDC session and one ESQL/C connection per worker on the source server. Watch onstat -g ses on the source if you push this over ~16. |
| threading.sessions_per_thread | 10 | Many small / mostly-idle tables; lets one worker poll a wide set without burning extra sessions. | A busy table sharing a worker with quiet ones gets serviced in round-robin; a hot table on its own worker is always faster. |
| threading.max_recs_per_read | 1 | Per-record overhead dominates — high commit rate, small transactions, lag growing while CPU and network are light. | A partial response stalls until either max_recs is reached or cdc_timeout fires, so the CDC_REC_TIMEOUT tick is delayed by up to that interval. Don't combine high max_recs_per_read with high cdc_timeout. |
| threading.cdc_timeout | 5 sec | Need quicker reaction to SIGTERM or sentinel-file polling; lower this so the worker wakes more often. | Lower means more ifx_lo_read wake-ups per second when the source is idle. Setting 0 = block forever — only safe when you don't need responsive shutdown. |
The single most common cause of lag is too little logical-log headroom on the source server. The Ripper can only consume what's still on disk: once Informix recycles a log past the Ripper's read position, that window's records turn into CDC_REC_DISCARD and are permanently lost.
| Source-side knob | Effect |
|---|---|
| Logical log size × count (onparams -a -d <dbspace> -s <size>) | Sets how many bytes of redo are reachable to the Ripper. The [LSN] lag line is in bytes; if lag_max ever approaches your total logical-log capacity, you are one commit away from a discard. |
| LTAPEDEV / log archival cadence | If logs aren't being archived they can't be recycled; if they are, archive frequency caps the Ripper's reachable window. |
| Source CDC buffer pool | The CDC layer on the source server has its own buffer pool; the CDC Error Codes page lists the error codes that fire when it overruns. |
The Ripper polls sysmaster:syslogs on the same cadence as [STATUS] and emits a single line per status tick:
[LSN 2026-04-27 14:49:35] source=0x43006d0000 min_processed=0x43006c220c
max_processed=0x43006c220c lag_max=56820 lag_min=56820
| Field | Meaning |
|---|---|
| source | Current head LSN on the source — the value Informix would assign to the next commit. |
| min_processed / max_processed | Slowest and fastest worker's last_committed_lsn. A wide spread between the two means one worker is stalled on a hot table while others are caught up. |
| lag_max | Bytes between source and the slowest worker. This is the number that will eventually trip a discard if it keeps growing. |
| lag_min | Bytes between source and the fastest worker. Steady, low lag_min with high lag_max = unbalanced workload. |
| Mode | What dominates | Tuning |
|---|---|---|
| file / json | One fopen/fwrite/fclose cycle per committed tx on the output filesystem. | Write to a local SSD, not NFS. Rotate / prune the output directory in a separate process; the Ripper never deletes its own output. |
| informix | One ESQL/C statement per row event, BEGIN WORK / COMMIT per tx. | Throughput is bounded by the target server's commit rate. Same-host or low-RTT network is much faster than a transcontinental link. |
| odbc | One SQLExecDirect per row event; SQLSTATE check after each. | The driver matters. Native PostgreSQL driver (psqlodbc) and MariaDB connector are both fine; some generic ODBC bridges add per-statement overhead. Check with the target's slow-query log. |
| YAML key | Default | Notes |
|---|---|---|
| logging.status_interval | 0 (off) | Cheap: a single computed line per worker plus one [LSN] line. Set to 60 in production for the operational signal; 1 only when actively debugging. |
| logging.log_maxsize | 0 (unlimited) | Bytes; trips a rotation when exceeded. 10 MB is a reasonable production value alongside log_keep: 5. |
| logging.log_keep | 5 | Rotated copies retained. log_keep: 0 reverts to the legacy "truncate in place" behavior. |
| Debug flags (debug_sql, debug_cdc, debug_detail, debug_hex) | off | Each adds output proportional to capture rate. debug_hex can multiply log volume by 10× or more; off in production. |
A practical starting point for a fresh deployment:
| Tables | Workload shape | Suggested max_threads × sessions_per_thread |
|---|---|---|
| 1–10 | any | 2 × 10 (defaults are fine; the cap rarely fires here) |
| 10–50 | mixed hot / cold | 4 × 12 (default 4 threads, raise sessions_per_thread so all tables fit) |
| 50–200 | mostly cold + a handful of hot | 8 × 25, with the busy tables grouped onto their own workers via the tables: ordering |
| > 200 | any | Watch the source CDC buffer pool first; CDC throughput on the source server, not the Ripper, is the bottleneck. Scale workers to keep lag_max bounded but don't expect linear speedup. |
After any tuning change, watch [LSN] lag_max across one full peak-traffic window. If it stays flat or falls, you're sized; if it rises, the next bottleneck is the one most recently touched.
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