Skip to content

refactor(storage): optimize gateway-controller DB schema indexes#1407

Open
renuka-fernando wants to merge 1 commit intowso2:mainfrom
renuka-fernando:refactor
Open

refactor(storage): optimize gateway-controller DB schema indexes#1407
renuka-fernando wants to merge 1 commit intowso2:mainfrom
renuka-fernando:refactor

Conversation

@renuka-fernando
Copy link
Contributor

@renuka-fernando renuka-fernando commented Mar 19, 2026

Purpose

The gateway-controller SQLite schema had several index-related issues:

  • Redundant explicit indexes duplicating implicit indexes created by UNIQUE constraints — both SQLite and PostgreSQL automatically create a B-tree index for every UNIQUE constraint, usable for prefix scans on leading columns.
  • Single-column indexes and UNIQUE constraints without gateway_id as the leading column. Since the DB is shared across multiple gateways, all queries scope by gateway_id first.
  • Indexes on columns never used in WHERE clauses (verified by inspecting all SQL queries in sql_store.go).
  • Inconsistent index naming conventions.

Approach

Removed indexes (redundant — covered by UNIQUE constraint implicit indexes)

Index Table Reason
idx_artifacts_gateway_id(gateway_id) artifacts Covered by UNIQUE(gateway_id, kind, handle) prefix
idx_artifacts_kind(kind) artifacts Covered by UNIQUE(gateway_id, kind, handle) prefix
idx_cert_name(name) certificates Covered by UNIQUE(gateway_id, name) prefix
idx_certificates_gateway_id(gateway_id) certificates Covered by UNIQUE(gateway_id, name) prefix
idx_template_handle(handle) llm_provider_templates Covered by UNIQUE(gateway_id, handle) prefix
idx_llm_provider_templates_gateway_id(gateway_id) llm_provider_templates Covered by UNIQUE(gateway_id, handle) prefix
idx_api_key(api_key) api_keys Covered by UNIQUE(gateway_id, artifact_uuid, api_key) prefix
idx_api_key_api(artifact_uuid) api_keys Covered by UNIQUE(gateway_id, artifact_uuid, name) prefix

Removed indexes (unused — column never appears in a WHERE clause)

Index Table
idx_api_key_expiry(expires_at) api_keys
idx_api_key_source(source) api_keys
idx_api_key_external_ref(external_ref_id) api_keys

Updated indexes (added gateway_id as leading column)

Old New
idx_artifacts_status(status) idx_artifacts_gateway_id_status(gateway_id, status)
idx_cert_expiry(not_after) idx_certificates_gateway_id_expiry(gateway_id, not_after)
idx_api_key_status(status) idx_api_keys_gateway_id_status(gateway_id, status)
idx_created_by(created_by) idx_api_keys_gateway_id_created_by(gateway_id, created_by)

Updated UNIQUE constraints (gateway_id moved to leading column)

Table Old New
certificates UNIQUE(name, gateway_id) UNIQUE(gateway_id, name)
llm_provider_templates UNIQUE(handle, gateway_id) UNIQUE(gateway_id, handle)
api_keys UNIQUE(artifact_uuid, name, gateway_id) UNIQUE(gateway_id, artifact_uuid, name)

Added UNIQUE constraint

Table Constraint
api_keys UNIQUE(gateway_id, artifact_uuid, api_key)

PRIMARY KEY change

Table Old New
api_keys PRIMARY KEY(api_key, gateway_id) PRIMARY KEY(uuid)

The old composite PK on (api_key, gateway_id) was semantically wrong — the natural identifier for a key record is its uuid. Uniqueness of the api key value is now enforced via UNIQUE(gateway_id, artifact_uuid, api_key).

Related Issues

N/A

Checklist

  • Tests added or updated (unit, integration, etc.)
  • Samples updated (if applicable)

Security checks

  • Followed secure coding standards: yes
  • Confirmed no secrets committed: yes

- Put gateway_id as leading column in all UNIQUE constraints
  (certificates, llm_provider_templates, api_keys) so implicit
  B-tree indexes support gateway-scoped prefix queries
