Skip to content

Driver: single-owner engine loop, field conflicts, canonical numbers (DEV-1150 part D) - #51

Merged
adamski merged 31 commits into
mainfrom
adam/dev-1150-driver
Sep 29, 2026
Merged

adamski merged 31 commits into
mainfrom
adam/dev-1150-driver

Conversation

@adamski

@adamski adamski commented Sep 29, 2026

Copy link
Copy Markdown
Collaborator

DEV-1150 part D: the single-owner driver that runs the core against the store, socket and timers. It is stacked on #50 (store), which is stacked on #49 (transport) and #48 (engine core). Merge them in order: #48, #49, #50, then this one. Spec rev 8 describes this branch.

What's in it

  • Owner loop (driver::engine):
    • One task owns the core, the store, the connection and the timers. It uses a biased select: a socket event that has already arrived is handled before a timer due at the same moment.
    • Effects run in order from one FIFO queue. Database answers are tagged with the socket generation, so a stale answer never reaches a newer connection.
    • Engine::stop never waits on the network.
  • Credentials: after auth_invalid, the engine records the credentials that signed the rejected join, not the most recently loaded ones. Only the 5-minute retry compares against them.
  • Dial cooldown: each dial starts a 1 s cooldown. A Reconnect command during it resets the backoff and dials 1 s later.
  • Uploads:
    • An upload's rows are marked sent before its frame goes out.
    • A snapshot rebases edits that were never sent. Only a sent upload with no acknowledgement takes the conflict path.
    • Uploads wait while the own scope is resynced by snapshot.
    • A lost reply waits for the change stream before the server copy is applied.
    • Store errors while loading, building or marking uploads back off and retry. Rows never wait for an unrelated event.
  • Field-level conflicts: when two devices change the same field, only the colliding fields are kept aside, in recovered with reason field_conflict and their paths. Other edits merge. The host gets FieldConflict { paths }, and restore_fields writes the kept values back.
  • Kept copies: nothing deletes them on a timer. They stay until dismissed, via list_recovered and dismiss_recovered.
  • Number canonicalisation: integral floats are stored as integers when server content is decoded and when the host writes. Float fields no longer cause false conflicts or extra re-uploads.
  • Host events: Changed carries its origin: Local, OtherProcess or Server. Doc notices and DatabaseChanged are also reported.
  • Test infrastructure: a scripted protocol v2 server that matches replicant-server's handling of logs, cursors, idempotency and floats. It has switches to lose, hold, reject or drop replies and connections.

Testing

  • cargo test -p replicant-client --lib: 491 passed. --test wire_contract_tests: 15 passed. --test v2_smoke --no-run compiles.
  • The branch includes end-to-end tests of a real engine against the scripted server, reconnect-storm tests (at most 6 dials in 30 s against a server that accepts and then drops; at most 1 dial per second under a Reconnect flood; no socket leaks on any failure path), and cross-process host-event tests.
  • The randomised property test passes at 20,000 seeds.
  • Each fix was checked by deliberately reintroducing the bug.
  • clippy reports only the existing large_enum_variant.

…e more catch-up; fuzz exercises the SocketOpened generation guard
…dials carry a User-Agent, credentials change for the next join
…se-conflict hits by source, and matches the server's catch-up-page drop of upserts for a since-deleted document
…ck, so float fields never conflict or re-upload
…failed build or mark backs the document off, a failed pending load reschedules the pump
…k, assert the adopted shadow content; drop plan references from comments
@adamski
adamski changed the base branch from adam/dev-1150-store to main September 29, 2026 21:29
@adamski adamski closed this Sep 29, 2026
@adamski adamski reopened this Sep 29, 2026
@adamski
adamski merged commit 6552788 into main Sep 29, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant