VentStream writes to a target:
OpenSearch / Elasticsearch (one connector —
they share the
_bulk API), Meilisearch, or
Redis. The examples here use OpenSearch; set the
endpoint with VS_OS_ENDPOINT.Source requirements
Change streams require a replica set or sharded cluster — they are not supported on a standalonemongod. Works on self-hosted MongoDB 4.0+ and
Atlas (mongodb+srv://).
Connect via the replica-set URI or a mongos. The engine’s user needs read
access plus the change-stream / find privileges on the watched database
(read on the database is enough on most deployments).
How it works
- Trigger, then re-read. Each change event is only a trigger. With
fullDocument: updateLookup(the default) the source carries the current whole document on every insert/update/replace, so the sink always reflects the latest state — the change payload never needs to be complete. - Bootstrap. On cold start the source captures a change-stream resume
token, scans every in-scope collection by
_id(paged), emits each document as an insert, then tails the change stream from the captured token. A row mutated during the scan is re-emitted by the tail and de-duplicated by the deterministic doc id at the (idempotent) sink. - Resume. The resume token is persisted to a file in
VS_MONGO_STATE_DIR(a PVC in Kubernetes), flushed everyVS_MONGO_TOKEN_FLUSH_MS. A restart resumes from there. If the token has aged out of the oplog, the source detects it, wipes the cursor, and re-bootstraps. Note the trade-off: a fresh snapshot cannot emit tombstones for documents deleted during the gap, so a sink document whose source was deleted while the token was expired survives the re-bootstrap. Reconcile the destination after such a gap if stale documents matter. - Deletes become tombstones — the deterministic doc id targets the exact document to remove.
Ordering across parallel writes
Every change event carries the change’sclusterTime — the oplog’s own
ordering key — as ventstream.cdc.source_version (packed as
time << 31 | increment, which fits a signed 64-bit sink version through
the year 2106). Sinks that version documents (OpenSearch with
external_gte, Redis versioned keys) reject a write older than the one they
hold, so the dispatcher can run bulks in parallel (max_parallel_bulks > 1)
without two updates to one document landing in the wrong order, and a delete
can never be undone by a stale re-insert that arrives after it. Bootstrap
documents carry the cluster’s operationTime at scan start as a floor: a live
write that commits during the scan outranks the snapshot row whichever lands
first.
The _id mapping
The sink document id is the canonical, namespaced form
{database}.{collection}:["<_id>"] (e.g. shop.orders:["64f0…"]),
matching the Postgres source’s shop.orders:["…"] shape. An ObjectId is
rendered as its plain hex string; DateTime as RFC 3339; Decimal128 as its
decimal string.
Run the engine
VS_MONGO_COLLECTIONS is optional — omit it to watch every collection in
the database. The index template renders one index per collection
(orders, customers, …).
The equivalent canonical config file (VS_ENGINE_CONFIG=./ventstream.yaml):
TLS
Atlas enables TLS through its connection string and uses publicly trusted certificates. To enforce the same policy independently of URI options, set:tls settings in the URI. Strict mode validates
the server certificate and hostname.
Key environment variables
Full list in the engine env reference.
Index mapping for mixed-type numeric arrays
Current limitations
- Raw 1:1 only. One document → one sink document; there are no cross-collection joins yet, so a change to a referenced collection does not recompose a parent document.
- Replica set / sharded cluster required — change streams don’t run on a
standalone
mongod. - Oplog-bounded resume. A resume reaches back only as far as the oplog retains. Size the oplog to cover worst-case agent downtime, or the agent re-bootstraps after a long outage.