Wayfarer
History derivation and upgrade preparation
Issue #918 package I918-A supplies internal data and upgrade readiness. It does not by itself deliver the history routes or complete #918. I918-B supplies routed reads and dependent-operation gates. I918-C documents backend composition/fidelity and the measured performance limitation below; backend #918 acceptance is not yet supported. #604 owns actual M6/maintainer instance qualification. This procedure grants no production operation authority.
Authorities and upgrade boundary
Follow Versioning, the Release contract
and the installation’s supported operator lifecycle.
Do not turn a schema migration or ordinary startup/read into a history backfill.
The early history-data application CLI exits before web startup, migrations,
seeding, logging sinks, Quartz, storage initialization or provider registration.
It consumes the normal application configuration, including
Database:PasswordFile; it never prints connection strings or source content.
Before an authorized upgrade, preserve the normal database/uploads/key-ring
recovery set. Stop/quiesce all old application/import/enrichment writers at the
supported upgrade boundary. Apply migration 20261023175322_LocationHistoryPreparation
using that release’s normal schema-maintenance procedure and exact target binary.
New writers must all be from that binary before preparation/readiness is accepted.
An old binary or arbitrary external SQL writer can violate these invariants and
must not run concurrently. Whole-database restores require this check again.
The migration adds nullable facts, coherence constraints, a two-row preparation
state table and four indexes. It requests trusted extension pg_trgm alongside
existing PostGIS/citext. Have the database owner provision the extension if the
maintenance role cannot do so; never grant the application superuser rights.
Missing privileges or failed index creation leave preparation unavailable.
The migration uses ordinary transactional index builds while writers are quiesced;
allow its table/index locks and disk headroom. No concurrent-build release protocol
or release manifest/operator changes are introduced here.
UTC index status coverage correction
Migration 20261024172605_LocationHistoryUtcStatusCoverage replaces only
IX_Location_HistoryUtc. Its B-tree keys remain (UserId, LocalTimestamp, Id);
the INCLUDE payload becomes (Coordinates, CaptureLocalTimestamp, ActivityTypeId,
HistoryTimeStatus), with no predicate. The EF model matches this definition.
Status covers capture membership and uncertainty without becoming a search key.
HistoryTimeVersion and singleton UUID remain uncovered. Preparation logic,
generation versions, filters and budgets are unchanged; no history rebuild is needed.
The physical preparation check accepts either exact UTC payload, retaining the
original correctness prerequisite and recognizing the corrected coverage. It still
rejects missing/invalid indexes, different keys, payloads and predicates. Migration
history and the current EF model own installation of the corrected definition;
a preparation-ready result alone does not certify that migration or performance.
Use the existing explicit schema-maintenance workflow, with a verified recovery
set and all application/read/import/enrichment traffic quiesced. This ordinary EF
transaction drops and rebuilds the index, then records migration history at commit.
The DROP INDEX lock
blocks table access until the transaction ends. This is an offline replacement,
without CONCURRENTLY or transaction suppression. Fresh migration chains first
create the original index and then replace it; upgrades replace that original.
Down rebuilds the original payload under the same locking/headroom requirements.
If CREATE, lock acquisition, cancellation or commit fails before a confirmed commit, inspect migration history and the physical definition before retrying. Transaction rollback restores the original index and leaves this migration unapplied; there is no committed interval without the index or invalid concurrent-build remnant. An ambiguous commit requires inspection rather than blindly rebuilding. Do not perform an out-of-band replacement and mark history applied manually.
Allow space for the old index retained until commit, the complete replacement, temporary sort files, WAL and the recovery reserve. Final index growth alone is insufficient headroom. The supported 2,838,240-row experiment measured 408.64 → 440.68 MiB (+32.04 MiB), a 3.6-second CREATE, 384.11 MiB database build-temp writes and about 395.63 MiB shared-cluster WAL LSN advance. These are local observations, not peak bounds or production sizing. Maintenance command timeouts must accommodate the real rebuild and lock window through the established operator workflow; no global tuning is introduced. Verify valid/ready physical definition, no pending model/migration changes, and ordinary readiness before resuming traffic. See the bounded delivery evidence. #604 retains actual deployment, restored-population headroom and recovery qualification.
Commands and recovery
Run the target application’s CLI under the same protected configuration/database identity used by its supported maintenance process. For native/source qualification:
dotnet Wayfarer.dll history-data status
dotnet Wayfarer.dll history-data prepare
dotnet Wayfarer.dll history-data status
prepare resumes existing compatible progress. It defaults to both generations;
time or text selects one. JSON progress reports target/active versions, all-owner
total/pending/unresolved counts, examined/prepared counts, last committed ID,
physical index readiness and last actionable failure. It contains no owner IDs,
Notes, addresses or provider credentials. Exit codes: 0 selected generations ready,
1 pending/failed, 2 invalid arguments, 130 cancellation. status is read-only and
observes readiness/counts in one repeatable-read database transaction.
Each batch locks/reloads at most 500 rows in ascending ID order, derives from their
current stored values, writes only derived columns and commits progress in the same
transaction. Per-batch lock timeout is five seconds and statement timeout sixty
seconds. Ctrl-C cancels the current batch; process termination also leaves committed
checkpoints recoverable. Resume prepare; do not reset import checkpoints, replace
canonical rows, or retry provider requests. An operational failure records a safe
resumable category when the database permits it and never becomes unresolved evidence.
If recording the failure also fails, the primary error and earlier committed progress
remain authoritative. A completed cursor is followed by a stale-version sweep;
readiness never relies on maximum ID alone. New supported writers already produce
current facts. Edits/deletions serialize with each short row lock; deleted rows are
not recreated. No locks span provider HTTP.
Before activation the command verifies the required valid/ready index definitions,
trigram operator class and C text collation, then records the active generation and
writer contract. It runs ANALYZE "Locations" after selected preparation. Final
status rechecks physical prerequisites and all-owner versions. A durable ready flag
alone is insufficient. Repair a missing index through authorized schema maintenance,
then resume; do not weaken matching or indexes. Restored mixed/stale rows defeat
status even when an old preparation receipt says ready, and prepare sweeps them.
Servicing dependency changes
Time and text have independent, explicit semantic generation IDs. A reviewed compatible dependency/runtime update retains the affected ID, prepared rows, active/target state, resumable checkpoints and cursor binding. An actual behavior change, or compatibility that cannot be justified, changes only the affected ID and requires its controlled rebuild. Package/runtime metadata alone is not a reason to rebuild. There is no automatic startup/background preparation, alias matching, legacy-prefix acceptance, silent restamping or forced readiness.
For the R1 batch (#937/#938), both old generations require rebuilding:
NodaTime 3.3.3 / TZDB 2026c → 3.3.6 / 2026e changes historical capture values
(Dublin 1925-09-25 12:00Z changes from 13:00/+3600 to 12:00/0). AngleSharp
1.7.1 → 1.7.2 changes visible extraction of historical MathML TEXT/HTML content
from SECRET VISIBLE to VISIBLE. Unchanged recent records cannot waive these
all-owner rebuilds. Runtime-only 10.0.13 → 10.0.14 fixture equivalence is a separate
bounded compatibility decision, not an extra reason to rewrite rows.
The first explicit IDs retain the exact qualified R1 strings to avoid rebuilding already-compatible R1 data merely for a rename:
history-time-1;GeoTimeZone-6.1.0;TZDB: 2026e (mapping: 49)
history-text-1;RichNotes-2;AngleSharp-1.7.2.0;runtime-10.0.14;unicode-31129-0000799900000000000000000000007f
These are opaque historical names. Their embedded versions do not report the
currently executing runtime. Future semantic changes may use history-time-2 or
history-text-2; do not rename a compatible generation for cosmetic reasons.
history-writers-1 retains its unchanged atomic-writer meaning.
Environment qualification versus preparation
history-data status reports actual dependency build identities, full TZDB identity,
executing runtime and effective invariant globalization/sort profile separately from
IDs. Qualified is independent of row counts/index readiness; QualificationFailure
explains missing admission. Status never repairs state and returns 1 for an unready
or unqualified selected generation. Exit 0 still requires all-owner readiness.
The code-owned R1 admission checks exact GeoTimeZone 6.1.0, NodaTime 3.3.6 and
AngleSharp 1.7.2 upstream build identities, TZDB 2026e (mapping: 49), and actual
.NET 10.0.13 or 10.0.14. Text additionally requires effective ICU normalization,
verified by a normalization control, and invariant sort version 31129 / identity
00007999-0000-0000-0000-00000000007f. Time does not depend on this text profile.
The maintained Noble image uses ICU 74.2 and runtime 10.0.14; native Ubuntu also
qualifies on runtime 10.0.13. NuGet 10.0.14 packages do not imply that a probe
executes on 10.0.14. No OS name, machine identity, path or architecture is an
admission condition. Framework-dependent and self-contained processes are checked
by what they actually load. Invariant mode, NLS, unknown artifacts, future runtimes
and other globalization fingerprints need a reviewed compatibility decision first.
This is history admission, not security-release or native-platform acceptance.
Qualification is lazy and local to history; it does not stop global web startup. Unknown qualification cannot stamp a selected generation or activate data. New Locations require both qualifications; edits require each generation they actually recompute (including opportunistic stale-generation updates). A rejection fails the whole tracked/import save atomically through the caller’s existing operational-error path (generic HTTP failure where applicable), without a success acknowledgement. Both fenced bulk address publication branches use the same text check and transaction: address, derived text and final provider publication commit together or roll back together. Operational failure never becomes an unresolved Location. Other independent writes retain their existing boundaries.
prepare/rebuild preflight every selected kind before changing any state; batches
also check before row/checkpoint work. On qualification failure committed progress,
active state and row facts remain untouched, while fresh status/readiness refuses
to advertise readiness. Use a qualified build/runtime/globalization profile or have
the maintainer qualify a supported profile. Rebuilding alone cannot qualify an
unsupported environment. There is no operator version override.
Capture/viewer predicates and nonempty normalized searches reject missing required
qualification with 503 historyQualificationRequired, separately from 503
historyPreparationRequired on a qualified but unprepared deployment. UTC nontext
collections and direct detail remain usable. Unqualified capture/viewer publication
says unavailable / historyQualificationRequired, retains the stored capture
version, and advertises null for an unqualified serving generation in
derivationVersions. It never calls that operational failure pending or unresolved.
Detail still publishes safe Notes through the existing sanitizer contract.
Before accepting a new dependency/profile, review relevant source/data changes and run the existing time, parser and Unicode fixtures at shared writer/query seams. Finite controls and sort equality are admission evidence, not universal behavioral proof. Retain IDs only with a justified compatibility decision; otherwise change the affected ID. Record actual profile and rationale in the servicing record. No compatibility registry, extra stored generation or migration framework is used. See the R1 record.
Independent rules rebuilds and readiness
dotnet Wayfarer.dll history-data rebuild time
dotnet Wayfarer.dll history-data rebuild text
A rebuild marks selected generations unready before processing any rows, resets
only their progress and scans every row even for a same-version rebuild. Interrupted
rebuilds resume with prepare time/prepare text, rather than starting another
rebuild. Changed target rules require explicit rebuild; arbitrary operator version
labels are rejected. There is one stored generation, so declare maintenance for
affected reads while rebuilding. Unselected facts/progress stay intact.
Deploy coherent qualified binaries across writers/readers, with the existing quiesced upgrade procedure. Qualification is not a live-process fence; stop old web/import/enrichment/CLI processes before new writers. No rolling/mixed-release upgrade is supported. Collection cursors bind both semantic IDs: compatible servicing preserves them; either semantic change rejects old continuations.
Pending time has null version/status/facts; a mismatched version is also pending.
Resolved time carries the coordinate-derived IANA zone, wall timestamp and historical
offset together. Current-version unresolved time carries a safe evidence reason and
no invented capture values. Invalid/empty/nonfinite/out-of-range coordinates,
unusable event evidence (including PostgreSQL infinity sentinels) and unrepresentable
local conversion are exceptional evidence; unexpected resolver/tool/store failures
remain unfinished work. Stored UTC LocalTimestamp, Timestamp, coordinates and
imported TimeZoneId are preserved; the imported zone does not override coordinates.
Readiness requires matching target/active/writer identities, valid indexes and zero
pending rows across every owner. Current-version unresolved time is terminal
classified evidence, counted separately from pending. Collection reads check the
required generation identities, physical prerequisites and a single all-owner
pending-row existence predicate in the response snapshot. They do not compute
unused operator totals; history-data status retains its full progress accounting.
Capture predicates depend on time readiness, nonempty search on text readiness.
Affected operations return historyPreparationRequired;
unaffected UTC/viewer/nontext reads and direct detail retain their contracts.
I918-A exposes no routes or readiness/capability advertisement to consumers.
Rollback and restored-data readiness
History preparation does not authorize a binary or schema downgrade. Follow the existing release authority and installation recovery procedure. There is one stored generation per kind; rollback has no automatic-readiness or zero-downtime guarantee.
- Stop current writers before changing binaries or restoring data. Stop all
current Web, import, enrichment and CLI writer processes, including interrupted
maintenance processes, and keep affected reads in maintenance. Qualification and
the shared
history-writers-1value are not process fences. Do not let old and new writers alternate stamps. - Restoring the pre-upgrade recovery set: use the supported managed update recovery or managed restore procedure, or the administrator-owned native recovery boundary. Select a verified, compatible, coherent pre-upgrade database, complete active Data Protection ring and durable uploads/imports together, with the protected deployment configuration needed by that installation. This returns canonical data and history state to that recovery point; it does not retain later writes. Preserve the upgraded state’s recovery evidence through the existing emergency backup requirements, retained receipts and recovery holds. Let that authority select the compatible target; merely selecting old binaries is not a restore.
- Retaining the upgraded database: consider an earlier binary only where the
installation’s existing release procedure explicitly supports that target’s
schema, credential and storage compatibility. Managed
updaterefuses downgrades; this runbook adds no downgrade route or migration reversal. If that compatibility is not established, use the coherent recovery-set path. A binary change alone preserves the newer row stamps, active/target state and checkpoints; it does not restore the older binary’s prepared generations. - Inspect with the actual recovered target. Run
dotnet Wayfarer.dll history-data status bothusing that target binary, its actual runtime/globalization profile and protected configuration/database identity, before maintenance and again before reopening affected reads. Inspect both generations’ target/active/writer agreement, committed progress, all-owner pending and separate unresolved counts, and valid/ready physical indexes, extension and collation prerequisites. CheckQualifiedand actual diagnostics when the target provides them; previous-main status lacks those fields, so use its release’s profile evidence rather than inventing an admission result. Under R1, an unsupported profile needs qualification, not a rebuild. - Prepare only what that target requires. The previous-main binary compares
exact dependency/runtime-derived IDs: newer R1 time/text stamps are stale to
its older rules, even if a stored Ready flag is true. A different persisted
target refuses ordinary
prepare; use that binary’s explicithistory-data rebuild timeand/orhistory-data rebuild textfor the affected kinds. If R1 changed both targets, returning to previous-main rules while retaining that database requires both rebuilds. Conversely, compatible R1 binaries retain the exact qualified IDs: do not rebuild compatible ready data merely because runtime provenance changed. Resume interrupted work for the same target withhistory-data prepare time,prepare textorprepare both; retain its committed checkpoints and let the stale-row sweep finish. Use the same preparation for missing/stale rows under an unchanged target. Rebuilds recompute derived fields only; preserve canonical UTC times, coordinates, Notes, addresses, identities and provenance. Never copy Ready flags, relabel generations or rows, or reset import checkpoints/provider accounting.
Until target-binary readiness is established, capture-basis pages, overviews and
candidate lists remain unavailable while time is unready; any nonempty normalized
search remains unavailable while text is unready, including UTC/viewer searches.
Combined requests need both. Qualified but unprepared reads return 503
historyPreparationRequired; R1 rejects missing required qualification separately
with 503 historyQualificationRequired, including viewer-time predicates. UTC and
qualified viewer collections without text, and direct owner detail, retain their
independent contracts, with stale capture facts reported as pending and unqualified
time facts unavailable. Changed generation IDs invalidate old continuations.
Reopen each affected read only after fresh target status confirms its required
generation ready; backup receipts or a prior binary’s status are insufficient.
Text equivalences and matching
LocationHistoryDerivation.NormalizeText is the shared stored/query implementation.
It applies compatibility decomposition (NFKD), the explicitly selected accent fold,
Unicode whitespace collapse/trim, invariant scalar uppercase and NFC composition.
It removes U+0300/0301/0302/0303/0304/0306/0307/0308/030A/030B/030C/0313/0314/031B/
0323/0324/0325/0326/0327/0328/032D/032E/0330/0331/0342 only following Latin or Greek
base letters, including cedilla, ogonek, comma/dot below and Vietnamese horn/tone marks. Thus cafe, CAFÉ, and decomposed
cafe + acute agree; Greek tonos, dialytika, breathing and circumflex accents fold.
Greek final sigma agrees through uppercase. Iota subscript, Arabic marks, Indic
virama and Japanese dakuten remain meaningful. Letters without an applicable
compatibility decomposition (for example ø, ł, ß) are not transliterated. This is
not translation, stemming, fuzzy matching or a wildcard/token language.
Notes first use RichNotes.Normalize and parsed visible text. Inline words stay
continuous; block/list/line boundaries add spaces, including discarded legacy
block containers. Parsing decodes entities once; attributes, URLs, scripts/styles,
images and removed content never contribute text. No media is fetched. Each of
the eleven authorized fields normalizes independently, then joins with newline.
A normalized query cannot contain that delimiter, so phrases cannot cross fields.
Original Notes/address fields are never rewritten by preparation.
Store search text with C collation. Collection queries use ordinal literal substring:
parameterized LIKE with escaped backslash/percent/underscore for useful trigrams;
exact strpos for short/punctuation-only terms. No minimum phrase length or raw-Notes
application scan is needed. The qualification checks LIKE/strpos parity with the
accent-folded representation and confirms the GIN path; common terms may legitimately
scan broadly. I918-B qualifies exact routed predicates; I918-C records scale performance below.
Writer coverage and canonical isolation
| Writer | Atomic integration |
|---|---|
| Web create/edit/bulk Notes and manual address edits | Shared tracked save boundary locks an untracked current row, overlays only modified canonical properties and derives from that final merge. Provider clears participate. |
| Bearer log/check-in/create/coordinate refresh | Same save boundary inside existing account/deletion/provider fences. Both synchronous and async save overloads are covered. No added admission/receipt authority. |
| CSV/GPX/KML/Google Timeline/Wayfarer GeoJSON imports | Existing final normalization, legacy/keyed AddRange and conflict-retry saves use the same derivation; UUID/dedup/delete/checkpoint ownership is unchanged. |
| Retained reverse geocoding | Existing tracked publication saves include final retained fields. |
| Both fenced enrichment ExecuteUpdate branches | Successful final-row reload and derived text update happen before commit in the existing fenced transaction; repair retains nonempty fields/current Notes. Failed publication writes no derived provider content. |
| Preparation and row/account deletion | Preparation writes only derived columns; same-row derived data disappears with ordinary deletion/cascade. Workflow-only bulk updates change no Location inputs. |
Time is recomputed for event/coordinate changes or stale time rules; text for any of the eleven text inputs (including clears) or stale text rules. Activity remains the ordinary catalog FK. Internal properties have nonpublic setters, BindNever and JsonIgnore; parsers/exports retain explicit canonical mappings. API whole-entity snapshot comparison excludes only the eight new derived properties while retaining all previous canonical checks. Preparation therefore creates no new owner edit conflict or idempotency equality/export field.
Local qualification and measured limits
The retained I918-A receipt uses PostgreSQL 18.6/PostGIS on Linux/WSL, 10,000 synthetic rows in a fresh fixture-owned database, with distinct diary content and all stored address fields. Rules were GeoTimeZone 6.1.0, TZDB 2026c (mapping 48.2), RichNotes-2, AngleSharp 1.7.1.0, .NET 10.0.13 and Unicode 31129 with the invariant sort identity recorded in CLI status. It uses the actual production preparer, 500-row transactions, no provider calls and no raw pre-extracted text COPY. The trial took 13.14 seconds; maximum batch elapsed/lock upper bound was 448 ms. Table+TOAST grew 4,349,952 → 14,991,360 bytes; all indexes grew 6,881,280 → 24,576,000 bytes, with four history indexes growing 3,096,576 → 16,949,248 bytes. Observed preparation WAL was 86,780,568 bytes. These are pre-vacuum physical footprints including MVCC/GIN pending-list growth and fixture allocation, not steady-state per-row or production estimates. Allow at least the observed growth plus index-build temporary space and WAL retention; measure the restored installation’s real headroom before its authorized upgrade. Migration/index-build elapsed time and peak temporary disk were not measured by this preparation trial; instance qualification must budget those separately.
Equivalent 100-row canonical SQL trials, each rolled back, compared baseline versus
the same transaction plus production derivation/storage: create/import 35.45/124.15 ms,
Notes/address edit 68.80/154.43 ms, enrichment 61.93/126.45 ms. Observed WAL respectively
177,904/724,360; 454,872/2,528,088; 478,328/960,888 bytes. Transaction/lock windows were respectively 27.85/117.27, 61.81/147.34 and
54.93/119.53 ms; totals include connection/rollback overhead. These isolate added derivation at the SQL seam;
they do not measure complete routed/import/provider workflows or sustained load.
WAL is cluster-observed and can include other work. A forced eligible GIN plan for
folded CAFE took 5.8 ms locally; this proves amended-normalizer compatibility,
not routed query performance or a production SLO.
Focused tests cover folded scripts, safe visible Notes, historical folds/gaps/non-hour offsets, final partial edits, both fenced publication branches, import original/dedup preservation, real checkpoints, interruption after a derived UPDATE, concurrent edit/delete, all-owner accounting and independent rebuilds. Consumer/native/browser, production population/headroom remain separate. I918-C routed evidence follows below.
Routed qualification and readiness limitation (#918 I918-C)
The earlier timeout correction retains the existing routes, prepared-data architecture, shared representable partition edges, complete filters, uncertainty and five-second cancellation. Overview SQL streams numeric extraction and validation through subquery boundaries before exact edge comparisons; the actual grouping plan no longer expands PostGIS conversion/validation into every comparison. Collection readiness uses one all-owner pending-row existence check for required generations instead of unused operator totals. Physical index, target/active/writer version and scanning/checkpoint guarantees remain in the response snapshot.
Fresh comparable before/after trials use the retained 210,240 and 1,051,200-record owners plus 210,240 unrelated rows, distinct Notes and all eleven searchable fields, five-minute events, PostgreSQL 18.6/PostGIS 3.6.4, Debug and actual bearer MVC routes. Each selection has first use plus two warm observations. This is local WSL2 TestServer evidence with warm OS cache; no disk-cold, network, sustained-load or production-host guarantee. Prepared fixture insertion is not backfill throughput. The under-one-second target remains an engineering target on these workloads.
| Warm routed request | 210,240 before → after ms | 1,051,200 before → after ms |
|---|---|---|
| Full-period UTC overview | 503 at 5,016–5,018 → 570–580 | 503 at 5,017–5,018 → 1,892–1,918 |
| First broad chronology page | 98–101 → 107–113 | 439–444 → 423–451 |
| Capture day overview | 1,205–1,249 → 665–698 | 1,304–1,332 → 835–912 |
| Recurring capture hours/common phrase overview | 2,656–2,923 → 1,300–1,325 | 503 at 5,010–5,016 → 2,321–2,384 |
| Combined Tokyo/year/activity/common overview | 1,613–1,631 → 1,152–1,193 | 2,455–2,536 → 2,275–2,423 |
Absent short phrase (zz) overview |
3,354–3,504 → 891–903 | 3,558–3,732 → 1,427–1,459 |
| Complete long detail | 45–48 → 48–49 | 44–47 → 43–51 |
All 158 corrected requests return 200, versus 25 failures before correction. All five pure four-read rounds complete: zero failures in twenty requests versus nine in twenty before, at 2,091–3,725 ms after correction. Both receipts retain exactly 158 unique intact observations; appends are serialized and no concurrent round contains diagnostic replays. The original qualification’s nine failures in sixteen clean requests and logging collision/recovery remain separate historical evidence.
The sub-second target is not fully met: 80 corrected requests are under one second and 78 miss it, including every concurrent and post-write request. Warm narrow days/months, broad chronology, rare/absent long phrases, detail, small-owner broad overview and small-owner absent short text succeed under one second. Recurring hours with/without common text, combined Tokyo scopes and punctuation overviews miss at both sizes; large broad/short-text overviews and absent-short-text pages also miss. The first small-owner day overview takes 1,050 ms. Completion inside five seconds alone does not establish I918-C performance acceptance.
Natural broad aggregate replays improve from 7,201/30,785 ms to 456/1,559 ms for small/large owners. JIT stays enabled: its totals fall from 5,998/9,120 ms to 34/35 ms, while planning falls from 3.128/6.189 ms to 0.871/0.840 ms. Grouping keys shrink from 35,181 characters to about 3,480 without repeated coordinate expressions. Execution work improves as well as compilation. The large aggregate still examines the 1,471,680-row relation and reads about 247,913 shared blocks. Its external sort writes 15,973 temporary 8 KiB blocks (about 125 MiB), up from 10,061 before; the small aggregate no longer spills. These are the earlier correction’s measurements; the compact-cell follow-up below removes the remaining broad spill without changing work_mem.
Readiness remains a required all-owner scan: the combined pending probe takes about 361 ms in the large recurring/common plan. Its exact uncertainty count still takes about 1,085 ms and the complete aggregate about 931 ms in separate natural replays. The Tokyo candidate plan has lossy bitmap rechecks; its count takes about 1,232 ms. These are remaining concrete query-work costs, not serialization costs. Further target work needs a separately bounded investigation of those repeated required scans and filtered aggregation spill/access paths; no new indexes, fused query architecture or weakened readiness is approved by either correction.
Historical whole-cluster samples retain 169.15 → 110.90 PostgreSQL CPU seconds and 2,664 → 2,730 MiB summed RSS over 25.16 → 16.18 wall seconds. They include background work and double-count shared pages, so they are excluded from attributable query CPU/private-memory claims. Direct application peak RSS is 393 → 436 MiB. Admission, worker and memory settings are unchanged; no memory reduction or sustained capacity is established. The compact key’s separate concurrency result below remains unproven.
After the same 500 tracked address edits without explicit vacuum, all corrected post-write routes succeed: broad overview 1,933–2,013 ms, recurring/common 2,380–2,480 ms. The count’s covering index has 716 heap fetches versus two in the prepared observation. The separate post-write broad aggregate takes 1,567 ms and retains the large sort spill at that stage. These do not qualify write throughput improvements. Complete long detail remains 1,203,344/1,203,347 response bytes; the observed serialization work is about 2–3 ms, with no truncation or invented unavailable facts.
Retained raw requests/responses, first/warm tables, CPU/RSS samples, stage timings,
SQL/plans/buffers/rechecks and reproducible commands are in
/home/stef/.local/share/wayfarer-investigations/918-performance-20261024/.
The original failure report remains at
/home/stef/.local/share/wayfarer-investigations/918-20261024/i918-c/.
See testing and evidence classification.
Compact cell grouping follow-up
The subsequent change groups the existing exact X/Y decisions by one reversible integer key, then decodes and sorts the completed cells with the original null ordering. Nullable positions become digits 0–16 in radix 17; keys 0–288 are unique and safely fit PostgreSQL integers for every supported grid (1–16). Exact shared edges, every match/count/extremum, sparse geography, singleton facts, uncertainty, all-owner readiness, response snapshots, authority and deadlines remain unchanged. There is no epsilon, sampling, new index, schema or planner-setting change.
The retained query-shape investigation is
/home/stef/.local/share/wayfarer-investigations/918-query-shape-20261024/.
Its final three-repetition SQL comparison has large broad medians 1,508 → 1,295 ms
(about 14%); the small broad median is essentially unchanged, 403 → 402 ms.
The large broad plan changes from sorting all 1,051,200 matches to a one-batch
40 KiB hash aggregate and a 25 KiB sort of six completed cells. Temporary writes
fall from 15,973 8 KiB blocks (124.79 MiB) to zero; the former sort’s peak disk
footprint is 63,816 KiB, a different measure. All 1,471,680 relation rows are still
examined and 247,914 shared blocks touched. Buffer reads do not prove disk reads.
The investigation’s warm large broad routed range is 1,846–1,879 → 1,656–1,705 ms. Sequential measurements straddle a background memory release, so this full latency difference cannot be attributed solely to the key. Its paired post-write large broad SQL observation is 1,485 → 1,237 ms with identical shared-block totals and 15,973 → zero temporary writes; routed ranges are 1,873–1,918 → 1,722–1,795 ms. The modest latency gain leaves the query-plus-projection target unmet. Filtered recurring/common aggregation still spills 668 temporary blocks, and readiness and uncertainty scans remain material. No gain is claimed for the selective shapes.
Reuse the investigation’s 36 compact-key aggregate parity receipts (plus 90 for
rejected alternatives), 50 complete routed response pairs and unchanged lower-seam
time/text/filter evidence. Its five
alternating four-read rounds all succeed, but summed makespans are 16.15 → 16.65 s;
the compact key demonstrates no concurrency/throughput improvement. No attributable
PostgreSQL CPU/private-memory reduction is established. These read-only prototype
results motivated the delivery change; they are not independent review or I918-C
acceptance. The bounded production repeat, commands, response parity and plans are
retained separately in
/home/stef/.local/share/wayfarer-investigations/918-compact-cell-20261024/.
That production repeat has 48/48 successful routed reads with no-store and eight exact overview response pairs (only response observation time excluded). Its eight reconstructed baseline commands equal the retained previous production SQL exactly; the candidate commands are unchanged production SQL. The same seed/versions/settings and 1,471,680-row shape are used. Three routed observations per shape and one separate natural plan replay suffice here; the unaffected matrix and concurrency are reused.
| Fresh warm routed overview, ms | Previous grouping | Compact key |
|---|---|---|
| Broad, 210,240 rows | 560–590 | 517–568 |
| Broad, 1,051,200 rows | 1,928–1,956 | about 1,801 |
| Recurring/common, 1,051,200 rows | 2,241–2,287 | 2,304–2,343 |
| Tokyo/activity/area, 1,051,200 rows | 1,711–1,713 | 1,689–1,771 |
| Post-write broad, 1,051,200 rows | 1,961–2,018 | 1,570–1,765 |
The separate fresh large broad SQL replay takes 1,633 → 1,295 ms, with the same 247,918 shared blocks and 15,973 → zero temporary writes. After 500 tracked address edits without vacuum, it takes 1,576 → 1,159 ms, with identical 247,995 shared blocks and 15,973 → zero temporary writes; both uncertainty counts have 680 heap fetches. The candidate retains the 40 KiB one-batch hash and 25 KiB six-row output sort. Large recurring/common aggregation still writes 668 temporary blocks. Companion query-plus-projection remains 2,004 → 1,688 ms for large broad and 1,801 → 1,597 ms post-write; selective results remain variable. Serialization is below 0.4 ms. The roughly 7% warm broad routed gain is modest and does not meet the sub-second target. Direct-probe sampled RSS is about 300 → 301 MiB; no CPU/private database memory or new concurrency improvement is claimed. The owned fixture is cleaned up.
A data-ready receipt proves generation/index/pending prerequisites, not performance. The reviewed finite year-0001 capture limitation remains honestly unresolved while UTC/viewer/detail facts are retained. Fresh exact-head independent review, applicable CI, maintainer acceptance and disposition of the measured target misses remain outstanding. #918 stays open. Reuse I918-A’s preparation/write/storage receipts; #604 still owns restored-population headroom, WAL/index-build/migration/recovery qualification. Mobile/deployed acceptance and #853 C4 remain separate.
Deferred singleton UUID follow-up
Overview now aggregates singleton IDs before retrieving their nullable UUIDs by
owner and primary key in the same SQL statement. This removes the noncovered UUID
from broad aggregation inputs, allowing the existing IX_Location_HistoryUtc
covering path. Every matching row still contributes to exact cell membership,
counts and extrema. Response shape, shared edges/drill-down, uncertainty,
readiness, cancellation, repeatable-read snapshot and deadlines are unchanged.
There is no application query per cell, added index or combined-scan experiment.
The compiled delivery repeat uses the same research seed and two owner scales,
plus 210,240 unrelated-owner rows. All 30 routed reads return 200/no-store, and
all five complete response pairs agree except root observation time. Three
observations per variant cover only broad and recurring/common-text overviews at
both scales and one large-owner post-write broad control. Candidate SQL is unchanged
production SQL; reconstructed baseline SQL is checked against retained commands.
Use qualified/ in the receipt below: the initial comparison attempt failed its
strict reconstruction check on a missing final newline, was preserved, and was
corrected once with all five SQL shapes checked before seeding again.
| Fresh warm routed overview, ms | Baseline | Deferred UUID |
|---|---|---|
| Broad, 210,240 rows | 544–568 | 254–256 |
| Broad, 1,051,200 rows | 1,871–1,874 | 1,048–1,190 |
| Recurring/common, 210,240 rows | 1,364–1,393 | 1,364–1,662 |
| Recurring/common, 1,051,200 rows | 2,334–2,374 | 2,314–2,323 |
| Post-write broad, 1,051,200 rows | 1,937–2,013 | 1,932–2,030 |
Prepared broad aggregate plans examine 210,240 / 1,051,200 owner index entries instead of all 1,471,680 heap rows, with zero prepared index heap fetches and two singleton UUIDs recovered. Inclusive root shared-block references fall from 247,915 → 3,847 small and 247,918 → 19,244 large. After 500 tracked address edits without vacuum, they fall from 247,995 → 19,454, with 680 covering-scan heap fetches. Natural aggregate execution is 483 → 165 ms small, 1,466 → 717 ms large and 1,403 → 1,092 ms post-write. These are separate diagnostics; shared-block reads do not establish physical disk reads. Broad temporary writes remain zero, with a 32/40 KiB one-batch hash and 25 KiB six-cell output sort.
The large filtered routed control shows no material regression in this selection. Filtered improvement is not established: the small candidate has a 1,662 ms observation, and separate query/projection companions are slower at both sizes. Its slow request includes slower unchanged readiness and uncertainty commands as well as aggregation. Filtered heap access remains; large aggregate spill falls from 668 to 494 temporary write blocks, but that separate replay takes 918 → 1,206 ms. Post-write warm routed latency overlaps baseline despite lower broad buffer work. These observations limit any latency claim; no additional run or optimization is authorized by this receipt.
Separate candidate query-plus-projection is 227 ms for small broad (meets the
one-second target), 1,028 ms for large broad, 1,611/2,562 ms for small/large
recurring/common and 1,854 ms for large post-write broad (misses). Serialization
is below 0.4 ms for these compact responses. Five-second completion does not waive
these misses. Full first-use/warm observations, commands, plans, typed parameters,
parity and both attempts are retained at
/home/stef/.local/share/wayfarer-investigations/918-singleton-delivery-20261024/.
Both guarded fixtures completed cleanup. The wider named workload, lower-seam
semantics, preparation/write/storage and four-read evidence above are reused;
there is no newly measured concurrency gain.
This is a bounded broad-query resource improvement. I918-C and #918 remain incomplete. Independent exact-head review and an explicit measured maintainer readiness decision remain required; filtered workloads and universal sub-second performance are unresolved. No further optimization campaign is automatically authorized. Production/mobile/deployed acceptance remains separate.
Final protective ceiling and operating range
The 24 October 2026 maintainer decision in #918 sets history/page and overview evaluation to fifteen seconds, retaining the separate thirty-second selected-detail ceiling. The SQL timeout remains transaction-local; application-wide and database-wide defaults, preparation timeouts, admission and disconnect handling are unchanged. Prior five-second observations above retain their original provenance.
See the final bounded performance/resource qualification for the 25-year synthetic owner, field-distribution comparisons and mixed-use results. Sub-second target misses require an explicit measured maintainer disposition. No automatic acceptance, production sizing, issue closure or further optimization authorization follows from raising the protective ceiling.