Add partial indexes: blocks(block_type)='mermaid' and notes(note_type)='architecture' #252
Labels
No labels
domain:backend
domain:devops
domain:frontend
status:approved
status:in-progress
status:needs-fix
status:qa
type:bug
type:devops
type:feature
No milestone
No project
No assignees
1 participant
Due date
No due date set.
Dependencies
No dependencies set.
Reference
ldraney/pal-e-api#252
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Type
Feature
Lineage
Standalone — discovered during 2026-04-10 architecture-template gate review. Mermaid + architecture notes identified as first-class query paths currently unindexed. Motivation is semantic self-documentation, not performance — at 23K blocks / 900 notes sequential scans are <5ms.
Scope review round 1: NEEDS_REFINEMENT (review-944-2026-04-10). Fixed in this revision: (1) backing arch note created (
arch-domain-pal-e-docs), (2) arch label canonical spelling established, (3) EXPLAIN verification ACs made deterministic viaSET enable_seqscan = off.Repo
forgejo_admin/pal-e-api(Note: the Python package inside the repo is named
pal_e_docsas an artifact of the pal-e-docs→pal-e-api rename.models.pylives atsrc/pal_e_docs/models.py, Alembic atalembic/.)User Story
As the Superuser (Lucas)
I want semantic-level "first-class" declaration of the mermaid-block and architecture-note query paths via partial indexes
So that the schema self-documents which lookups matter, and future scale degrades gracefully when the knowledge base grows
Traces to:
story:superuser-queryonproject-pal-e-docs.Architecture
arch:blocks— theblockscomponent inarch-domain-pal-e-docsarch:notes— thenotescomponent inarch-domain-pal-e-docs(Canonical spelling is
arch:notes— maps to thenotesDB table row in the Components table. The sibling ticket pal-e-api#251 usesarch:notes-apiwhich refers to a different component at a different abstraction layer — API routes, not DB entities. Not a conflict.)Context
pal-e-docs has two query paths that currently sequential-scan:
list_notes(note_type='architecture')— scans all ~900 notes to find the architecture subset.At current scale (~5ms scans) these are not performance problems. The reason to add partial indexes is semantic documentation: partial indexes are the cheapest way to declare "this is a first-class query path." They self-document the schema and future-proof the query paths before they become hot.
The architecture template prescribes 3 diagram notes per project. With ~15 projects that's ~45 expected architecture notes and ~45 expected mermaid blocks. Current state: 25 mermaid blocks across 8 existing triplets. The indexes make the "find all architecture / find all diagrams" queries first-class as the triplet fills out.
Partial indexes (not full btree on
block_type/note_type) because:mermaidandarchitectureare the query-worthy valuesblock_typewould waste space on 9634headingentries we never filter toFile Targets
Files to create:
alembic/versions/<next>_add_mermaid_and_architecture_partial_indexes.py— new Alembic migrationThe new migration's
down_revisionmust chain to the current Alembic head (verify withalembic headsbefore writing the file).Files the agent should NOT touch:
src/pal_e_docs/models.py— do NOT addIndex(...)declarations in__table_args__for partial indexes; SQLAlchemy's nativeIndexdoesn't expressWHEREclauses cleanly. Keep the indexes migration-only and document them in a brief comment above the affected model class.Acceptance Criteria
ix_blocks_block_type_mermaidas a partial btree:CREATE INDEX ix_blocks_block_type_mermaid ON blocks (block_type) WHERE block_type = 'mermaid'ix_notes_note_type_architectureas a partial btree:CREATE INDEX ix_notes_note_type_architecture ON notes (note_type) WHERE note_type = 'architecture'down_revisionchains correctly to current head (verified viaalembic heads)SET enable_seqscan = off;,EXPLAIN SELECT * FROM blocks WHERE block_type = 'mermaid'showsIndex Scan using ix_blocks_block_type_mermaidSET enable_seqscan = off;,EXPLAIN SELECT * FROM notes WHERE note_type = 'architecture'showsIndex Scan using ix_notes_note_type_architectureWhy
enable_seqscan = off: at current scale (25 mermaid blocks, 25 architecture notes) the PostgreSQL planner may legitimately prefer Seq Scan — the row counts are too small for the index to win on cost. Disabling seqscan forces the planner to use the partial index if it's valid, which is the deterministic way to verify the index is wired correctly regardless of data volume. TheEXPLAINoutput is captured in the PR description as the smoking-gun evidence the index exists and is query-planner-visible.Test Expectations
EXPLAIN ANALYZEoutput (withenable_seqscan = off) captured in PR description showing each partial index is usedalembic upgrade head && alembic downgrade -1 && alembic upgrade headConstraints
op.create_indexwithpostgresql_where=sa.text("...")— the idiomatic Alembic partial-index patternix_<table>_<columns>prefix — seeix_blocks_note_id_position,ix_blocks_anchor_id)blocks.contentfromjsontojsonbin this ticket — that's separate scope (would enable GIN indexes on diagram definitions but is a bigger migration)Checklist
enable_seqscan = off) captured in PR descriptionRelated
project-pal-e-docs— affected projectarch-domain-pal-e-docs— backing architecture note (new)template-architecture— prescribes the diagram triplet this indexesScope Review: NEEDS_REFINEMENT
Review note:
review-944-2026-04-10Scope is technically solid and executable in a single agent pass (~3 min, well under the 5-minute rule). File targets verified on Forgejo — alembic lives in
pal-e-api, current head ist0o1p2q3r4s5, andmodels.pyatsrc/pal_e_docs/models.pyconfirms bothblock_typeandnote_typecolumns plus the existingix_<table>_<columns>naming convention the ticket prescribes. Repo placement is correct (the task brief's claim that models.py is in~/pal-e-docswas a false alarm — the Python package is namedpal_e_docsinside thepal-e-apirepo, an artifact of the #217 rename).Blocking issues:
arch-blocksbacking note exists in pal-e-docs. Create before moving to todo.arch-notesbacking note exists in pal-e-docs. Create before moving to todo.arch:notes, sibling backlog ticket #908 usesarch:notes-api. Decide canonical and update.SET enable_seqscan = offto confirm the partial index is usable. Otherwise AC #4 and #5 may fail non-deterministically.Once the two arch notes exist and the label is reconciled, this ticket is READY.
Scope Review: APPROVED (round 2)
Review note:
review-944-2026-04-10-r2All four round-1 blockers resolved. Ticket is READY to move backlog → todo.
arch-domain-pal-e-docsComponents table hasblocksrownotesrow (forward-references this ticket)SET enable_seqscan = offwith rationaleAlembic head re-verified:
t0o1p2q3r4s5_drop_legacy_boards_table.py. Storystory:superuser-queryverified inproject-pal-e-docsuser-stories. 7 ACs — over the 5-AC caution threshold but tightly coupled to one migration file; no decomposition needed.No action needed. Ready for dev dispatch.