Data Flow Examples#
Complete data flow examples showing request-to-response patterns for different scenarios.
Transfer Overview Read (Operation Lists)#
The transfer overview (GET /api/transfer/overview) does not read a per-stage
status column off each artifact. It reads the uniform Operation chain off the
package and derives everything — the funnel, the failed list, the per-kind counts —
from the operation rows. The model itself is described once in the
ops-db Operation model reference;
the identity-vs-routing split is in the
data-transfer two-axes page.
The read side here only navigates that model; it does not redefine it.
sequenceDiagram
participant UI as Web Frontend
participant API as FastAPI
participant Auth as Authentication
participant Router
participant Crud as crud.get_transfer_overview
participant DB as Local Database
UI->>API: GET /api/transfer/overview
API->>Auth: Verify JWT (admin actions only; overview is anonymous)
API->>Router: Route to handler
Router->>Crud: build overview
Crud->>DB: COUNT operations per OperationKind
Crud->>DB: Load non-archived RawDataPackages
Crud->>DB: _attach_operations — bulk-load 5 op lists BY FK
DB-->>Crud: packages + their operation chains
Crud->>Crud: classify_transferring_step (seven-step funnel)
Crud->>Crud: attribute failures latest-step-first
Crud-->>Router: TransferOverview (no error text)
Router-->>API: Format response
API-->>UI: JSON response (< 100ms)
How the read side reads the package#
The overview builder (crud.get_transfer_overview → build_package_funnel)
walks each package’s operation chain rather than any paired status column:
_attach_operationsbulk-loads five operation lists by FK. There is no artifact→operation ORM relationship — operations carry forward FKs only — so the read side resolves each stage with a fixed number of bulk queries (one per kind: packaging, bundling, transfer, unpack, archive) keyed onraw_data_package_id/data_transfer_package_id. This is what avoids an N+1: the queries are independent of the number of packages, attachingpackaging_operations,bundling_operations,transfer_operations,unpack_operations, andarchive_operationsas plain lists on each package.classify_transferring_stepplaces each in-flight package in the seven-step funnel. It walks the first incomplete operation in pipeline ordertransfer → unpack → awaiting_archive → archiving. The occupancy invariant is that every package lands in exactly one cell: Σ(occupancy + failed) over all seven steps plus the inconsistent overflow equals the total package count. Theawaiting_archive/archivingsplit distinguishes the at-rest archive backlog (transfer + unpack done, no archive op yet) from the in-flight archive set (an archive op exists), per ADR-0003.Failure attribution is charged latest-step-first to the failing Operation.
attribute_failed_packagescans the chain from the end backwards (archiving → unpack → transfer → bundling → packaging) and charges the failure to the first currently-FAILED operation. The breadcrumb it emits is keyed by the operation’sOperationKind.valueplus the Operation row id — the same key the durablefailure-history/{operation_type}/{operation_id}endpoint uses. Error text is read server-side only to pick the funnel cell; it is never shipped in the anonymous overview payload (ADR-0002). Astate=FAILEDpackage with no currently-failed op is reconciled to the explicit “inconsistent” bucket (ADR-0004) rather than silently dropped.
Observatory Write (Buffered)#
Critical operation with transaction buffering:
sequenceDiagram
participant Script as Observatory Script
participant API
participant Builder as Transaction Builder
participant Manager as Transaction Manager
participant Redis
participant BG as Background<br/>Processor
participant MainDB as Main DB
participant LSN as LSN Tracker
participant Replica
Script->>API: POST /executed_obs_units/start
API->>Builder: Build transaction
Builder->>Builder: Generate UUID
Builder-->>API: Transaction
API->>Manager: Buffer transaction
Manager->>Redis: LPUSH to buffer
Manager->>Redis: Cache generated ID
Redis-->>Manager: OK
Manager-->>API: Transaction ID
API-->>Script: 201 Created (< 20ms)
Note over Script: Client continues immediately
loop Background Processing
BG->>Redis: RPOP from buffer
BG->>MainDB: Execute transaction
MainDB-->>BG: Success
BG->>MainDB: Capture LSN
MainDB-->>BG: LSN: 0/12345678
BG->>LSN: Track replication
LSN->>Replica: Poll replay LSN
alt Replicated
Replica-->>LSN: LSN caught up
LSN-->>BG: Replicated
BG->>Redis: Cleanup caches
else Not yet
Replica-->>LSN: Still behind
LSN-->>BG: Not replicated
BG->>Redis: Extend cache TTL
end
end
Observatory Read (Smart Query)#
Query merging database + buffer:
sequenceDiagram
participant Script
participant API
participant Smart as Smart Query<br/>Manager
participant DB as Local Replica
participant Redis as Redis Buffer
participant ReadBuf as Read Buffer
Script->>API: GET /executed_obs_units/123
API->>Smart: search_records(...)
par Parallel Queries
Smart->>DB: Query database
Smart->>Redis: Query buffered cache
Smart->>ReadBuf: Query read buffer
end
DB-->>Smart: [obs1, obs2]
Redis-->>Smart: [obs3 (buffered)]
ReadBuf-->>Smart: [updates to obs3]
Smart->>Smart: Merge (buffer > DB)
Smart->>Smart: Apply read buffer updates
Smart->>Smart: Deduplicate by ID
Smart-->>API: [obs1, obs2, obs3 (merged)]
API-->>Script: JSON response
WebSocket Real-Time Updates#
Streaming updates via WebSocket:
sequenceDiagram
participant UI
participant API
participant Redis as Redis<br/>Pub/Sub
participant DB
UI->>API: Connect WS /api/transfer/ws/overview
API->>API: Authenticate token
API->>Redis: Subscribe to "transfer_updates"
API->>DB: Query initial data
DB-->>API: Current overview
API->>UI: Send initial data
Note over UI,DB: Real-time updates
loop On Data Changes
DB->>Redis: PUBLISH transfer_updates
Redis-->>API: Update message
API->>UI: Send update
end
Complete Observatory Operation#
End-to-end flow from start to finish:
Start observation (buffered)
Query observation (smart query, shows buffered)
Finish observation (update read buffer)
Query again (smart query, shows updated)
Background processing executes buffered start
Background processing executes buffered finish
LSN tracking confirms replication
Cache cleanup occurs
Future queries read from database (normal)
Summary#
Key patterns:
Transfer overview reads: Navigate the uniform Operation chain — the funnel, failed list, and per-kind counts are derived from operation rows (
_attach_operationsby FK,classify_transferring_step), not from a paired per-stage status column on the artifact.Operations writes: Buffered (reliable, never block)
Operations reads: Smart queries (merge buffer + DB)
Real-time: WebSockets with Redis pub/sub
Next Steps#
Transaction Buffering Overview - Buffering details
Smart Queries with Buffering - Tutorial