120 lines
5.1 KiB
Markdown
120 lines
5.1 KiB
Markdown
# Store Transaction And Locking Boundary
|
|
|
|
Date: 2026-06-09
|
|
|
|
Status: current contract clarification
|
|
|
|
Related:
|
|
|
|
- [Persisted run/resume contract](./2026-06-03-persisted-run-resume-contract.md)
|
|
- [Workflow config targets and sources](./2026-06-03-workflow-config-targets-and-sources.md)
|
|
- [Store-backed source registry](./2026-06-03-store-backed-source-registry-design.md)
|
|
|
|
## Purpose
|
|
|
|
This spec defines what the current file-backed stores guarantee and what they
|
|
intentionally do not guarantee. The product path is now a long-lived
|
|
`wf-rpc-server`, so future agents must not assume that JSON files plus
|
|
process-local locks provide cloud-grade transaction semantics.
|
|
|
|
The short rule:
|
|
|
|
> File stores are local/dev/single-process stores. Multi-process or cloud use
|
|
> needs a transactional backend before claiming strong concurrent mutation
|
|
> safety.
|
|
|
|
## Current Store Classes
|
|
|
|
| Store | Current role | Current concurrency guarantee |
|
|
| --- | --- | --- |
|
|
| `FileWorkflowArtifactStore` | Immutable artifact versions and mutable deployments | Path validation only; simple JSON writes/deletes; no multi-operation transaction |
|
|
| `FileDraftWorkspaceStore` | Mutable draft workspaces | Per-process `RLock`; optimistic revision checks are safe inside one process only |
|
|
| `FileRunStore` | Durable stopped-run summaries and checkpoints | Per-process `RLock` around individual writes; `WorkflowRunApi.resume_run()` adds a per-run async critical section in one API process |
|
|
| `AtomicJsonRegistryStore` / `FileSourceRegistryStore` | Desired source registry document | Whole-file temp-write + replace; no compare-and-swap revision; concurrent writers are last-writer-wins |
|
|
| `FileAuthStore` | Local/dev MCP auth JSON records | Simple JSON writes/deletes; payload values are write-only through admin surfaces, but storage is plaintext local JSON |
|
|
| `FileCatalogStore` | MCP catalog snapshots | Simple JSON writes; snapshot cache, not an authoritative source of truth |
|
|
| `FileStore` | Compatibility wrapper for MCP auth + catalog stores | Delegates to `FileAuthStore` and `FileCatalogStore`; no extra locking |
|
|
|
|
## Guarantees Today
|
|
|
|
### Path Safety
|
|
|
|
All durable file stores validate ids before constructing filesystem paths. A
|
|
rejected id must not escape the configured store root.
|
|
|
|
### Single-Process Mutation Safety
|
|
|
|
Some stores protect multi-step operations inside one process:
|
|
|
|
- `FileDraftWorkspaceStore.create_workspace()` and `.replace_workspace()` keep
|
|
duplicate/revision checks and writes under one process-local lock.
|
|
- `WorkflowRunApi.resume_run()` serializes the restore, pinned dependency
|
|
validation, runtime resume, and checkpoint write sequence per `run_id` in one
|
|
API/server process.
|
|
|
|
These are process-local guards. They do not coordinate with another Python
|
|
process that points at the same root.
|
|
|
|
### Atomic Single-File Replacement
|
|
|
|
Some writes use temp files followed by `Path.replace()`:
|
|
|
|
- draft workspace writes
|
|
- run checkpoint/run summary writes
|
|
- source registry whole-file writes
|
|
|
|
This reduces partially-written file risk for one file. It does not make a
|
|
multi-file operation transactional.
|
|
|
|
## Non-Guarantees Today
|
|
|
|
Current file stores do not guarantee:
|
|
|
|
- Cross-process locks.
|
|
- Compare-and-swap updates.
|
|
- Serializable transactions.
|
|
- Crash recovery across a multi-file mutation.
|
|
- Rollback when one file write succeeds and a later related write fails.
|
|
- Secret encryption at rest.
|
|
- Multi-writer safety for registry/auth/admin mutation endpoints.
|
|
|
|
## API-Layer Policies
|
|
|
|
Some correctness rules intentionally live above stores:
|
|
|
|
- `WorkflowArtifactApi.delete_artifact()` checks referencing deployments before
|
|
deleting an artifact version. `FileWorkflowArtifactStore.delete_artifact()`
|
|
only removes the file.
|
|
- `WorkflowRunApi.resume_run()` owns same-process resume serialization.
|
|
`FileRunStore` only saves and loads run records/checkpoints.
|
|
- Source registry config ownership rules are enforced by the registry admin and
|
|
connection services. `FileSourceRegistryStore` only loads/saves the desired
|
|
registry document.
|
|
|
|
This is acceptable for current local/dev use, but a transactional backend should
|
|
move the relevant compare-and-swap guarantees into the store layer.
|
|
|
|
## Future Transactional Store Requirements
|
|
|
|
A SQL or equivalent transactional backend should provide:
|
|
|
|
- Atomic artifact/deployment mutation policies where needed.
|
|
- Draft workspace compare-and-swap by revision.
|
|
- Per-run transaction or lock for resume state transitions.
|
|
- Monotonic checkpoint sequence allocation per run.
|
|
- Source registry revision or compare-and-swap writes.
|
|
- Auth record storage suitable for the deployment environment, preferably via a
|
|
secret manager or encrypted-at-rest store.
|
|
- Clear behavior for multiple API workers.
|
|
|
|
## Implementation Guidance
|
|
|
|
- Do not add ad hoc filesystem lock files unless a real cross-process locking
|
|
design is specified and tested on Windows.
|
|
- Do not claim a file-backed deployment is cloud-safe just because focused tests
|
|
pass.
|
|
- Prefer keeping local file stores simple and adding a dedicated transactional
|
|
store implementation when the product needs multi-worker durability.
|
|
- Keep error messages explicit when an operation is blocked by policy rather
|
|
than by missing files.
|