8000
Skip to content

fix(db): index two FK columns on pki_signer_certificate_issuance_jobs - #7357

Open
jaydeep-pipaliya wants to merge 2 commits into
Infisical:mainfrom
jaydeep-pipaliya:fix/pki-signer-issuance-jobs-fk-indexes
Open

fix(db): index two FK columns on pki_signer_certificate_issuance_jobs#7357
jaydeep-pipaliya wants to merge 2 commits into
Infisical:mainfrom
jaydeep-pipaliya:fix/pki-signer-issuance-jobs-fk-indexes

Conversation

@jaydeep-pipaliya
Copy link
Copy Markdown
Contributor

Context

The signer-revamp migration (20260528124443_signer-revamp.ts) created pki_signer_certificate_issuance_jobs with three FK columns but only indexed signerId:

  • caId (uuid notNullable) β†’ certificate_authorities(id) ON DELETE CASCADE β€” no index
  • certificateId (uuid nullable) β†’ certificates(id) ON DELETE SET NULL β€” no index

Postgres does not auto-index FK columns. Deleting a CA or a certificate fires a per-row RI trigger that seq-scans this table for each deleted parent β€” cheap today, but grows with issuance-job volume.

  • caId is notNullable and every row participates β†’ full index.
  • certificateId is populated only after the issuance job completes; most in-flight rows are NULL β†’ partial WHERE certificateId IS NOT NULL index (same shape used across the codebase for mostly-NULL FK columns per backend/CLAUDE.md).

Same pattern as the fix in #7294 for the pam-revamp FK columns.

Changes

  • New migration 20260721093822_add-fk-indexes-pki-signer-issuance-jobs.ts adds both indexes using CREATE INDEX CONCURRENTLY IF NOT EXISTS (so the deploy doesn't take a write-blocking lock) with config = { transaction: false }. Down path uses DROP INDEX CONCURRENTLY IF EXISTS.

Test plan

  • npm run migration:latest-dev from backend/
  • Verify indexes exist:
    SELECT indexname FROM pg_indexes
    WHERE tablename = 'pki_signer_certificate_issuance_jobs'
      AND indexname LIKE '%_id_idx'
    ORDER BY indexname;
  • npm run migration:rollback-dev β€” indexes drop cleanly
  • Re-apply β€” still idempotent (IF NOT EXISTS on up, IF EXISTS on down)

The signer-revamp migration (20260528124443) created the issuance-jobs
table with three FK columns but only indexed signerId. caId (CASCADE
to certificate_authorities) and certificateId (SET NULL to
certificates) have no covering index, so a delete on either parent
seq-scans this table per row via the RI trigger.

Adds a full index on caId (notNullable, every row participates) and a
partial WHERE certificateId IS NOT NULL index on certificateId (only
set after issuance completes, so most in-flight rows are NULL).

Both built CONCURRENTLY so the deploy doesn't take a write-blocking
lock, mirroring the pattern from 20260715060344.
@infisical-cla-app
Copy link
Copy Markdown

πŸ“ Contributor License Agreement required

Before this PR can merge, every contributor must sign the Infisical CLA.
Signing is quick: sign in with GitHub, review the CLA, and accept.

πŸ‘‰ Sign the CLA

Still needs to sign:

Once everyone has signed, the check updates automatically β€” no need to close and reopen the PR.

@greptile-apps
greptile-apps Bot commented Jul 21, 2026
Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds the missing foreign-key indexes for PKI signer certificate issuance jobs. The main changes are:

  • Adds a full concurrent index for caId.
  • Adds a partial concurrent index for non-null certificateId values.
  • Disables migration transactions and restores session timeout settings.

Confidence Score: 4/5

The concurrent-index retry path needs a fix before merging.

  • The index columns and partial predicate match the table schema.
  • Non-transactional execution is appropriate for concurrent index operations.
  • An interrupted build can leave an invalid same-named index that IF NOT EXISTS silently preserves.

backend/src/db/migrations/20260721093822_add-fk-indexes-pki-signer-issuance-jobs.ts

Important Files Changed

Filename Overview
backend/src/db/migrations/20260721093822_add-fk-indexes-pki-signer-issuance-jobs.ts Adds two concurrent FK indexes, but an interrupted build can leave an invalid index that later runs skip.

Reviews (1): Last reviewed commit: "fix(db): index two FK columns on pki_sig..." | Re-trigger Greptile

if ((await knex.schema.hasTable(idx.table)) && (await knex.schema.hasColumn(idx.table, idx.column))) {
const predicate = idx.partial ? `WHERE "${idx.column}" IS NOT NULL` : "";
await knex.raw(`
CREATE INDEX CONCURRENTLY IF NOT EXISTS "${idx.name}"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Invalid Index Survives Retry

If CREATE INDEX CONCURRENTLY is interrupted by the configured timeout, a deployment cancellation, or a lost connection, PostgreSQL can leave an invalid index with this name. A rerun then skips the build because of IF NOT EXISTS, so the migration completes without a usable FK index and parent deletions continue scanning the issuance-job table. The retry path should detect and replace an index whose indisvalid value is false.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch β€” pushed 9f1c443. Added a dropIfInvalid helper that queries pg_index.indisvalid for each target index name and issues DROP INDEX CONCURRENTLY IF EXISTS before the CREATE INDEX CONCURRENTLY IF NOT EXISTS on rerun. Also pushed the same fix to #7294 (aa671ab) since it has the identical pattern and would have the same failure mode.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ’‘ Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 70591ffa06

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with πŸ‘.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

if ((await knex.schema.hasTable(idx.table)) && (await knex.schema.hasColumn(idx.table, idx.column))) {
const predicate = idx.partial ? `WHERE "${idx.column}" IS NOT NULL` : "";
await knex.raw(`
CREATE INDEX CONCURRENTLY IF NOT EXISTS "${idx.name}"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Rebuild invalid concurrent indexes before skipping

When either concurrent build is canceled by this migration's lock/statement timeout, PostgreSQL can leave an INVALID index with the same name; the docs note failed concurrent builds are ignored for querying and should be dropped/retried, while IF NOT EXISTS only checks that a same-name relation exists (https://www.postgresql.org/docs/current/sql-createindex.html). On a retry this line can skip the invalid index and mark the migration applied, so deletes of CAs/certificates keep seq-scanning the child table; check pg_index.indisvalid and drop/rebuild invalid indexes before using IF NOT EXISTS.

Useful? React with πŸ‘Β / πŸ‘Ž.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch β€” pushed 9f1c443. Added a dropIfInvalid helper that queries pg_index.indisvalid for each target index name and issues DROP INDEX CONCURRENTLY IF EXISTS before the CREATE INDEX CONCURRENTLY IF NOT EXISTS on rerun. Also pushed the same fix to #7294 (aa671ab) since it has the identical pattern and would have the same failure mode.

An interrupted CREATE INDEX CONCURRENTLY (deploy cancel,
statement_timeout, lost connection) leaves the index row in pg_class
with indisvalid=false. A rerun with IF NOT EXISTS then no-ops, so the
migration succeeds without producing a usable index and parent
deletions continue to seq-scan.

Check pg_index.indisvalid for each target name before the CREATE and
drop the invalid remnant first, so the retry actually rebuilds.
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

0