dizzy.engine.replicate¶
dizzy.engine.replicate
¶
Anti-entropy replication + fold-on-replicate.
Pull-based, git-style: fetch the peer's heads, walk parent pointers backwards
until events we already hold (the stop-at-known idiom), verify every record's
content hash on arrival, add parents-first, then fold each NEW event through
the same projections local emits use — one code path, two triggers. The
event's ingested_at travels inside its hashed payload, so both time axes
survive the hop; replay equality across nodes follows from canonical order.
Transports are closures — fetch_heads() -> [id] and fetch_event(id) ->
Event — so a peer can be a file on the same disk, a USB stick, an NFS mount
or an HTTP endpoint without this module knowing which. Fetching is naive and
per-event; batching is an optimization for later.
Nothing here names a feature. :func:fold_envelopes takes the projection
runners and the reconstructor as arguments, because which projections fold
an event is the wiring's knowledge, not the replicator's.
pull(local, fetch_heads, fetch_event)
¶
Pull everything the peer has that we lack.
Returns the new envelopes in delivery order (parents-first), ready for
:func:fold_envelopes.
Source code in dizzy/src/dizzy/engine/replicate.py
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | |
fold_envelopes(envelopes, session, runners, reconstruct)
¶
Fold-on-replicate: replicated events run the SAME projections local
emits do, with their original ingested_at.
Confluent projections make arrival order and redelivery harmless, which is what lets a pull happen at any time. Returns the number of events folded.
runners maps event class -> [(name, runner)], the same shape the
engine registers; reconstruct turns an envelope back into an event
instance (EventStore.reconstruct_event is one).
Source code in dizzy/src/dizzy/engine/replicate.py
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | |
file_transport(peer_store_path)
¶
Read a peer's store file directly (same disk / USB / NFS).
Source code in dizzy/src/dizzy/engine/replicate.py
96 97 98 99 | |
http_transport(base_url, client=None)
¶
Speak to a peer over HTTP.
The peer serves two endpoints, which a host mounts in whatever framework it already runs — DIZZY does not ship a server, and would have to pick one:
GET {base}/replicate/heads
{"heads": [<event id>, ...]} — from :meth:EventStore.heads.
GET {base}/replicate/event/{id}
{"id", "type", "parents", "payload"} — from
:meth:EventStore.raw_event, which returns exactly those fields.
404 when the id is unknown.
client is injectable for tests — anything with .get(url) returning a
response with .json(). httpx is imported only if you do not supply one,
so it is the caller's dependency, not DIZZY's.
Source code in dizzy/src/dizzy/engine/replicate.py
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | |