Define the replication scheduling service boundary

This commit is contained in:
vorotamoroz
2026-08-28 04:10:48 +00:00
parent 22b0cc133a
commit fc160ee060
2 changed files with 72 additions and 20 deletions
@@ -130,16 +130,38 @@ The selected provider and its active Replicator decide **how**, and whether,
that request can be performed. A provider module must not subscribe to the
application resume lifecycle merely because it can construct a transport.
Self-hosted LiveSync will have one LiveSync-owned core ServiceModule as the
replication lifecycle coordinator. It uses `AppLifecycleService`, persisted
settings, `ReplicationService`, and the active support declaration. It does
not branch on `remoteType` or use `instanceof` as a capability test.
Commonlib owns the trigger-aware replication contract; the host owns
application lifecycle wiring.
Self-hosted LiveSync will compose one LiveSync-owned serviceFeature as the
replication scheduling boundary. The serviceFeature constructs one private
scheduling controller and connects it to `AppLifecycleService`, settings
lifecycle events, and the periodic timer. The controller owns only scheduling
state and transitions: external-poller ownership, Continuous ownership of
recurring work, the daemon's satisfied initial OneShot marker, resume
coalescing, and periodic-timer reconciliation. It does not register handlers
or acquire `LiveSyncBaseCore`.
The controller receives narrow collaborators for readiness and suspension
queries, current settings, `ReplicationService`, periodic-timer control, and
diagnostic logging. The surrounding serviceFeature owns lifecycle registration
and adapts those Services to the controller. It returns a focused control view
containing only the daemon operations to select external polling and mark the
initial OneShot as satisfied. The host may retain that view for the CLI, but
must not expose the controller's mutable state or recover it from a core-keyed
global or `WeakMap`.
This boundary is not a ServiceModule merely because it owns state. It neither
owns a shared external resource nor supplies a general operational capability
to several unrelated consumers. If a future consumer needs a stable shared
scheduling capability beyond the focused CLI view, that ownership decision
must be reviewed explicitly rather than widening the controller implicitly.
The scheduling controller uses persisted settings, `ReplicationService`, and
the active support declaration. It does not branch on `remoteType` or use
`instanceof` as a capability test. Commonlib owns the trigger-aware replication
contract; the host owns application lifecycle wiring.
The existing `onResumed` event remains the eligible-resume boundary after
initial readiness, settings application, and visibility recovery. It is not
redefined as a once-per-process event. The coordinator coalesces duplicate work
redefined as a once-per-process event. The controller coalesces duplicate work
within one lifecycle generation and preserves readiness and suspension gates:
- configured Continuous replication starts only through an active Continuous
@@ -155,8 +177,8 @@ within one lifecycle generation and preserves readiness and suspension gates:
`ReplicationService` remains responsible for readiness checks, bounded finite
activity, failure processing, and replication timing. It exposes distinct
user-initiated and unattended entry points, or a typed request which carries
interaction authority. The coordinator never calls a concrete Replicator's
`openReplication()` directly.
interaction authority. The scheduling controller never calls a concrete
Replicator's `openReplication()` directly.
`P2P_AutoStart` remains a separate P2P room policy. It is not central
Continuous replication and is not `syncOnStart`; its service lifecycle is
@@ -164,14 +186,30 @@ specified in Part 2. Reopening after `EVENT_DATABASE_REBUILT` is a
flow-authorised continuation requested by the Rebuilder, not evidence that
AutoStart is enabled.
Correctness must not depend on the registration order of equal-priority resume
handlers. P2P AutoStart records persistent room demand, while an unattended P2P
OneShot records finite room demand. `P2PRoomSessionOwner` serialises both and
retains the room while either demand remains. Provider-specific P2P lifecycle
wiring and host replication scheduling may therefore run in either order.
Focused owner tests cover AutoStart-before-OneShot and OneShot-before-AutoStart;
the host feature-binding test must not encode their current registration order
as a scheduling prerequisite.
The CLI daemon owns its initial finite convergence before its mirror scan.
Restored settings mark that convergence as satisfied for the current lifecycle
generation, so `syncOnStart` does not repeat it. In `--interval` mode, the
daemon poller is the sole recurring remote-poll scheduler. In changes-feed mode,
the coordinator starts one configured Continuous session when supported;
the controller starts one configured Continuous session when supported;
otherwise it may enable the configured generic periodic timer. Continuous has
precedence when both are configured.
The controller starts resume work synchronously far enough to reserve
Continuous ownership, then lets the lifecycle handler settle without awaiting
network completion. Concurrent resume notifications share one internal
operation. Periodic reconciliation therefore observes the reservation before
it can enable a competing timer. A failed operation is logged and releases the
coalescing slot so a later resume can retry.
### Use a fixed current-provider definition
Commonlib defines the canonical current remote kinds, provider contract,
@@ -183,7 +221,7 @@ API.
CouchDB is part of every current host composition. Object Storage and P2P are
compile-time composition choices and may be included or omitted without
changing the generic lifecycle coordinator. Adding another current provider
changing the generic scheduling feature. Adding another current provider
requires a Commonlib kind and support declaration, host composition,
Setup/profile schema handling, and provider-specific tests. It does not require
a runtime plug-in registry or behaviour for unknown provider kinds.
@@ -60,12 +60,14 @@ Object Storage handling, add a failing test in which a stopped or failed
journal transfer must not produce a completed outcome; then propagate the
actual journal result.
Add the LiveSync-owned lifecycle coordinator, remove the resume handler from
`ModuleReplicatorCouchDB`, and route CouchDB Continuous and OneShot Sync plus
Object Storage `syncOnStart` through `ReplicationService`. Migrate every
automatic caller to the unattended entry point in this stage, so periodic and
event calls cannot fall back to an interactive P2P role. Migrate manual commands
to the user-initiated entry point.
Add the LiveSync-owned replication scheduling serviceFeature, remove the resume
handler from `ModuleReplicatorCouchDB`, and route CouchDB Continuous and OneShot
Sync plus Object Storage `syncOnStart` through `ReplicationService`. Its private
controller owns scheduling state and transition order; the serviceFeature owns
lifecycle and settings-handler registration. Migrate every automatic caller to
the unattended entry point in this stage, so periodic and event calls cannot
fall back to an interactive P2P role. Migrate manual commands to the
user-initiated entry point.
Replace the factory-registration-only responsibilities of
`ModuleReplicatorCouchDB` and `ModuleReplicatorMinIO` with composed provider
@@ -79,10 +81,12 @@ snapshot. This is the minimum publication fence for this stage. Waiting for
in-flight adapter work and making acquisitions wait for replacement settlement
remain part of the later active-construction migration.
The lifecycle coordinator coalesces its network work internally, but an
The scheduling controller coalesces its network work internally, but an
`onResumed` handler settles once that work has been scheduled. It does not hold
later resume consumers until a OneShot transfer or Continuous start has
settled.
settled. Compose the controller with narrow replication, settings, lifecycle,
timer, and logging collaborators. Return only the daemon-facing control view;
do not retain scheduling state in a core-keyed `WeakMap`.
At this boundary, existing P2P AutoSync, AutoWatch, and incoming-request
entry points receive the same non-interactive readiness and accepted-peer gate.
@@ -90,6 +94,12 @@ The no-interaction authority reaches counterpart RPC authorisation and
broadcast progress notifications. An unknown peer is blocked rather than
prompting.
Add focused Commonlib owner tests which start an unattended finite room demand
before AutoStart demand and after AutoStart demand. Both orders retain one room
until every remaining demand has settled. The LiveSync feature-binding test
must not rely on the current registration order of equal-priority resume
handlers.
Until Stage 4 supplies target-aware unattended P2P, each host composition
declares generic `P2P_SyncOnReplication` as `not-implemented`. Its automatic
request settles without UI with an explicit blocked result. Existing AutoSync,
@@ -97,7 +107,11 @@ AutoWatch, and accepted incoming-request paths continue with the Stage 2 gate.
This is a temporary migration state, not the target matrix in Part 1.
Apply and test the CLI scheduling precedence defined in Part 1, so the daemon
and lifecycle coordinator cannot schedule duplicate initial or recurring work.
and scheduling controller cannot schedule duplicate initial or recurring work.
Replace `ModuleReplicationLifecycle` and the replication-specific
`ModulePeriodicProcess` wiring only after equivalent controller and feature-
binding tests pass. Reuse the existing timer implementation behind a narrow
timer port; changing other periodic feature owners is outside this stage.
## Stage 3: make P2P transport ownership truthful