Skip to content

refactor(storage): backend-neutral blob/queue/identity storage — swap backends with zero consumer changes - #81

Closed
Diego Colombo (colombod) wants to merge 1 commit into
harden-durable-ingestionfrom
storage-api-on-78
Closed

refactor(storage): backend-neutral blob/queue/identity storage — swap backends with zero consumer changes#81
Diego Colombo (colombod) wants to merge 1 commit into
harden-durable-ingestionfrom
storage-api-on-78

Conversation

@colombod

@colombod Diego Colombo (colombod) commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator

What this delivers

Storage becomes pluggable. Blob, queue, and identity persistence each move behind a backend-neutral Protocol + factory, so a new backend (Azure, S3, a database) can be dropped in by adding one implementation, with zero changes to any consumer. Today's filesystem behaviour is preserved exactly — the abstraction is what's new.

Per package

  • blob_store — backend-neutral blob API: write() -> BlobReference (callers hold an opaque reference, never a path); reads via scan()/list(); fenced delete(uri, if_unmodified=ref) that refuses if the blob changed since it was observed, closing a TOCTOU delete race. settings.blob_backend selects the backend; consumers use the create_blob_store(settings) factory.
  • queue_manager — carries fix: eliminate silent event loss from queue corruption on shared storage (+ crash-safe recovery) #78's authoritative durable FileSystemQueueManager unchanged (class body byte-identical modulo rename + 3 self-refs); the Protocol defines the Record/Batch types; create_queue_manager() factory.
  • identity_store — backend-neutral identity store; the on-disk path is now private; callers use the exists() contract; create_identity_store(settings, kind) factory.

Why it matters

Consumer code no longer knows how storage works — only the protocol. This is the seam that lets the deployment run on cloud object storage without rewriting the server, and it's the foundation the operability & data-quality work stacked on top (#79) builds on.

Supporting changes

  • Blob-key fix: tool_call_id is included in blob-key node_id construction.
  • Anti-leak cleanup: queue boot-reclaim logging uses qm.queues_dir (fixes a latent mismatch with settings.queues_path); three identity first-boot warnings no longer echo store paths.
  • One sanctioned exception retained: registry.queues_dir_path -> Path(get_settings().queues_path) — the WriterLease boot detector needs a resolver that does not construct a QueueManager.

Verification

Full suite: 2082 non-neo4j + 86 neo4j = 2168 passed, 0 failed. All storage-artifact operations are isolated to the three backend packages — no Path/glob/unlink/settings.*_path reads in consumers.

Stack

#78 harden-durable-ingestion   ← this PR's base
  └─ this PR                    (storage-API isolation)
       └─ #79                   (operability & data-quality, v6.7.3)

Merge after #78, before #79.

🤖 Generated with Amplifier

Co-Authored-By: Amplifier 240397093+microsoft-amplifier@users.noreply.github.com

@colombod Diego Colombo (colombod) changed the title refactor(storage): isolate blob, queue, and identity storage behind backend-neutral protocols (stacked on #78) refactor(storage): isolate blob, queue & identity storage behind backend-neutral protocols Aug 24, 2026
@colombod Diego Colombo (colombod) changed the title refactor(storage): isolate blob, queue & identity storage behind backend-neutral protocols refactor(storage): backend-neutral blob/queue/identity storage — swap backends with zero consumer changes Aug 24, 2026
…ackend-neutral protocols

Transplant PR #76's storage-API isolation onto PR #78's hardened durable
queue so the two stack cleanly (78 -> 76). Every store is now reached only
through a backend-neutral Protocol + factory; no consumer constructs a
concrete backend or touches an on-disk path.

queue_manager/  FileSystemQueueManager implements the QueueManager Protocol.
                Built on #78's authoritative body (the durable per-record
                cursor model): the class body is byte-identical to #78's
                queue_manager.py apart from the class rename and its self-
                references. protocol.py carries #78's Record/Batch verbatim;
                factory.create_queue_manager is the only queue backend selector.

blob_store/     FileSystemBlobStore implements the BlobStore Protocol. write()
                returns a BlobReference (uri + size + last_modified); the store
                gains scan()/list() (async BlobReference iterators) and a fenced
                delete(uri, if_unmodified=ref). Adds settings.blob_backend.

identity_store/ FileSystemIdentityStore implements the IdentityStore Protocol;
                the commit-order and fail-closed-load contract lives in the
                protocol. The backing path is private -- callers use exists().

Also folds in the blob-key fix: process_event includes tool_call_id in the
blob-key node_id, so two same-millisecond parallel events no longer collide on
one blob and silently overwrite each other.

Consumers (registry, main, blob_processor, pipeline) build stores via the
factories and pass URIs/references, never paths. The boot-reclaim dry-run log
derives the blob path from the QueueManager's own queues_dir (fixing a latent
mismatch when it differs from settings.queues_path); the identity first-boot
warnings no longer echo the store path. The sole remaining config-path read
outside the storage layer is registry.queues_dir_path -- the resolver the
WriterLease boot detector uses precisely because it must not construct a
QueueManager.

Full non-neo4j suite: 2082 passed. Neo4j subsets (queue durability, blob
ingest, identity auth): green.

🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier)

Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
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