Skip to content

db: index sandboxes by ID suffix and by lowercased name - #1522

Merged
ac-pplx merged 2 commits into
mainfrom
ac/admin-list-search-index
Nov 5, 2026
Merged

ac-pplx merged 2 commits into
mainfrom
ac/admin-list-search-index

Conversation

@ac-pplx

@ac-pplx ac-pplx commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Adds two expression indexes on sandboxes for the admin sandbox list's search (#1523). Nothing reads them until that change merges.

  • idx_sandboxes_id_suffix on reverse(replace(id::text, '-', '')) COLLATE "C". The console labels a sandbox with the last eight hex digits of its UUIDv7, because the leading digits are the creation timestamp. Reversed, the IDs that end in a given suffix form one key range.
  • idx_sandboxes_name_lower on left(lower(name), 256) COLLATE "C": the names that start with a text, ignoring case, form one key range.

COLLATE "C" makes each range a byte range under any database collation. The statements read it with plain >= and <, which a generic plan can use as index conditions. A LIKE prefix with text_pattern_ops is indexable only when the pattern is a plan-time constant, so a cached generic plan would scan the table.

The name key keeps the first 256 characters, at most 1 kB. The API caps new names at 256 bytes, but the column is unconstrained. A btree entry over about 2.7 kB would fail the build on an older, longer name and leave an INVALID index.

Each index is built CONCURRENTLY in its own single-statement -- no-transaction migration, following db/README.md and 20261102223448_public_operations_org_state_index. They land alone because the migration guard rejects migrations combined with application changes.

Validation

At head 81164dd:

  • scripts/check-postgres-migrations-squawk.sh: no issues in the 2 up migrations (this also runs the partial-index and UUIDv4 checks).
  • scripts/check-migration-versions.sh and scripts/check-postgres-migrations-isolated.sh pass.

