Summary
HAIStack needs a clear separation between transactional (interactive REST) and analytical (cohort extraction, reporting, AI batch context) data paths. Today the conceptual split exists (pkg/http vs pkg/view + pkg/analytics), but edge mode co-locates OLTP and reporting in the same Postgres database, Bulk Data export is unimplemented (501), analytics uses full-scan view execution, and there is no verification that export features conform to the HL7 Bulk Data profile.
Problem / gap
What we have today
Transactional path (pkg/core + pkg/http):
- CRUD, conditional operations, JSON Patch
_search (GET/POST), _history
- Transaction/batch bundles
- SDC operations when wired
- Sync hub routes (
/sync/push, /sync/pull)
- Write path: validate → persist + history → terminology projection → outbox → search index (single
WriteSession transaction)
Analytical path (pkg/view + pkg/analytics):
- Subset of FHIR ViewDefinition (SQL-on-FHIR-style): single resource, flat columns, FHIRPath filters
- No joins,
forEach, unionAll, or materialization directives
pkg/analytics refreshes views into Postgres JSONB reporting tables or CSV export
- Three built-in views:
patient_summary_view, appointment_view, observation_view
- Full refresh only; no incremental cursors
Bulk Data:
| Component |
Status |
Server (pkg/http) |
Routes exist; return 501 Not Implemented |
Client (pkg/client) |
Kickoff/poll/cancel/manifest client with tests |
| NDJSON pipeline |
Not implemented |
Cloud mode seams:
ExternalSearch adapter — noop placeholder
ExternalWarehouse adapter — noop placeholder
- No read replica, columnar store, or CDC/streaming path
What is missing
-
Standard cohort extraction (Bulk Data)
GET /fhir/$export and GET /fhir/Group/{id}/$export must implement HL7 Bulk Data profile
- Async job polling, manifest, NDJSON output, delete-after-download semantics
- Conformance tests against Bulk Data spec (not just "we emit JSON lines")
-
Analytics path maturity
- View execution scans
ListIDs/Read — not search-driven or warehouse-optimized
- No incremental refresh (full scan every run)
- No Parquet, warehouse, or lakehouse sinks (deferred in docs)
- Reporting tables share Postgres with OLTP in edge mode
-
Physical/logical separation
- No read replica routing for analytics queries
- No guidance preventing REST
_search pagination abuse for large exports
- No event-driven CDC from outbox/sync to analytics layer
-
SQL-on-FHIR evaluation
- Current ViewDefinition subset should be evaluated against maturing SQL-on-FHIR spec
- Gap analysis: which ViewDefinition features to add vs defer
- Portability story for views defined in standard IG packages
-
Cloud export verification
- Before designing downstream jobs against cloud provider "FHIR export" features, verify Bulk Data conformance
- HAIStack client (
pkg/client) exists but server cannot be self-tested
Why this matters for HAIStack
- Researchers and data engineers assume
$export + NDJSON manifests for cohort extraction — REST pagination is wrong semantically and expensive on SQLite edge nodes.
- AI pipelines need batch context via views/analytics — full table scans do not scale.
- Edge deployments risk analytics workloads starving interactive clinical use when sharing one SQLite/Postgres instance.
- Interoperability: Downstream Spark/dbt/ML jobs expect standard Bulk Data, not custom export endpoints.
Proposed scope
Phase 1 — Bulk Data server implementation
Phase 2 — Analytics pipeline hardening
Phase 3 — Transactional vs analytical separation
Phase 4 — SQL-on-FHIR alignment
Phase 5 — Client and cloud export verification
Acceptance criteria
Out of scope (for this issue)
- GraphQL API
- Full SQL-on-FHIR engine with arbitrary SQL
- OpenSearch external search backend (separate search scaling issue)
- Real-time streaming (Kafka/Kinesis) — CDC hook only
Affected packages / files
| Area |
Path |
| HTTP / Bulk routes |
pkg/http/handler.go, pkg/http/doc.go |
| Bulk client |
pkg/client/ |
| Jobs (async export) |
pkg/jobs/ |
| Blob storage |
pkg/binary/ |
| Views |
pkg/view/ |
| Analytics |
pkg/analytics/ |
| Runtime modes |
pkg/runtime/ |
| Store/reporting |
pkg/store/reporting_table.go, pkg/postgres/migrations/0009_reporting_tables.sql |
| Auth (export permissions) |
pkg/auth/ |
Architecture target
Interactive workloads:
Client → pkg/http (REST) → pkg/core → pkg/search
Large cohort extraction:
Client → pkg/http ($export) → pkg/jobs → NDJSON blobs
OR Client → pkg/client → external conformant server
Analytics / reporting:
pkg/view (ViewDefinition) → pkg/analytics → reporting tables / Parquet / warehouse
AI batch context:
pkg/view + pkg/fhirpath (permissioned, row-limited) — not raw Bulk export
References
Related issues
- Conformance artefacts (ViewDefinitions from IG)
- Authorization semantics (export permissions, backend-service clients)
- Benchmark harness (bulk export at scale)
Summary
HAIStack needs a clear separation between transactional (interactive REST) and analytical (cohort extraction, reporting, AI batch context) data paths. Today the conceptual split exists (
pkg/httpvspkg/view+pkg/analytics), but edge mode co-locates OLTP and reporting in the same Postgres database, Bulk Data export is unimplemented (501), analytics uses full-scan view execution, and there is no verification that export features conform to the HL7 Bulk Data profile.Problem / gap
What we have today
Transactional path (
pkg/core+pkg/http):_search(GET/POST),_history/sync/push,/sync/pull)WriteSessiontransaction)Analytical path (
pkg/view+pkg/analytics):forEach,unionAll, or materialization directivespkg/analyticsrefreshes views into Postgres JSONB reporting tables or CSV exportpatient_summary_view,appointment_view,observation_viewBulk Data:
pkg/http)pkg/client)Cloud mode seams:
ExternalSearchadapter — noop placeholderExternalWarehouseadapter — noop placeholderWhat is missing
Standard cohort extraction (Bulk Data)
GET /fhir/$exportandGET /fhir/Group/{id}/$exportmust implement HL7 Bulk Data profileAnalytics path maturity
ListIDs/Read— not search-driven or warehouse-optimizedPhysical/logical separation
_searchpagination abuse for large exportsSQL-on-FHIR evaluation
Cloud export verification
pkg/client) exists but server cannot be self-testedWhy this matters for HAIStack
$export+ NDJSON manifests for cohort extraction — REST pagination is wrong semantically and expensive on SQLite edge nodes.Proposed scope
Phase 1 — Bulk Data server implementation
GET /fhir/$export(system export) inpkg/http:202 Accepted+Content-Locationexport.json) with correct structure_since,_type,_typeFilterparametersGET /fhir/Group/{id}/$export(group export)pkg/jobswith blob storage for export files (pkg/binary)pkg/clientagainstpkg/httpserver (round-trip)Phase 2 — Analytics pipeline hardening
_since-style delta refreshpkg/searchindex instead of fullListIDsscan where possible)Phase 3 — Transactional vs analytical separation
pkg/analyticsagainst Postgres replicaExternalWarehouseadapter interface (S3 + Parquet or warehouse stub)Phase 4 — SQL-on-FHIR alignment
forEach, joins,unionAll, materialized viewsPhase 5 — Client and cloud export verification
Acceptance criteria
GET /fhir/$exportreturns valid async Bulk Data response (not 501)pkg/clientbulk export integration test passes againstpkg/httpserverOut of scope (for this issue)
Affected packages / files
pkg/http/handler.go,pkg/http/doc.gopkg/client/pkg/jobs/pkg/binary/pkg/view/pkg/analytics/pkg/runtime/pkg/store/reporting_table.go,pkg/postgres/migrations/0009_reporting_tables.sqlpkg/auth/Architecture target
References
pkg/http/doc.go— bulk routes documented as 501pkg/view/README.md— ViewDefinition v1 subsetRelated issues