- Remove indexes redundant with UNIQUE constraint implicit indexes:
  idx_cert_name, idx_certificates_gateway_id, idx_template_handle,
  idx_llm_provider_templates_gateway_id, idx_api_key, idx_api_key_api
- Replace single-column indexes with composite (gateway_id, col)
  indexes since the DB is shared across multiple gateways
- Drop indexes on api_keys columns (expires_at, source,
  external_ref_id) not used in any WHERE clause in the codebase
- Change api_keys PRIMARY KEY from (api_key, gateway_id) to uuid;
  enforce uniqueness via UNIQUE(gateway_id, artifact_uuid, api_key)
- Standardize index names to use full table names consistently
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 19, 2026

Walkthrough

This PR updates the database schema for the gateway controller, consolidating indexes into composite indexes across multiple tables and restructuring the api_keys table with a PRIMARY KEY change from compound (api_key, gateway_id) to UUID-based, alongside updated uniqueness constraints and a new nullable issuer column.

Changes

Cohort / File(s) Summary
Database Schema Updates
gateway/gateway-controller/pkg/storage/gateway-controller-db.sql
Updated 2026 copyright header. Consolidated multiple single-column indexes into composite indexes across artifacts, certificates, and api_keys tables. Reordered UNIQUE constraint column sequences in artifacts, certificates, and llm_provider_templates. Restructured api_keys PRIMARY KEY from compound (api_key, gateway_id) to (uuid); replaced UNIQUE constraints; added nullable issuer column.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 Indexes consolidated, constraints reordered fine,
UUID primary keys now draw the line,
Gateway tables shuffled in schemas bright,
Composite indexes gleam in database light!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive The PR description addresses Purpose and Approach comprehensively with detailed tables and rationale, but is missing required sections: Goals, User stories, Documentation, Automation tests, Security checks (partially completed), Samples, and Test environment. Complete missing sections from the template: Goals, User stories, Documentation, full Automation tests details, complete Security checks (currently only statements without yes/no answers), Samples, and Test environment details.
✅ Passed checks (2 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The title accurately summarizes the main change: optimizing database schema indexes by removing redundant and unused indexes and reordering UNIQUE constraints.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@renuka-fernando renuka-fernando changed the title refactor(gateway-controller): clean up API, storage, and DB schema refactor(storage): optimize gateway-controller DB schema indexes Mar 19, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
gateway/gateway-controller/pkg/storage/gateway-controller-db.sql (1)

135-139: Consider clarifying the schema comment about index coverage or add a dedicated index for defensive programming.

The schema comment (lines 135-137) states that UNIQUE(gateway_id, artifact_uuid, api_key) covers prefix queries on (gateway_id) and (gateway_id, artifact_uuid). However, queries like GetAPIKeyByKey (line 1320: WHERE api_key = ? AND gateway_id = ?) and DeleteAPIKey (line 1500: WHERE api_key = ? AND gateway_id = ?) filter on api_key without including artifact_uuid, which means the UNIQUE constraint's index cannot efficiently serve these queries due to the intervening column.

That said, GetAPIKeyByKey does not appear to be used in production code (only in test mocks). Actual API key authentication occurs through xDS state-of-the-world snapshots, not direct database lookups. If these queries remain unused or are called infrequently, the optimization is not urgent. If they become part of a hot path in the future, consider adding:

CREATE INDEX IF NOT EXISTS idx_api_keys_gateway_id_api_key ON api_keys(gateway_id, api_key);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@gateway/gateway-controller/pkg/storage/gateway-controller-db.sql` around
lines 135 - 139, The schema comment incorrectly implies the UNIQUE(gateway_id,
artifact_uuid, api_key) index covers queries filtering by api_key and
gateway_id; because api_key is the third column it won't efficiently serve WHERE
api_key = ? AND gateway_id = ? (used by GetAPIKeyByKey and DeleteAPIKey). Fix by
either updating the comment to explicitly state the limitation (that prefix
coverage does not include queries that omit artifact_uuid) or add a defensive
index CREATE INDEX IF NOT EXISTS idx_api_keys_gateway_id_api_key ON
api_keys(gateway_id, api_key) so queries in GetAPIKeyByKey/DeleteAPIKey are
covered; reference the UNIQUE constraint UNIQUE(gateway_id, artifact_uuid,
api_key) and the queries GetAPIKeyByKey and DeleteAPIKey when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@gateway/gateway-controller/pkg/storage/gateway-controller-db.sql`:
- Around line 129-131: The foreign key from application_api_keys (FOREIGN KEY
(api_key_id, gateway_id) REFERENCES api_keys(uuid, gateway_id)) is invalid
because api_keys no longer has a UNIQUE constraint across (uuid, gateway_id);
update the schema by adding a UNIQUE constraint on the parent table api_keys for
the column pair (uuid, gateway_id) (or alternatively change the FK to reference
only uuid if that matches your model) so the referenced columns are either the
PRIMARY KEY or a UNIQUE key; modify the api_keys table definition (where UNIQUE
(gateway_id, artifact_uuid, name), UNIQUE (gateway_id, artifact_uuid, api_key),
PRIMARY KEY (uuid) are defined) to include UNIQUE(uuid, gateway_id) to satisfy
SQLite FK requirements.

---

Nitpick comments:
In `@gateway/gateway-controller/pkg/storage/gateway-controller-db.sql`:
- Around line 135-139: The schema comment incorrectly implies the
UNIQUE(gateway_id, artifact_uuid, api_key) index covers queries filtering by
api_key and gateway_id; because api_key is the third column it won't efficiently
serve WHERE api_key = ? AND gateway_id = ? (used by GetAPIKeyByKey and
DeleteAPIKey). Fix by either updating the comment to explicitly state the
limitation (that prefix coverage does not include queries that omit
artifact_uuid) or add a defensive index CREATE INDEX IF NOT EXISTS
idx_api_keys_gateway_id_api_key ON api_keys(gateway_id, api_key) so queries in
GetAPIKeyByKey/DeleteAPIKey are covered; reference the UNIQUE constraint
UNIQUE(gateway_id, artifact_uuid, api_key) and the queries GetAPIKeyByKey and
DeleteAPIKey when making the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 08888168-0ac1-40a2-b44f-7c20ff7294d3

📥 Commits

Reviewing files that changed from the base of the PR and between 71979ac and 6b947ae.

📒 Files selected for processing (1)
  • gateway/gateway-controller/pkg/storage/gateway-controller-db.sql

Comment on lines +129 to +131
UNIQUE (gateway_id, artifact_uuid, name),
UNIQUE (gateway_id, artifact_uuid, api_key),
PRIMARY KEY (uuid)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Foreign key constraint at Line 220 will break due to removed UNIQUE(uuid, gateway_id).

The application_api_keys table has FOREIGN KEY (api_key_id, gateway_id) REFERENCES api_keys(uuid, gateway_id). SQLite requires FK parent columns to be either the PRIMARY KEY or have a UNIQUE constraint. With PRIMARY KEY (uuid) only, and no UNIQUE(uuid, gateway_id) or UNIQUE(gateway_id, uuid), this FK reference is now invalid.

🐛 Proposed fix: Add UNIQUE constraint for the FK reference
     FOREIGN KEY (artifact_uuid) REFERENCES artifacts(uuid) ON DELETE CASCADE,
     UNIQUE (gateway_id, artifact_uuid, name),
     UNIQUE (gateway_id, artifact_uuid, api_key),
+    UNIQUE (gateway_id, uuid),
     PRIMARY KEY (uuid)
 );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@gateway/gateway-controller/pkg/storage/gateway-controller-db.sql` around
lines 129 - 131, The foreign key from application_api_keys (FOREIGN KEY
(api_key_id, gateway_id) REFERENCES api_keys(uuid, gateway_id)) is invalid
because api_keys no longer has a UNIQUE constraint across (uuid, gateway_id);
update the schema by adding a UNIQUE constraint on the parent table api_keys for
the column pair (uuid, gateway_id) (or alternatively change the FK to reference
only uuid if that matches your model) so the referenced columns are either the
PRIMARY KEY or a UNIQUE key; modify the api_keys table definition (where UNIQUE
(gateway_id, artifact_uuid, name), UNIQUE (gateway_id, artifact_uuid, api_key),
PRIMARY KEY (uuid) are defined) to include UNIQUE(uuid, gateway_id) to satisfy
SQLite FK requirements.

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