exlab_wizard.sync.nas_client#
NAS sync client. Backend Spec §7.1, §7.3.
The NASSyncClient is the public surface of the NAS sync
subsystem. It wires together the durable queue, the transport drivers,
the SHA-256 verifier, the bandwidth scheduler, the cleanup interlocks,
and the Pre-Sync Gate.
Per §7.1 the client is an in-process module of the FastAPI app; there is no separate daemon. Workers are asyncio tasks; the queue file is the durable record so a server restart does not lose pending work.
Classes
|
Durable, per-equipment NAS sync queue with Pre-Sync Gate. |
|
Lightweight handle returned by |
- class exlab_wizard.sync.nas_client.NASSyncClient(*, config, queue_db, validator, cache_creation, sync_state_writer=None, keyring_store=None, worker_poll_interval_s=0.05, push_callable_factory=None, check_callable_factory=None, lsjson_callable_factory=None)[source]#
Bases:
objectDurable, per-equipment NAS sync queue with Pre-Sync Gate.
Backend Spec §7.1, §7.3.
Lifecycle:
init()opens the queue DB, replays any in-flight jobs, and starts a single background worker task.enqueue()runs the Pre-Sync Gate, gates the run if needed, and otherwise inserts aQUEUEDrow.close()cancels the worker and closes the DB.
The worker loop is a simple “pick the oldest QUEUED whose
next_attempt_athas passed” scheduler with at-most-one inflight job at a time. This keeps determinism for tests; production deployments can extend to per-equipment parallelism without changing the public API.- Parameters:
config (
Config)queue_db (
Path)validator (
Validator)cache_creation (
CreationWriter)sync_state_writer (
SyncStateWriter|None)keyring_store (
Any)worker_poll_interval_s (
float)push_callable_factory (
Callable[[EquipmentConfig],Callable[...,Any]] |None)check_callable_factory (
Callable[[EquipmentConfig],Callable[...,Any]] |None)lsjson_callable_factory (
Callable[[EquipmentConfig],Callable[...,Any]] |None)
- apply_config(config)[source]#
Swap the cached config + equipment map in place (no relaunch).
The
equipment_id -> EquipmentConfiglookup is rebuilt into a local before assignment so the worker loop – which runs in the same event loop – never observes a half-built map. In-flight queued jobs carry their own captured paths; a removed equipment id simply errors that one job exactly as it would after a relaunch.
- async enqueue(run_path, files=None)[source]#
Pre-Sync Gate -> if hard-tier finding without override, mark
sync_status='blocked_by_validation'. Otherwise insert aQUEUEDrow.files(operator-free per-file NAS sync, 2026-05-21) is the per-file subset of run-relative POSIX paths eligible at enqueue time; an empty / omitted list means “the whole run”.The queue holds one row per
run_path(UNIQUE). Re-enqueue behaviour:existing job in a terminal state (
VERIFIED/CLEANUP_ELIGIBLE/CLEANED/FAILED) and a non-emptyfileslist -> reset toQUEUEDcarrying the new subset. This is how a file modified after a prior verify gets re-synced.existing job in a terminal
VERIFIED/CLEANUP_ELIGIBLE/CLEANEDstate with an emptyfileslist -> falls through to a no-op: there is no subset to re-sync and a successfully-verified run is not blindly re-queued. (Only a terminalFAILEDrow with emptyfilesis re-armed – the manual-retry branch below.)existing job active (
QUEUED/RUNNING/AWAITING_VERIFY) -> no-op (newly settled files ride the next sweep).existing terminal
FAILEDjob with nofiles-> re-armed viareset_to_queuedso the manual-retry contract holds.no existing job -> insert a
QUEUEDrow withfiles.
Returns a
SyncJobHandle. The handle’sstateis eitherSyncHandleState.BLOCKEDorSyncHandleState.QUEUED.- Parameters:
- Return type:
- async force_verify(run_path)[source]#
Re-run
rclone check --downloadagainst the configured remote.Used by the Settings “verify integrity” action. Reports only – does NOT advance the queue state and does NOT update
verified_sha256insync_state.json(the rclone-only migration deliberately keeps Slot A SHA capture scoped to the sync-time path that has access to a freshly-read local copy).Resolves the equipment from
run_path’s first component, gathers every tracked file insync_state.jsonas the--files-fromsubset, and asks the driver to compare. Returns a populatedVerifyResult; the caller rendersmismatched,missing, anderrorsto the operator. A run with no tracked files yieldsok=True(nothing to verify).- Parameters:
run_path (
Path)- Return type:
- async status(run_path)[source]#
Return the queue state of the job for
run_path."none"when no job exists; otherwise the underlyingSyncJobStatevalue.
- class exlab_wizard.sync.nas_client.SyncJobHandle(job_id, state, run_path, blocking_findings=())[source]#
Bases:
objectLightweight handle returned by
NASSyncClient.enqueue().job_idis empty when the gate blocked enqueue (the on-disksync_statuswill reflect the block).blocking_findingsis present iffstate == BLOCKED.- Parameters:
- state: SyncHandleState#
- class exlab_wizard.sync.nas_client.SyncJobState(*values)[source]#
Bases:
StrEnumState machine for a sync job. Backend Spec §7.1.2.
- AWAITING_VERIFY = 'awaiting_verify'#
- CLEANED = 'cleaned'#
- CLEANUP_ELIGIBLE = 'cleanup_eligible'#
- FAILED = 'failed'#
- QUEUED = 'queued'#
- RUNNING = 'running'#
- VERIFIED = 'verified'#