On a fresh local Kind stack deployed from this head (81164dd), the migration job applied both indexes. The stack then served the console checks against an API without the search (#1524). Every database that #1523's cargo nextest run -p pplx-space-api -p pplx-space-cluster-pool -p pplx-space-internal-api-server --all-features creates applies them: 3074 passed at #1523's head 6f5e710.

Build cost: in a disposable Postgres 16 with 300,000 synthetic rows, CREATE INDEX CONCURRENTLY took 0.31 s and 0.28 s. The indexes are 17 MB and 9 MB, beside a 22 MB table. The plans that use them are in #1523.

Severity and risk

Severity: does not apply. This enables the search in #1523.

Risk:

  • Writes. An insert writes one entry into each index. An update that changes neither id nor name and is otherwise HOT stays HOT. An update that is not HOT anyway also inserts an entry into both indexes; one that changes an indexed column such as lifecycle, for example.
  • Building. A concurrent build does not block writers. It waits for older transactions, and runs under the session's lock and statement timeouts, because a single-statement file cannot set its own.
  • A failed build leaves an INVALID index and no history row. To recover, run that migration's down statement by hand and rerun the migration; the comments say how.
  • Rollback: run the down migrations, which drop each index concurrently.

Rollout

Deploy this before #1523, which reads these indexes; until then they only cost writes. As with any migration, an image that does not bundle these versions refuses a database that has applied them, so rolling back below this release means running the down migrations first.

Stack

  1. This pull request: the migrations.
  2. api: search the admin sandbox list by ID, and name owners and pools #1523, which needs these indexes.
  3. ui: search sandboxes on the server, show owners' emails, and drop unused controls #1524.

AI assistance: Claude Code drafted the migrations, their comments and this description; the measurements were run as described.

🤖 Generated with Claude Code

The admin sandbox list is to search sandboxes by the label the console
shows, which is the end of the sandbox's UUIDv7, and by name ignoring
case. No index orders IDs by their end or holds the lowercased name, so
either search would read every sandbox row.

Add two expression btrees, each built CONCURRENTLY in its own
single-statement no-transaction migration: the ID's undashed hex
reversed, and the first 256 characters of the lowercased name, both
COLLATE "C" so the statements can read a prefix as a plain byte range
in custom and generic plans. The name key is cut at 256 characters so a
row stored before the name cap cannot fail the build.
@coderabbitai

coderabbitai Bot commented Nov 5, 2026 •

Copy link
Copy Markdown

Explain Change →

Automatic approval withheld

Automatic approval is disabled by the configured approval policy for this pull request. Review comments and requests for changes continue normally.

Commit: 81164ddc21c4.

No actionable comments were generated in the recent review.

Walkthrough

The migrations add concurrent B-tree indexes for sandbox ID suffix searches and sandbox name prefix searches. The ID index uses hyphen-free, reversed ID text with C collation. The name index uses the first 256 characters of the lowercased name with C collation. Each index has a rollback migration that drops it concurrently if it exists. The migration comments describe transaction requirements and failure recovery.

Merge Risk: Minimal · up to 81164

No actionable merge-blocking issue is identified for these index migrations; they are mergeable after normal migration checks.

Security architecture risk: Low · up to 81164

The change adds search indexes without changing access controls or sandbox records. Concurrent builds and bounded index keys limit disruption, but an interrupted build can require manual cleanup before deployment can proceed.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — Deployment work affects the shared sandbox table, rather than one tenant's records. The relevant exposure is database build and maintenance load or delayed migration completion, not a newly reachable data-disclosure path. Production impact magnitude is not established by the reviewed source.

Trust Boundaries and Controls

  • inferred — Sandbox identifiers and names become index keys through fixed built-in expressions, not executable SQL or a new privileged interface. The DDL-only comparison introduces no new identity transition, tenant filtering rule, credential access, or permission grant. Authorization behavior of the future search consumer remains outside this assessment.

Resilience and Maintainability Implications

  • observed — Concurrent creation avoids the ordinary index build's writer-blocking table lock, but can wait on older snapshots. These single-statement migrations inherit session timeouts and cannot set their own bounds. Safe interruption recovery therefore depends on the documented cleanup procedure rather than automatic retry.
Pre-merge checks: Passed 5

Check name Status Explanation
Title check Passed The title "db: index sandboxes by ID suffix and by lowercased name" follows the required format: a lowercase area ("db") followed by a colon, then a summary starting with a lowercase word ("index"). I…
Description Follows The Pr Template Passed The description contains all required template content: a substantive "## Summary" section; a "## Validation" section with named checks and reported results; a "## Severity and risk" section with seve…
Bug Fixes Name A Regression Test Passed PASS: The pull request changes only four files under db/postgres and changes no files under crates/, shim/, or ui/. It does not fix an application behavior defect, so the regression-test requirement d…
Weakened Safeguards Are Justified Passed PASS. The diff adds only four migration files. It does not remove tests, assertions, CI steps, or repository guards. The Squawk suppressions are justified: the up migrations explain that `CREATE INDEX…
Public-Safe Content Passed PASS. The title, description, commit subjects, and four added SQL files contain no credentials, private keys, internal URLs or hostnames, cloud account IDs, personal absolute paths, private tracker li…
Full details: Title check

Explanation

The title "db: index sandboxes by ID suffix and by lowercased name" follows the required format: a lowercase area ("db") followed by a colon, then a summary starting with a lowercase word ("index"). It clearly names both main changes, the ID suffix index and the lowercased name index, which match the migrations and PR objectives. It has no trailing period and no ticket IDs. Code identifiers keep their case where used, and none are needed here.

Full details: Description Follows The Pr Template

Explanation

The description contains all required template content: a substantive "## Summary" section; a "## Validation" section with named checks and reported results; a "## Severity and risk" section with severity "does not apply" and a stated risk; and an "AI assistance:" line that identifies Claude Code and the drafted content.

Full details: Bug Fixes Name A Regression Test

Explanation

PASS: The pull request changes only four files under db/postgres and changes no files under crates/, shim/, or ui/. It does not fix an application behavior defect, so the regression-test requirement does not apply.

Full details: Weakened Safeguards Are Justified

Explanation

PASS. The diff adds only four migration files. It does not remove tests, assertions, CI steps, or repository guards. The Squawk suppressions are justified: the up migrations explain that CREATE INDEX CONCURRENTLY requires a single -- no-transaction statement, cannot set its own timeouts, and intentionally omits IF NOT EXISTS so failed builds remain visible (lines 18-36 of both up files). The pull request description also documents concurrent rollback drops.

Full details: Public-Safe Content

Explanation

PASS. The title, description, commit subjects, and four added SQL files contain no credentials, private keys, internal URLs or hostnames, cloud account IDs, personal absolute paths, private tracker links, real cluster names, incident details, or undisclosed vulnerabilities. The reported measurements are explicitly for synthetic rows in a disposable PostgreSQL instance, not production data.

Recent review info

Automatic approval policy

Commit: 81164ddc21c4.

Setting Effective value Source
Automatic approvals Disabled Override 4
Merge risk Low Override 2
Security risk Not set Global configuration

Matching overrides (numbered as in the resolved configuration):

  • Override 2 — any author; paths: **, !ui/**; merge risk: Low.
  • Override 4 — any author; paths: .coderabbit.yaml, .coderabbit.yml, coderabbit.yaml, coderabbit.yml, .coderabbit.config.ts, **/AGENTS.md, **/CLAUDE.md, .agents/**, .claude/**, .claude-plugin/**, CONTRIBUTING.md, docs/versioning.md, docs/rust/space-conventions.md, docs/rust/space-review-lessons.md, .github/**, ci/**, !ci/scripts/setup-nextest-*.sh, scripts/source_guards/**, scripts/check*, clippy.toml, deny.toml, .gitleaks.toml, .pre-commit-config.yaml, lefthook.yaml, rust-toolchain.toml, .cargo/**, supply-chain/**, pyproject.toml, uv.lock, **/package.json, **/package-lock.json, **/go.mod, **/go.sum, **/requirements*.txt, **/requirements*.in, proto/**, db/**, crates/l40_api/api/src/auth.rs, crates/l40_api/api/src/auth/**, crates/l40_api/api/src/middleware/auth*, crates/l40_api/api/src/middleware/auth/**, crates/l40_api/api/src/routes/authz.rs, crates/l10_foundation/bearer/**, crates/l10_foundation/rustls/**, crates/l10_foundation/guest/**, crates/l10_foundation/pii/**, shim/**, docker/**, !docker/Dockerfile.rust-build-kind, templates/**, infra/space/aws/**, infra/space/cluster/**, infra/space/identity/**, infra/space/observability/**, infra/space/packer/**, infra/space/kind/charts/**, LICENSE, SECURITY.md; automatic approvals: disabled.

Any matching disabled override withholds approval, regardless of risk. Otherwise, the strictest explicitly set limit wins for each score. Limits omitted by all matching overrides inherit the global configuration.

Not set means no limit was configured after inheritance. Use @coderabbitai configuration to inspect the resolved settings and their sources.

Run configuration

  • Source mode: Auto
  • Repository: perplexityai/SPACE
  • Branch: main
  • File: .coderabbit.yaml
  • Commit: 6673450
  • Layers: Repository YAML; Enforced overrides: Organization
  • Review profile: CHILL
  • Plan: Enterprise
  • Run ID: 9f5b9be5-294f-4578-a678-cadae0be7b03
Commits

Reviewing files that changed from the base of the PR and between 6673450 and 81164dd.

Files selected for processing: 4

  • db/postgres/20261105161135_sandboxes_id_suffix_index.down.sql
  • db/postgres/20261105161135_sandboxes_id_suffix_index.up.sql
  • db/postgres/20261105161136_sandboxes_name_lower_index.down.sql
  • db/postgres/20261105161136_sandboxes_name_lower_index.up.sql

Limit details: You’ve used all 12 included reviews currently available.

@github-actions github-actions Bot added the triage:pending Waiting for a maintainer to triage label Nov 5, 2026
@github-actions

github-actions Bot commented Nov 5, 2026

Copy link
Copy Markdown

Thanks for opening this pull request! A SPACE maintainer will take a look.

CONTRIBUTING.md explains how we review outside contributions.

The migration comments said the ID-suffix and lowercased-name indexes
add no work to updates. They keep an otherwise-HOT update HOT while their
source columns are unchanged, but an update that is not HOT, such as one
changing lifecycle, inserts into both.

@johnlouie-pplx johnlouie-pplx left a comment

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.

Approving. The index expressions match #1523, and the name-length cap avoids failing the build on older long names. The up/down SQL passed on disposable data. Deploy this before #1523.

@ac-pplx
ac-pplx added this pull request to the merge queue Nov 5, 2026
Merged via the queue into main with commit 7cbfbb7 Nov 5, 2026
43 checks passed
@ac-pplx
ac-pplx deleted the ac/admin-list-search-index branch November 5, 2026 21:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

triage:pending Waiting for a maintainer to triage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants