perf/harden: reconcile-hourly loads unbounded read tables + double-writes per new reconciliation #60

Closed
opened 2026-07-04 12:11:58 +00:00 by momsse · 0 comments
Owner

Migré depuis viziertronic/octant#134 — ouvert le 2026-06-28 par @momsse.

Surfaced by the local /code-review during #55 (pre-existing; not introduced there).

ReconcileHourlyUseCaseImpl.execute (reconcile-hourly.use-case.ts):

  1. Unbounded reads each tickfindAllMatchCandidates() + findAllReconciliations() pull the entire tables into the API heap and rebuild Sets over all rows every hourly run; cost grows without bound as the tables accumulate. A candidate query bounded to unreconciled/pending rows (or batched pagination) would cap per-tick cost.
  2. Double event-store round-trip per new reconciliation — for a freshly proposed candidate, proposeReconciliation then establishReconciliation issue two separate executeCommand calls on the same reconciliation stream (the establish re-reads what propose just wrote). A single decide emitting Created + Established (or reusing the loaded state) halves the write I/O.
  3. Stale exclusivity sets (plausible) — the establish decision reads the async establishedTransactionIds/establishedDocumentIds derived from the read models, so a just-established (e.g. manual confirm) reconciliation not yet projected can let the job establish a second one for the same transaction/document until the supersede saga (#58) converges. Aligns with the write-side-no-async-read-models principle.

Relates to #46 (saga-error helper), #58 (supersession convergence).

> _Migré depuis [viziertronic/octant#134](https://github.com/viziertronic/octant/issues/134) — ouvert le 2026-06-28 par @momsse._ Surfaced by the local `/code-review` during #55 (pre-existing; not introduced there). `ReconcileHourlyUseCaseImpl.execute` (`reconcile-hourly.use-case.ts`): 1. **Unbounded reads each tick** — `findAllMatchCandidates()` + `findAllReconciliations()` pull the *entire* tables into the API heap and rebuild Sets over all rows every hourly run; cost grows without bound as the tables accumulate. A candidate query bounded to unreconciled/pending rows (or batched pagination) would cap per-tick cost. 2. **Double event-store round-trip per new reconciliation** — for a freshly proposed candidate, `proposeReconciliation` then `establishReconciliation` issue two separate `executeCommand` calls on the same reconciliation stream (the establish re-reads what propose just wrote). A single decide emitting `Created` + `Established` (or reusing the loaded state) halves the write I/O. 3. **Stale exclusivity sets (plausible)** — the establish decision reads the async `establishedTransactionIds`/`establishedDocumentIds` derived from the read models, so a just-established (e.g. manual confirm) reconciliation not yet projected can let the job establish a second one for the same transaction/document until the supersede saga (#58) converges. Aligns with the write-side-no-async-read-models principle. Relates to #46 (saga-error helper), #58 (supersession convergence).
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
momsse/octant#60
No description provided.