Postgres engine: schema bootstrap — engine-owned schema, table family, idempotent DDL, schema-prefixed indexes (task pg-engine-schema)
This commit is contained in:
1 parent
4caea21098
commit
2f1353bd41
6 files changed
+951
-9
No files matched your search
@@ -1,7 +1,7 @@
|
||||
---
|
||||
id: pg-engine-schema
|
||||
name: Postgres engine — schema bootstrap (engine-owned schema, table family)
|
||||
status: pending
|
||||
status: completed
|
||||
depends_on: []
|
||||
scope: narrow
|
||||
risk: low
|
||||
@@ -79,17 +79,18 @@ index exists.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `schema.rs` with an idempotent `bootstrap(conn, schema_name)`
|
||||
- [x] `schema.rs` with an idempotent `bootstrap(conn, schema_name)`
|
||||
creating the full table family + indexes in the one schema
|
||||
- [ ] Column sets match the contract's value shapes (the constructors'
|
||||
- [x] Column sets match the contract's value shapes (the constructors'
|
||||
field lists; stamp columns per ADR-010 §3a; `claimed_at` per
|
||||
ADR-021 §2)
|
||||
- [ ] Payload columns are `bytea` (exact-bytes posture, JSONB
|
||||
- [x] Payload columns are `bytea` (exact-bytes posture, JSONB
|
||||
rejected — documented in the module docs with the reason)
|
||||
- [ ] Schema name is a parameter (default `alkstore` decided at the
|
||||
- [x] Schema name is a parameter (default `alkstore` decided at the
|
||||
`PgOpts` layer, next task)
|
||||
- [ ] Idempotence + table-shape tests green against the harness server
|
||||
- [ ] `cargo test -p alkstore-postgres`, clippy `-D warnings`, fmt clean
|
||||
- [x] Indexes: idempotence + table-shape tests green against the
|
||||
harness server
|
||||
- [x] `cargo test -p alkstore-postgres`, clippy `-D warnings`, fmt clean
|
||||
|
||||
## References
|
||||
|
||||
@@ -103,8 +104,80 @@ index exists.
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
Decisions of record made while implementing (the description didn't
|
||||
pin them):
|
||||
|
||||
- **Table names**: `job`, `dead`, `stream_events`, `stream_offsets`,
|
||||
`schedule`, `locks` — fixed constants exported as
|
||||
`alkstore_postgres::tables` for the later mechanism tasks' SQL.
|
||||
Table names do not ride configuration; only the schema name does.
|
||||
- **Index-name qualification**: Postgres rejects schema-qualified
|
||||
index names at `CREATE INDEX`, but `IF NOT EXISTS` on a *bare* name
|
||||
checks existence across all schemas — a co-tenant's unrelated
|
||||
`public.job_claim_idx` would silently skip our index creation. So
|
||||
every engine index is named `{schema}_{table}_{suffix}`
|
||||
(`job_claim_idx`, `job_pending_idx`, `job_deadline_idx`,
|
||||
`dead_retention_idx`, `events_read_idx`, `schedule_fire_idx`,
|
||||
`locks_expiry_idx`, exported as `indexes::`) — schema-prefixed and
|
||||
quoted, tested against a co-tenant carrying the bare name.
|
||||
Sub-HOT-path indexes (`job_pending`, `job_deadline`, the substrate's
|
||||
trio) carried over from the reference shapes — the claim/heartbeat
|
||||
paths the queue task re-derives ride them.
|
||||
- **Reserved-word columns**: the offsets checkpoint column is
|
||||
`"offset"` and the events key column `"key"` — both quoted here and
|
||||
flagged in the module docs so every later task's SQL keeps the
|
||||
quotes. The offsets column carries a non-negative CHECK (cursors
|
||||
start at 0); monotonicity itself stays in the streams task's
|
||||
GREATEST-guarded upsert.
|
||||
- **Identifier quoting**: `quote_identifier` (doubles embedded `"`)
|
||||
is the engine's injection boundary for the consumer-supplied schema
|
||||
name, exported for later tasks; tested hostile (SQL-breakout schema
|
||||
name stays inert single-schema DDL).
|
||||
- **Schedule columns** anchor to the substrate's
|
||||
`__alkstore_scheduler_tasks` concrete-stamp shape, `enabled` dropped
|
||||
(no pause/resume in the collapse surface): `name` PK, `spec`,
|
||||
`queue`, `payload`, `priority`, `expires_s`, `next_fire_at` (the
|
||||
tick's boundary column), `max_attempts` (default 3),
|
||||
`visibility_timeout_s` (default 300), `backoff_base_s` (default 5),
|
||||
`dead_letter_retention_s`. No `last_fire_at` (`next_fire_at` is the
|
||||
only state the tick math needs).
|
||||
- **Dead table** mirrors the job table's live columns + the diagnosis
|
||||
fields (`last_error`, `died_at NOT NULL`), `id` a plain BIGINT PK
|
||||
(carried from the move, not a sequence); retention index
|
||||
`(queue, died_at)`.
|
||||
- **Job table** = the `Job::from_row` field list of record verbatim +
|
||||
the five stamp columns; `state TEXT DEFAULT 'pending'` (SQLite
|
||||
parity — the claim predicates read the text values); three indexes
|
||||
(claim-ordering partial `WHERE state IN ('pending','processing')`
|
||||
per ADR-010 §1's ordering row, pending `(queue, run_at)`, processing
|
||||
deadline `(queue, claim_expires_at)`).
|
||||
- **`thiserror` added to the crate** (v2, same as core/SQLite) for
|
||||
`BootstrapError`; the `open` task maps it onto `Error::Database`
|
||||
next task.
|
||||
- **Harness convention** (ride env, never hardcoded, skip-clean
|
||||
server-less): `ALKSTORE_PG_HOST/PORT/USER/PASSWORD/DB` — host
|
||||
127.0.0.1, port 15432, user/password `postgres`/`poc`, db `blobs`
|
||||
(the `pglo-poc` container's). The next tasks' tests should reuse
|
||||
these variable names. `bootstrap` itself takes any
|
||||
`&tokio_postgres::Client` (NoTls in tests; TLS is the `open` task's
|
||||
concern via deadpool config, not this module's).
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
Landed: `alkstore-postgres/src/schema.rs` — the engine-owned schema
|
||||
module (`bootstrap(conn, schema_name)` idempotent DDL over
|
||||
`batch_execute`; `DEFAULT_SCHEMA = "alkstore"`; `quote_identifier`;
|
||||
`tables`/`indexes` name constants; `BootstrapError`), wired into
|
||||
`lib.rs` re-exports, plus `tests/schema_tests.rs` (9 tests) driven
|
||||
against the harness server via env-carried DSN, skipping cleanly
|
||||
server-less. Table family per the acceptance criteria (job/dead/
|
||||
stream_events/stream_offsets/schedule/locks, bytea payloads, bigserial
|
||||
offsets, claim-ordering partial index with the pinned ordering row).
|
||||
Verified: 9/9 green against the harness server (idempotence ×3 runs,
|
||||
per-table column sets, bytea types, bigserial monotone + gap-on-delete,
|
||||
claim index def fragments incl. `priority DESC` + partiality, schema
|
||||
name as parameter incl. reserved-word name `select`, co-tenancy
|
||||
re-bootstrap, co-tenant bare-index-name collision exclusion, hostile
|
||||
schema-name quoting); workspace `cargo test` green server-less (236
|
||||
tests, pg tests skipped per convention); clippy `-D warnings` and fmt
|
||||
clean workspace-wide.
|
||||
Reference in new issue
Block a user