Architecture
Two crates:
crates/pg_automerge_core: all Automerge logic, plain Rust with no Postgres dependency, tested withcargo test: validation and normalization (normalize), merging and containment on stored bytes or loaded documents (loaded:Input,LoadedDoc,merge,merge_changes,contains,contains_changes), themerge_aggstate (MergeAccumulator), the stored-header parser behind the heads fast path and the change-chunk splitter (header), the scan that prices a load before Automerge allocates (budget, see Resource limits), the history functions (history), the jsonb mapping as a walk into aJsonSink(json), the notification payload builder (notify), text encodings (encoding), the counters of live documents and loads (stats, see Memory observability) and, with thetest-hooksfeature, test instrumentation (test_hooks). Every Automerge call runs under a panic guard (see Errors).- The root crate (
src/): the pgrx glue, one module per SQL area:datum.rs(the Rust types ofautomergearguments and results, and the prefix reads),expanded.rs(expanded values),io.rs(the type's SQL, I/O functions and casts),merge.rs(merge,||,merge_agg, the support function),introspect.rs(automerge_heads,automerge_contains),history.rs,notify.rs(the trigger),jsonb.rs(building jsonb from the core's walk),error.rs(raising errors), andalloc.rs(the counting global allocator) withmemory.rs(automerge_memory_usage,automerge_memory_reset). The glue converts datums, calls the core, and raises core errors with their SQLSTATE.
Data flow:
- Writes arrive as
byteaor text and pass the input functions, which validate and normalize them into stored bytes; or they go throughmerge, whose result is a loaded document (an expanded value) that is saved once, when Postgres stores it. - Reads (
::jsonb, history) load the stored bytes into an Automerge document per call;automerge_heads,automerge_change_count, the no-op checks and the notification trigger read only a prefix of the stored value (the header). - Notifications are built from the old and new row's headers by the
trigger and sent with
NOTIFY; listeners fetch what they lack withautomerge_changes_bytes.