Add index for nullifier hash integer lookups - #2129
Open
soamdesai-tfh wants to merge 4 commits into
Open
Conversation
soamdesai-tfh
temporarily deployed
to
development
July 20, 2026 23:11 — with
GitHub Actions
Inactive
soamdesai-tfh
temporarily deployed
to
development
July 20, 2026 23:28 — with
GitHub Actions
Inactive
soamdesai-tfh
temporarily deployed
to
development
July 20, 2026 23:57 — with
GitHub Actions
Inactive
soamdesai-tfh
marked this pull request as ready for review
July 21, 2026 00:01
igorosip0v
approved these changes
Jul 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
nullifier_hash_int_idxwith the catalog query in the Deployment note: no row, or the exact partial-index definition below withindisvalid = trueandindisready = trueCREATE INDEX CONCURRENTLY, outside a transaction (SQL in the Deployment note)Merging without the pre-create means the migration auto-applies on deploy (the
cli-migrations-v3image runs migrations at container startup) as a plainCREATE INDEX, which write-lockspublic.nullifierfor the whole build — blocking/api/v2/verifyand OIDC authorize writes and potentially stalling Hasura startup. With the pre-create done, the migration is an instant no-op viaIF NOT EXISTS.What changed
public.nullifier(nullifier_hash_int)for rows wherenullifier_hash_int IS NOT NULL.IF NOT EXISTSso a matching index can be created concurrently before deployment on a large target.Why
AtomicUpsertNullifierupdates nullifier rows withnullifier_hash_intequality as the only predicate; with no index on that column, PostgreSQL performed a full sequential scan ofnullifieron every successful verification.FetchAppActionperforms the same equality lookup nested underactions, so it was likely already served by the existingaction_id_usesindex rather than a sequential scan; the new index still gives it a direct lookup path. The existing unique index onnullifier_hashcannot serve this different predicate.The column is nullable and was added after the table without a backfill migration. Excluding
NULLvalues keeps legacy rows that cannot match these equality lookups out of the index, reducing its storage and write-maintenance cost without changing the supported query plans.The pinned Hasura v2.47 migration runner executes migration SQL inside a transaction, so the checked-in migration cannot use
CREATE INDEX CONCURRENTLY.Impact
There is no API or functional behavior change. PostgreSQL gains a direct lookup path for both equality consumers, preventing their execution time from growing linearly with table size. The tradeoff is the normal storage and write-maintenance cost of one targeted B-tree index.
Validation
NULLvalue; the partial index was 16 KB.nullifier_hash_int = $1still used the partial index, executing in approximately 0.8 ms.AtomicUpsertNullifiertwice and confirmed one row was retained whileusesincremented from 1 to 2.pnpm format:checknpx tsc --noEmitDeployment note
First inspect any same-named index.
IF NOT EXISTSchecks the name only, and an interruptedCREATE INDEX CONCURRENTLYcan leave behind an invalid index whose definition still looks correct:Proceed only if this returns no row, or returns the exact partial-index definition below with
indisvalid = trueandindisready = true. If it returns an invalid index (a failed earlier concurrent build), drop it first withDROP INDEX CONCURRENTLY "public"."nullifier_hash_int_idx";and re-create.If the target table is large, pre-create the semantically identical partial index outside the transactional migration. Run it outside any transaction and without
IF NOT EXISTS, so a name conflict fails loudly instead of silently skipping:The checked-in migration then safely no-ops via
IF NOT EXISTSbecause a valid matching index already exists under the same name.