mirror of
https://github.com/element-hq/synapse.git
synced 2026-11-04 11:17:51 +00:00
At `full`, `_persist_event_batch` hands the batch to `Persister.persist_batch` and steps through it with `batch_next`: - The first step drops the events already persisted under the same transaction ID (`get_already_persisted_events`). - Each step prepares the next chunk of up to 100 events, marks the batch's state groups as being persisted (`persisting_state_group_references`, two short native transactions), takes the chunk's stream orderings and writes it. - Python applies what the chunk asks for, runs `_after_persisting_events` and finishes the orderings before the next step, which first releases the state groups. So each chunk is prepared against current caches. - If Python cannot apply a chunk, `batch_abort` releases the state groups as a failed persist does. A chunk Rust cannot prepare (a state resolution fallback, or an error) is handed back with the rest of the batch to the chunk-by-chunk path, which prepares it again and falls back or raises as at lower levels. Python drives the steps, rather than Rust calling back into Python, so every step has a `_blocking` twin for tests and nothing waits on the reactor from Rust. That costs the same hops as a callback: one in and one out per chunk. The prepare inputs are now built from `Event`s that share their data with the Python events, so a chunk's input can be built without the GIL once the batch has been deduplicated. The persister's Deferreds already follow the logcontext rules, so they are awaited directly. Wrapping them in `make_deferred_yieldable` again, as the `prepare_chunk`, `persist_events_txn` and `persist_chunk` call sites did, resumed the caller in the sentinel context. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LXMkcZVkcZhi21augBff4a