docs: the pg LISTEN identifier-truncation wake-collision note (review 004 item 13)
deployment.md's Postgres co-tenancy paragraph and engine-postgres.md's connection-architecture wake-substrate section now carry the 63-byte identifier-truncation collision note as dated annotations: two names longer than 63 bytes sharing a 63-byte prefix silently register the same server-side wake channel; pg_notify (the parameter path, untrun- cated, rejecting >= 64-byte channels) is unaffected, so the collapse is on the registration side. Mitigation: keep mechanism names at most 63 bytes (byte count). The naming contract's deliberate no-cap verdict is untouched. Discharges review 004 'findings recorded, not fixed' item 13 (task docs-pg-identifier-truncation-note).
This commit is contained in:
1 parent
f2f5e03d7c
commit
6a6ffd68de
3 files changed
+82
-9
No files matched your search
@@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
status: stable
|
status: stable
|
||||||
last_updated: "2026-10-11 (the log-facade decision of record landed —
|
last_updated: "2026-10-11 (the pg identifier-truncation wake-collision
|
||||||
|
note — review 004 item 13's doc-fix disposition — added to the
|
||||||
|
Postgres engine section).
|
||||||
|
Earlier the same day: the log-facade decision of record landed —
|
||||||
[ADR-025](decisions/025-log-facade.md): engine diagnostics route
|
[ADR-025](decisions/025-log-facade.md): engine diagnostics route
|
||||||
through the `tracing` facade, silent by default, subscriber wiring on
|
through the `tracing` facade, silent by default, subscriber wiring on
|
||||||
the consumer/binary; the diagnostics-posture section added below.
|
the consumer/binary; the diagnostics-posture section added below.
|
||||||
@@ -90,6 +93,28 @@ engine-owned PostgreSQL schema (default `alkstore`, per-engine option)
|
|||||||
— layout per [ADR-010](decisions/010-queue-semantics-depth.md) §8
|
— layout per [ADR-010](decisions/010-queue-semantics-depth.md) §8
|
||||||
(resolved from [queues.md](queues.md)'s namespace bullet).
|
(resolved from [queues.md](queues.md)'s namespace bullet).
|
||||||
|
|
||||||
|
One pg-specific naming caveat, dated per the family's amendment
|
||||||
|
convention *(annotated 2026-10-11 — review 004 item 13, recorded
|
||||||
|
"not fixed" in the review with a fold-into-the-next-doc-pass
|
||||||
|
disposition; this note is that fix)*: the wake machinery
|
||||||
|
interpolates the channel as a quoted SQL identifier on
|
||||||
|
`LISTEN`/`UNLISTEN`, and PostgreSQL truncates identifiers to
|
||||||
|
**63 bytes (`NAMEDATALEN` − 1)** server-side — with only a
|
||||||
|
`NOTICE`, never an error — so two distinct names longer than 63
|
||||||
|
bytes sharing a 63-byte prefix register the **same** server-side
|
||||||
|
wake channel (a silent cross-channel collision). The `pg_notify`
|
||||||
|
path is unaffected by the truncation — it carries the channel as a
|
||||||
|
text parameter, not an identifier, and the server rejects channels
|
||||||
|
of 64 bytes or more there ("channel name too long") — so the
|
||||||
|
collapse is on the registration side. The
|
||||||
|
[naming contract](core-contract.md#naming--reserved-namespace)
|
||||||
|
deliberately caps no name (the length-independence verdict stands),
|
||||||
|
so nothing engine-side warns. Mitigation:
|
||||||
|
**keep mechanism names under the PostgreSQL identifier limit —
|
||||||
|
at most 63 bytes; the limit counts bytes, not characters, so
|
||||||
|
multibyte names reach it sooner.** Distinct names within the limit
|
||||||
|
cannot collide.
|
||||||
|
|
||||||
### Mem engine
|
### Mem engine
|
||||||
|
|
||||||
- No connections — there is nothing to budget. The engine instance
|
- No connections — there is nothing to budget. The engine instance
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
status: stable
|
status: stable
|
||||||
last_updated: "2026-10-10 (review-wave-5 gate: the verification backlog fully discharged in the version-stamped contract suite, green against this engine; spec flipped draft → stable)"
|
last_updated: "2026-10-11 (review 004 item 13's wake-channel
|
||||||
|
identifier-truncation note — the doc-fix disposition — added to the
|
||||||
|
connection-architecture wake-substrate section).
|
||||||
|
Earlier, 2026-10-10 (review-wave-5 gate: the verification backlog fully discharged in the version-stamped contract suite, green against this engine; spec flipped draft → stable)"
|
||||||
---
|
---
|
||||||
|
|
||||||
# Postgres engine
|
# Postgres engine
|
||||||
@@ -60,6 +63,26 @@ obligations live in the core spec.
|
|||||||
broadcast re-subscribe (no server round-trips); per-channel
|
broadcast re-subscribe (no server round-trips); per-channel
|
||||||
connections are never warranted at this scale (POC-verified).
|
connections are never warranted at this scale (POC-verified).
|
||||||
|
|
||||||
|
One wake-channel naming hazard, noted per the review 004 item 13
|
||||||
|
disposition *(annotated 2026-10-11)*: the forwarder's
|
||||||
|
`LISTEN`/`UNLISTEN` carry the channel as a quoted SQL identifier
|
||||||
|
(`quote_ident` — the POC's quoting shape), and PostgreSQL truncates
|
||||||
|
identifiers to 63 bytes
|
||||||
|
(`NAMEDATALEN` − 1) server-side — with only a `NOTICE`, never an
|
||||||
|
error — so two distinct names longer than 63 bytes sharing a
|
||||||
|
63-byte prefix register the same server-side wake channel, silently.
|
||||||
|
The `pg_notify` path is unaffected by the truncation — it carries
|
||||||
|
the channel as a text parameter, not an identifier (channels of 64
|
||||||
|
bytes or more are rejected there server-side) — so the collapse is
|
||||||
|
on the registration side, not the send side. Names are unbounded by
|
||||||
|
the contract's naming rules (deliberate; [ADR-008](decisions/008-contract-v1-pinning.md)
|
||||||
|
§4), nothing engine-side warns, and the machinery itself is
|
||||||
|
name-length agnostic (63 bytes is a server-side limit, not an
|
||||||
|
engine check). Mitigation is a consumer obligation: keep channel
|
||||||
|
names under the PostgreSQL identifier limit (at most 63 bytes; the
|
||||||
|
limit counts bytes, not characters) — the operator-facing statement
|
||||||
|
lives in the [deployment matrix](deployment.md).
|
||||||
|
|
||||||
## Mapping the contract
|
## Mapping the contract
|
||||||
|
|
||||||
| Contract piece | Engine realization |
|
| Contract piece | Engine realization |
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
id: docs-pg-identifier-truncation-note
|
id: docs-pg-identifier-truncation-note
|
||||||
name: Document the pg identifier-truncation collision in the release text (review 004 item 13)
|
name: Document the pg identifier-truncation collision in the release text (review 004 item 13)
|
||||||
status: pending
|
status: completed
|
||||||
depends_on: []
|
depends_on: []
|
||||||
scope: single
|
scope: single
|
||||||
risk: trivial
|
risk: trivial
|
||||||
@@ -32,12 +32,12 @@ annotation per the family's convention.
|
|||||||
|
|
||||||
## Acceptance Criteria
|
## Acceptance Criteria
|
||||||
|
|
||||||
- [ ] `deployment.md` carries the truncation-collision note with a
|
- [x] `deployment.md` carries the truncation-collision note with a
|
||||||
dated annotation (mechanism + mitigation, honestly worded)
|
dated annotation (mechanism + mitigation, honestly worded)
|
||||||
- [ ] `engine-postgres.md` carries it too if a wake-channel section
|
- [x] `engine-postgres.md` carries it too if a wake-channel section
|
||||||
exists there (or the absence recorded in Notes)
|
exists there (or the absence recorded in Notes)
|
||||||
- [ ] The validation contract's no-cap verdict untouched
|
- [x] The validation contract's no-cap verdict untouched
|
||||||
- [ ] Doc gate green
|
- [x] Doc gate green
|
||||||
|
|
||||||
## References
|
## References
|
||||||
|
|
||||||
@@ -51,8 +51,33 @@ annotation per the family's convention.
|
|||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
> To be filled by implementation agent
|
Deployment matrix note: `deployment.md` has no standalone wake-channel
|
||||||
|
section — the landing site is the Postgres engine's co-tenancy
|
||||||
|
paragraph under "Connection budgets" (the one place the matrix
|
||||||
|
discusses naming), as a dated amendment annotation per the family
|
||||||
|
convention. `engine-postgres.md` has no dedicated wake-channel section
|
||||||
|
either; the note landed at the end of "Connection architecture" (the
|
||||||
|
wake substrate lives there) with a cross-reference to the
|
||||||
|
deployment-matrix statement. Accuracy sharpening beyond the review's
|
||||||
|
text (verified against Postgres master `commands/async.c`):
|
||||||
|
`pg_notify` does not merely escape identifier truncation — the server
|
||||||
|
*rejects* channels of 64 bytes or more there ("channel name too
|
||||||
|
long", an error), while quoted identifiers on the `LISTEN` side
|
||||||
|
truncate to 63 bytes silently (a `NOTICE`); the notes state the
|
||||||
|
collapse is on the registration side. The mitigation is phrased as a
|
||||||
|
consumer obligation (names at most 63 bytes; a byte count, not a
|
||||||
|
character count) — no validation contract change, per the task
|
||||||
|
description.
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
> To be filled on completion
|
Landed the pg identifier-truncation collision note in
|
||||||
|
`docs/architecture/deployment.md` (dated annotation, Postgres engine
|
||||||
|
section) and `docs/architecture/engine-postgres.md` (connection
|
||||||
|
architecture, wake-substrate section) — mechanism, honest send-vs-
|
||||||
|
registration-side statement, and the keep-names-under-63-bytes
|
||||||
|
mitigation. Frontmatter `last_updated` of both files carried forward.
|
||||||
|
Verified: `RUSTDOCFLAGS="-D warnings" cargo doc --no-deps` green,
|
||||||
|
`cargo fmt --check` green, `cargo clippy --all-targets -- -D warnings`
|
||||||
|
green; `core-contract.md`'s no-cap naming verdict untouched (diff
|
||||||
|
touching only the two doc files).
|
||||||
Reference in new issue
Block a user