Skip to content

perf(db): composite indexes for every list-query pattern#70

Merged
CryptoJones merged 1 commit into
masterfrom
perf/list-query-indexes
May 18, 2026
Merged

perf(db): composite indexes for every list-query pattern#70
CryptoJones merged 1 commit into
masterfrom
perf/list-query-indexes

Conversation

@CryptoJones
Copy link
Copy Markdown
Owner

Summary

Every bycompany/bycustomer/byvendor/byheader/byjob endpoint runs WHERE <parent>Id = ? AND <arch> = false. TimeEntry already had composite indexes for this; the other 13 entities were sequential-scanning. Migration 20260517100000-list-query-indexes adds matching IF NOT EXISTS indexes everywhere.

Test plan

  • Migration applies cleanly to the live compose Postgres (45 ms)
  • \di dbo.* confirms all 13 new indexes
  • vitest unchanged (no app code touched): 261 + 4 skipped
  • Down migration reverses with DROP INDEX IF EXISTS

Index list

Table Index Columns
Customer Customer_compId_arch_idx (custCompId, custArch)
Worker Worker_compId_arch_idx (workerCompId, workerArch)
BillingType BillingType_compId_arch_idx (btCompId, btArch)
InventoryItem InventoryItem_compId_arch_idx (invitCompId, invitArch)
PurchaseOrderVendors POVendors_compId_arch_idx (povCompId, povArch)
InventoryTransactions InventoryTransactions_compId_arch_idx (invtCompanyId, invtArch)
Job Job_custId_arch_idx (jobCustId, jobArch)
Invoice Invoice_custId_arch_idx (invCustId, invArch)
CustomerPayment CustomerPayment_custId_arch_idx (cpayCustId, cpayArch)
PurchaseOrderHeaders POHeaders_povId_arch_idx (pohPovId, pohArch)
PurchaseOrderLines POLines_pohId_arch_idx (polpoh, polArch)
InvoiceJob InvoiceJob_invId_arch_idx (injbInvId, injbArch)
ProductEntry ProductEntry_jobId_arch_idx (pentJobId, penArch)

Proudly Made in Nebraska. Go Big Red! 🌽 https://xkcd.com/2347/

Every `GET /v1/<entity>/bycompany|bycustomer|byvendor|byheader|byjob/:id`
endpoint runs the same query shape:

  SELECT ... WHERE "<parent>Id" = ? AND "<arch>" = false
  ORDER BY ... LIMIT ?;

Without an index on `(<parent>Id, <arch>)` that's a sequential
scan against a growing table. TimeEntry already has these
indexes (added in setup/TimeEntry.sql). This migration adds the
same pattern to the other 13 entities:

  Direct-compId scoping:
    Customer, Worker, BillingType, InventoryItem,
    PurchaseOrderVendors, InventoryTransactions

  Customer-scoped:
    Job, Invoice, CustomerPayment

  Header/vendor/job-scoped:
    PurchaseOrderHeaders (povId), PurchaseOrderLines (polpoh),
    InvoiceJob (invId), ProductEntry (jobId, penArch typo)

All `CREATE INDEX IF NOT EXISTS` so operators who've added some
of these by hand don't trip. The down() reverses with
`DROP INDEX IF EXISTS` (covers a partial-failed up).

Verified locally against the running compose Postgres: migration
applies in 45ms, all 13 indexes present in `\\di dbo.*`.

Suite: 261 / 261 + 4 integration skipped (no app-code changes).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@CryptoJones CryptoJones merged commit 1fe15be into master May 18, 2026
2 of 3 checks passed
@CryptoJones CryptoJones deleted the perf/list-query-indexes branch May 18, 2026 02:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant