fix(persistence,rest): _lastUpdated on Postgres; server-populated meta in response bodies - #876
Conversation
…nse bodies Two findings from the #448 Postgres leg. _lastUpdated searches returned an opaque 500 on the Postgres backend at every precision and prefix: build_last_updated_condition bound the raw query string as a text parameter against the TIMESTAMPTZ last_updated column, which tokio-postgres refuses to serialize, and the path had none of the precision-range semantics the date parameters got in #463. It now binds real timestamps and mirrors build_date_condition: eq at day precision means [day, day+1), gt excludes the named period, and a full-precision instant falls back to scalar comparison. Response bodies carried no server-populated meta on any read path — versionId and lastUpdated lived only in the ETag and Last-Modified headers, so search entries, reads, vreads, history bundles, and batch/transaction responses returned resources with no server metadata, and the Resources workspace's UPDATED column rendered blank on every backend. StoredResource::content_with_meta now merges the row's version and timestamp into the returned body (client-supplied meta members like profile survive), and every handler and bundle builder that echoed stored content uses it. Verified live against postgres:16 with the #448 dataset: the full battery passes 22/22, and a Patient read returns meta.profile + versionId + lastUpdated together. Closes #871 Closes #873
Resolve conflict in core/search.rs: main split SearchResult::to_bundle into a shared bundle_shell plus a copying to_bundle and a consuming into_bundle. Apply the #873 server-populated meta merge to both paths, adding a consuming StoredResource::into_content_with_meta so into_bundle keeps its clone-free behaviour. Claude-Session: https://claude.ai/code/session_01334x1QZSmUsznAtEVcnVy7
test_prefix_operators asserted the scalar operators the pre-#871 code emitted — the exact semantics the fix replaces. Day-precision prefixes now assert their [day, day+1) range shapes and bound counts, and a full-precision instant pins the scalar fallback.
|
CI fix pushed: |
…iosSoftware/hfs into fix/871-873-read-correctness
… manifest `postgres_bulk_submit_import_directives_round_trip` called `claim_next_manifest` and asserted the claimed view carried the directives it had just set. The claim queue is cross-tenant and ordered by `added_at`, the test binary shares one container database, and the batch test that #880 added leaves its manifest as `processing` with no lease — which the claim query treats as an orphan to reclaim. Whenever that manifest was added first, the directives test claimed it instead of its own and failed with `left: []`; main's coverage job has been red on most runs since. The test now claims through `claim_specific_manifest`, the submit-side twin of the export tests' `claim_specific`: loop until the target manifest comes back, hold any foreign lease picked up along the way so it cannot be re-claimed, then release those back to the queue. The batch test's comment no longer presents its `process_entries` call as a defence against concurrent claims. No product code changes. Tests: the two submit tests pass five consecutive runs together; the full postgres_tests binary passes (156).
|
CI here was failing on |
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
Closes #871
Closes #873
Both found by the #448 Postgres sanity leg; base is
main(independent of the bulk-progress stack).#871 —
_lastUpdated500s on Postgresbuild_last_updated_conditionbound the raw query string as a text parameter against theTIMESTAMPTZlast_updatedcolumn — tokio-postgres refuses to serialize it ("error serializing parameter 2"), so every precision and prefix returned an opaque 500. The path also predated the precision-range semantics dates got in #463.Now it binds real timestamps and mirrors
build_date_conditionexactly:eqat day precision means[day, day+1),neits complement,gt/saexclude the named period,lt/le/geuse the correct boundary, and a full-precision instant falls back to scalar comparison. Unit tests pin the SQL shape, the bound param types, and the period-end arithmetic.#873 — response bodies carried no server-populated
metaversionId/lastUpdatedlived only in theETag/Last-Modifiedheaders: search entries, reads, vreads, history bundles, and batch/transaction responses all returned resources with no server metadata — R4 requires it at least on vread, Bundles have no per-resource headers, and the Resources workspace's UPDATED column rendered blank on every backend.Fix is read-side and central:
StoredResource::content_with_meta()merges the row's version and timestamp into the returned body — client-suppliedmetamembers (profile,tag,security) survive,versionId/lastUpdatedare overwritten because the row is authoritative — and every echo point uses it: read, vread, history entries, searchto_bundle(match + include), create/update/patch responses, batch/transactionBundleEntryResult, and subscription event payloads. Read-side means pre-existing rows are fixed immediately, no migration.An integration test walks the whole lifecycle: create (v1 meta in the response, profile preserved) → update → read (v2) → vread (v1 keeps its version) → history (per-version meta) → search (entries carry meta).
Verified
meta.profile + versionId + lastUpdatedtogether.