Docs refreshed for Astervis v1 — now covering AI call analytics. What’s new
Astervis Docsv1.0
Configuration

Real-time Sync (CDC)

How Astervis keeps in step with your PBX, powered by litcdc.

Astervis never reads from your PBX database directly. Instead, a tiny service called litcdc tails the MySQL/MariaDB binary log, copies every Call Detail Record (CDR) into Astervis's own TimescaleDB store, and keeps copying as new calls land — in real time. A call shows up on your dashboard within seconds of hanging up, and your PBX database is never touched by a query from Astervis.

This page explains what that pipeline does, how to check it's healthy, and what to tune if it ever falls behind. For most installs there's nothing to configure: the installer sets it all up and it just runs.

What CDC actually does

CDC stands for Change Data Capture. Rather than polling the cdr table for new rows, litcdc reads the same binary log that MySQL replication uses — the authoritative, ordered record of every change the database makes. That gives Astervis an exact, gapless copy of your call history without putting any read load on your PBX.

The flow looks like this:

Initial snapshot (historical backfill)

On its very first run, litcdc takes a single consistent snapshot of the existing cdr table. This is your historical backfill — every call already in your PBX is copied across, so your dashboards aren't empty on day one. The snapshot uses a brief read lock to pin a consistent point in the binlog, which is why the CDC database user is granted LOCK TABLES.

Realtime streaming

Once the snapshot finishes, litcdc switches to streaming: it reads new binlog events as they're written and forwards them onward. From here on, every new or updated CDR is captured the instant the PBX writes it.

Durable buffer

Captured events are written to a durable, bounded log on disk (an embedded redb store) before anything consumes them. This buffer is what makes the whole pipeline crash-safe.

HTTP long-poll delivery

The replication service connects to litcdc over a local HTTP long-poll feed, drains events from the buffer, and writes them into TimescaleDB. Long-poll means the consumer holds a request open until there's something to deliver — low latency, no busy waiting, and no message broker to operate.

litcdc replaced the old Debezium + Redpanda pipeline that earlier versions used. It's a single ~10–25 MiB Rust binary that does the job of a Java connector plus a Kafka-style broker — which is why the whole Astervis stack now fits comfortably in 2 GB of RAM.

Snapshot vs. streaming — and why it rarely re-snapshots

The snapshot is a one-time event. litcdc runs in initial snapshot mode: snapshot once, then stream forever. The key to that "forever" is the durable buffer, which lives on a host-mounted volume at /opt/astervis/litcdc_data. Because the buffer survives container restarts and full server reboots, litcdc remembers exactly where it was in the binlog and resumes streaming from that point — it does not re-snapshot.

This matters in practice: restarting the stack, upgrading the installer, or rebooting the host is cheap. litcdc picks up where it left off instead of re-copying your entire call history every time.

A re-snapshot only happens if the durable buffer is deleted or the binlog position it remembers is no longer available on the PBX. The installer sets MySQL expire_logs_days = 3, so as long as the stack is never down for more than a few days, litcdc always finds its position and resumes cleanly. Don't delete /opt/astervis/litcdc_data unless you intend to force a full re-backfill.

Checking sync status

The fastest health check is the installer's status command, which lists every container and its health at a glance:

sudo astervis-installer status

The two services that make up the sync pipeline are litcdc (container astervis-litcdc) and replication (container astervis-replication). Both should be running. If litcdc is healthy but call history is missing, the problem is almost always upstream — the MySQL binlog config or the CDC user's grants — rather than the consumer.

To look closer, read the logs directly. litcdc logs its snapshot progress and then a steady stream of captured events:

docker logs -f astervis-litcdc

During the initial run you'll see snapshot progress over the cdr table, then it flips to streaming binlog events. A row count climbing toward your total CDR count means the historical backfill is working.

litcdc's HTTP feed is published on the host at 127.0.0.1:18088 (it binds 8088 inside the Docker network, but host port 8088 belongs to Asterisk's own HTTP server on FreePBX, so it's mapped to 18088 outside). It's loopback-only and not meant for direct use, but it's handy as a quick liveness check:

curl -s http://127.0.0.1:18088/healthz

The simplest confirmation of all: make a test call on your PBX, hang up, and watch it appear in the call history view within a few seconds. That's the entire pipeline — binlog → litcdc → buffer → replication → TimescaleDB → dashboard — working end to end.

Buffer, backpressure, and retention

litcdc's durable buffer is bounded — it won't grow without limit and fill your disk. Two settings govern its behaviour, both set by the installer in /opt/astervis/docker-compose.yml:

SettingValueWhat it does
LITCDC_BUFFER_MAX_BYTES67108864 (64 MiB)The hard cap on the buffer. litcdc truncates events the consumer has already read once the buffer passes this size. If the consumer has fallen behind and nothing new has been read, litcdc pauses the binlog reader (backpressure) rather than overflowing.
REPLICATION_THROTTLE_MS0The delay the replication consumer waits between batches. Zero means drain at full speed — it keeps the buffer shallow and pulls events through as fast as TimescaleDB accepts them.

The combination is deliberate. The consumer drains with no throttle, so under normal load the buffer stays nearly empty and the 64 MiB cap is never approached. The cap exists purely as a safety valve: if the consumer stalls (database busy, container restarting), litcdc applies backpressure to the binlog reader instead of consuming unbounded memory or disk. Once the consumer recovers, the backlog drains and streaming resumes — and because the buffer is durable, no events are lost in the meantime.

REPLICATION_THROTTLE_MS=0 is the right value and you shouldn't need to change it. An earlier non-zero throttle was the reason backfills crawled; setting it to 0 took a ~64k-row table from roughly an hour down to about eight minutes. Raise it only if you have a specific reason to rate-limit writes into TimescaleDB.

Tuning notes

These values live in /opt/astervis/docker-compose.yml, in the litcdc and replication services. After editing, recreate just those two containers:

cd /opt/astervis
docker compose up -d --force-recreate litcdc replication

Running astervis-installer upgrade self-updates the installer binary — it does not regenerate docker-compose.yml. So if you need the latest CDC defaults on a server that was installed with an older version, you have to edit the compose file by hand (or do a fresh install). The defaults shipped from installer v0.0.84 onward are mem_limit: 256m, LITCDC_BUFFER_MAX_BYTES=67108864, and REPLICATION_THROTTLE_MS=0.

Troubleshooting

Where the pieces live

docker-compose.yml
.env

Last updated on

On this page