Skip to content

perf: use seperate query to look at identity and key ratelimits when l…#3895

Merged
chronark merged 4 commits intomainfrom
perf/list-keys-query
Sep 1, 2025
Merged

perf: use seperate query to look at identity and key ratelimits when l…#3895
chronark merged 4 commits intomainfrom
perf/list-keys-query

Conversation

@Flo4604
Copy link
Member

@Flo4604 Flo4604 commented Aug 29, 2025

fix performance when listing keys for an api

CleanShot 2025-08-29 at 23 34 41@2x

to 40ish ms CleanShot 2025-08-29 at 23 35 26@2x

What does this PR do?

Fixes #3893

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • Chore (refactoring code, technical debt, workflow improvements)
  • Enhancement (small improvements)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How should this be tested?

Run the query against a develop db and look at the speed / use explain to see that we are using indexes propery with high filter values.

Checklist

Required

  • Filled out the "How to test" section in this PR
  • Read Contributing Guide
  • Self-reviewed my own code
  • Commented on my code in hard-to-understand areas
  • Ran pnpm build
  • Ran pnpm fmt
  • Checked for warnings, there are none
  • Removed all console.logs
  • Merged the latest changes from main onto my branch with git pull origin main
  • My changes don't cause any responsiveness issues

Appreciated

  • If a UI change was made: Added a screen recording or screenshots to this PR
  • Updated the Unkey Docs if changes were necessary

Summary by CodeRabbit

  • New Features

    • List Keys response now groups rate limits by source (key- and identity-based) and exposes additional identity and encryption details for richer results.
  • Refactor

    • Simplified List Keys parameters by removing the workspace filter.
    • Filtering and pagination continue to rely on cursor and identity; overall behavior and limits preserved.

@changeset-bot
Copy link

changeset-bot bot commented Aug 29, 2025

⚠️ No Changeset found

Latest commit: 94e875e

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@vercel
Copy link

vercel bot commented Aug 29, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
dashboard Ready Ready Preview Comment Sep 1, 2025 8:30am
engineering Ready Ready Preview Comment Sep 1, 2025 8:30am

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 29, 2025

📝 Walkthrough

Walkthrough

Removed WorkspaceID from ListLiveKeysByKeyAuthID call site and from generated DB params; the SQL no longer filters by workspace_id. The query’s ratelimits projection was reworked into a UNION-derived table aggregated to JSON and additional identity/encryption fields were exposed in the SELECT.

Changes

Cohort / File(s) Summary
API Handler Param Update
go/apps/api/routes/v2_apis_list_keys/handler.go
Stops passing WorkspaceID to db.ListLiveKeysByKeyAuthIDParams; continues passing KeyAuthID, IDCursor, Identity, Limit.
DB Generated Params & Bindings
go/pkg/db/key_list_live_by_auth_id.sql_generated.go, go/pkg/db/querier_generated.go
Removes WorkspaceID field from ListLiveKeysByKeyAuthIDParams; updates callsite argument bindings to drop workspace arg; doc-comment formatting adjusted.
SQL Query Logic
go/pkg/db/queries/key_list_live_by_auth_id.sql
Removes k.workspace_id = ? predicate; reworks ratelimits as a UNION ALL derived table aggregated to JSON; exposes additional identity/encryption fields and preserves joins/pagination/limit semantics.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Client
  participant API as v2 List Keys Handler
  participant DB as DB: ListLiveKeysByKeyAuthID

  Client->>API: GET /v2/apis/.../keys?identity&cursor&limit
  API->>DB: ListLiveKeysByKeyAuthID(KeyAuthID, IDCursor, Identity, Limit)
  note right of DB #e6f4ea: SQL filters by key_auth_id and identity/id cursor\nNo workspace_id filter
  DB-->>API: Rows (including combined ratelimits JSON + extra identity/encryption fields)
  API-->>Client: JSON response
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Assessment against linked issues

Objective Addressed Explanation
Improve list keys performance (#3893) Change removes workspace filter and restructures ratelimits, but diff lacks benchmarks, index or plan changes to confirm performance gains.
Maintain response compatibility for list keys (#3893) Query now returns additional fields and a restructured ratelimits JSON; response shape change may break existing clients.

Possibly related PRs

  • fix: keys setting vulns #2718 — Removes workspaceId from frontend/trpc inputs; closely related to removing workspace_id from DB and handler surfaces.

Suggested reviewers

  • imeyer
  • chronark
  • perkinsjr
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch perf/list-keys-query

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@Flo4604 Flo4604 marked this pull request as ready for review August 29, 2025 21:45
@Flo4604 Flo4604 marked this pull request as draft August 29, 2025 21:52
@graphite-app
Copy link

graphite-app bot commented Aug 29, 2025

Graphite Automations

"Notify author when CI fails" took an action on this PR • (08/29/25)

1 teammate was notified to this PR based on Andreas Thomas's automation.

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: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
go/apps/api/routes/v2_apis_list_keys/handler.go (2)

186-192: Prevent duplicate first record on paginated fetch.

Given the current SQL uses >=, the first row of the next page duplicates the last row of the previous page. Add a defensive drop of the duplicate (and keep limit+1 for hasMore) so behavior is correct even before the SQL fix lands.

-  // Handle pagination
-  hasMore := len(keyResults) > limit
+  // Handle pagination (drop duplicate if cursor was inclusive)
+  if cursor != "" && len(keyResults) > 0 && keyResults[0].ID == cursor {
+    keyResults = keyResults[1:]
+  }
+  hasMore := len(keyResults) > limit
   var nextCursor *string
   if hasMore {
-    nextCursor = ptr.P(keyResults[len(keyResults)-1].ID)
-    keyResults = keyResults[:limit]
+    nextCursor = ptr.P(keyResults[len(keyResults)-1].ID)
+    keyResults = keyResults[:limit]
   }

After switching SQL to >, this guard remains harmless.


340-372: Nil guard when attaching identity ratelimits.

response.Identity can be nil; setting response.Identity.Ratelimits will panic. Guard it.

-  if len(identityRatelimits) > 0 {
-    response.Identity.Ratelimits = &identityRatelimits
-  }
+  if len(identityRatelimits) > 0 {
+    if response.Identity == nil {
+      response.Identity = &openapi.Identity{}
+    }
+    response.Identity.Ratelimits = &identityRatelimits
+  }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 45e6e76 and 9875a38.

📒 Files selected for processing (4)
  • go/apps/api/routes/v2_apis_list_keys/handler.go (1 hunks)
  • go/pkg/db/key_list_live_by_auth_id.sql_generated.go (2 hunks)
  • go/pkg/db/querier_generated.go (1 hunks)
  • go/pkg/db/queries/key_list_live_by_auth_id.sql (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.go: Follow comprehensive documentation guidelines for Go code as described in go/GO_DOCUMENTATION_GUIDELINES.md
Every public function/type in Go code must be documented
Prefer interfaces for testability in Go code
Use AIDEV-* comments for complex/important code in Go services

Files:

  • go/apps/api/routes/v2_apis_list_keys/handler.go
  • go/pkg/db/querier_generated.go
  • go/pkg/db/key_list_live_by_auth_id.sql_generated.go
**/*.{env,js,ts,go}

📄 CodeRabbit inference engine (CLAUDE.md)

All environment variables must follow the format: UNKEY_<SERVICE_NAME>_VARNAME

Files:

  • go/apps/api/routes/v2_apis_list_keys/handler.go
  • go/pkg/db/querier_generated.go
  • go/pkg/db/key_list_live_by_auth_id.sql_generated.go
🧠 Learnings (2)
📓 Common learnings
Learnt from: ogzhanolguncu
PR: unkeyed/unkey#2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use `ctx.workspace.id` directly instead of fetching the workspace separately for better performance and security.
📚 Learning: 2025-04-08T09:34:24.576Z
Learnt from: ogzhanolguncu
PR: unkeyed/unkey#2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use `ctx.workspace.id` directly instead of fetching the workspace separately for better performance and security.

Applied to files:

  • go/pkg/db/key_list_live_by_auth_id.sql_generated.go
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Test API / API Test Local
  • GitHub Check: Build / Build
  • GitHub Check: Test Go API Local / Test
🔇 Additional comments (7)
go/pkg/db/queries/key_list_live_by_auth_id.sql (1)

78-79: Fix pagination duplication: use exclusive cursor.

k.id >= sqlc.arg(id_cursor) repeats the last record on the next page. Switch to > to make the cursor exclusive.

-  AND k.id >= sqlc.arg(id_cursor)
+  AND k.id > sqlc.arg(id_cursor)
⛔ Skipped due to learnings
Learnt from: Flo4604
PR: unkeyed/unkey#3677
File: go/pkg/db/queries/ratelimit_override_list_by_namespace_id.sql:7-8
Timestamp: 2025-07-30T10:46:56.453Z
Learning: In the Unkey codebase, cursor pagination uses a "limit + 1" approach where queries fetch one extra record beyond the requested limit. The cursor is set to the ID of this extra record (which is not returned to the client), so using `>=` in the WHERE clause is correct because the next page should start from that cursor ID.
Learnt from: ogzhanolguncu
PR: unkeyed/unkey#2896
File: internal/clickhouse/src/ratelimits.ts:468-592
Timestamp: 2025-02-21T11:15:16.185Z
Learning: The cursor logic in getRatelimitOverviewLogs query uses (time, request_id) < (cursorTime, cursorRequestId) comparison which works well with descending order but may need adjustment for ascending sorts based on real usage patterns.
go/pkg/db/key_list_live_by_auth_id.sql_generated.go (3)

101-106: Param removal LGTM.

Dropping WorkspaceID from ListLiveKeysByKeyAuthIDParams matches the SQL and reduces bind overhead.


90-96: Make cursor exclusive in generated SQL.

Reflect the > change in the generated query and regenerate.

-  AND k.id >= ?
+  AND k.id > ?

Re-run sqlc generate.

⛔ Skipped due to learnings
Learnt from: Flo4604
PR: unkeyed/unkey#3677
File: go/pkg/db/queries/ratelimit_override_list_by_namespace_id.sql:7-8
Timestamp: 2025-07-30T10:46:56.453Z
Learning: In the Unkey codebase, cursor pagination uses a "limit + 1" approach where queries fetch one extra record beyond the requested limit. The cursor is set to the ID of this extra record (which is not returned to the client), so using `>=` in the WHERE clause is correct because the next page should start from that cursor ID.

231-238: Bind order confirmed correct; no changes required.

go/pkg/db/querier_generated.go (1)

1365-1450: Doc block matches current (but flawed) SQL; will need regen after ratelimits/cursor fixes.

No functional changes here; just ensure comments are updated post-SQL adjustments for future readers.

After applying the SQL diffs, re-run sqlc generate and verify this doc shows a flat ratelimits array and k.id > ?.

go/apps/api/routes/v2_apis_list_keys/handler.go (2)

172-176: Param update LGTM.

Using only KeyAuthID, IDCursor, Identity, and Limit aligns with the new query.


167-176: Workspace scoping is safe without an extra filter. key_auth_id is a globally unique PK, and we load the API and verify api.WorkspaceID == auth.AuthorizedWorkspaceID before calling ListLiveKeysByKeyAuthID, so all returned keys belong to that workspace.

@Flo4604 Flo4604 marked this pull request as ready for review August 29, 2025 22:05
@github-actions
Copy link
Contributor

github-actions bot commented Aug 29, 2025

Thank you for following the naming conventions for pull request titles! 🙏

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: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
go/pkg/db/queries/key_list_live_by_auth_id.sql (1)

1-95: Indexing checklist to lock in the perf win.

Ensure these indexes exist to guarantee the 40 ms target under load:

  • keys(key_auth_id, id) covering the seek ORDER BY/LIMIT.
  • ratelimits(key_id), ratelimits(identity_id) for both UNION branches.
  • keys_roles(key_id), roles_permissions(role_id), keys_permissions(key_id), permissions(slug), roles(name).

If any are missing, add them in a forward-compatible migration.

go/pkg/db/key_list_live_by_auth_id.sql_generated.go (1)

145-149: Prefer json.RawMessage over interface{} for JSON columns.

Map JSON columns (roles, permissions, role_permissions, ratelimits) to json.RawMessage via sqlc.yaml overrides for safer typing and fewer type assertions. Regenerate after config change.

Example sqlc.yaml snippet:

overrides:
  - db_type: "json"
    go_type:
      type: "encoding/json.RawMessage"
♻️ Duplicate comments (4)
go/pkg/db/queries/key_list_live_by_auth_id.sql (3)

10-22: Stabilize JSON ordering inside roles aggregation.

Move ORDER BY inside JSON_ARRAYAGG to make ordering deterministic; remove the subquery ORDER BY.

- (SELECT JSON_ARRAYAGG(
-                 JSON_OBJECT(
-                         'id', r.id,
-                         'name', r.name,
-                         'description', r.description
-                 )
-         )
-  FROM keys_roles kr
-           JOIN roles r ON r.id = kr.role_id
-  WHERE kr.key_id = k.id
-  ORDER BY r.name),
+ (SELECT JSON_ARRAYAGG(
+                 JSON_OBJECT(
+                         'id', r.id,
+                         'name', r.name,
+                         'description', r.description
+                 ) ORDER BY r.name
+    )
+  FROM keys_roles kr
+  JOIN roles r ON r.id = kr.role_id
+  WHERE kr.key_id = k.id),

24-38: Stabilize JSON ordering inside direct permissions aggregation.

Same pattern; order inside the aggregator.

- (SELECT JSON_ARRAYAGG(
-                 JSON_OBJECT(
-                         'id', p.id,
-                         'name', p.name,
-                         'slug', p.slug,
-                         'description', p.description
-                 )
-         )
-  FROM keys_permissions kp
-       JOIN permissions p ON kp.permission_id = p.id
-  WHERE kp.key_id = k.id
-  ORDER BY p.slug),
+ (SELECT JSON_ARRAYAGG(
+                 JSON_OBJECT(
+                         'id', p.id,
+                         'name', p.name,
+                         'slug', p.slug,
+                         'description', p.description
+                 ) ORDER BY p.slug
+    )
+  FROM keys_permissions kp
+  JOIN permissions p ON kp.permission_id = p.id
+  WHERE kp.key_id = k.id),

40-55: Stabilize JSON ordering inside role-derived permissions aggregation.

Ditto for role_permissions.

- (SELECT JSON_ARRAYAGG(
-                 JSON_OBJECT(
-                         'id', p.id,
-                         'name', p.name,
-                         'slug', p.slug,
-                         'description', p.description
-                 )
-         )
-  FROM keys_roles kr
-       JOIN roles_permissions rp ON kr.role_id = rp.role_id
-       JOIN permissions p ON rp.permission_id = p.id
-  WHERE kr.key_id = k.id
-  ORDER BY p.slug),
+ (SELECT JSON_ARRAYAGG(
+                 JSON_OBJECT(
+                         'id', p.id,
+                         'name', p.name,
+                         'slug', p.slug,
+                         'description', p.description
+                 ) ORDER BY p.slug
+    )
+  FROM keys_roles kr
+  JOIN roles_permissions rp ON kr.role_id = rp.role_id
+  JOIN permissions p ON rp.permission_id = p.id
+  WHERE kr.key_id = k.id),
go/pkg/db/key_list_live_by_auth_id.sql_generated.go (1)

21-67: Propagate JSON ordering fix from source SQL and regenerate.

Roles/permissions aggregations still order outside JSON_ARRAYAGG. Update the source SQL as suggested and re-run sqlc so this constant matches.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 9875a38 and cd05e58.

📒 Files selected for processing (3)
  • go/pkg/db/key_list_live_by_auth_id.sql_generated.go (2 hunks)
  • go/pkg/db/querier_generated.go (1 hunks)
  • go/pkg/db/queries/key_list_live_by_auth_id.sql (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.go: Follow comprehensive documentation guidelines for Go code as described in go/GO_DOCUMENTATION_GUIDELINES.md
Every public function/type in Go code must be documented
Prefer interfaces for testability in Go code
Use AIDEV-* comments for complex/important code in Go services

Files:

  • go/pkg/db/querier_generated.go
  • go/pkg/db/key_list_live_by_auth_id.sql_generated.go
**/*.{env,js,ts,go}

📄 CodeRabbit inference engine (CLAUDE.md)

All environment variables must follow the format: UNKEY_<SERVICE_NAME>_VARNAME

Files:

  • go/pkg/db/querier_generated.go
  • go/pkg/db/key_list_live_by_auth_id.sql_generated.go
🧠 Learnings (3)
📓 Common learnings
Learnt from: ogzhanolguncu
PR: unkeyed/unkey#2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use `ctx.workspace.id` directly instead of fetching the workspace separately for better performance and security.
📚 Learning: 2024-10-20T07:05:55.471Z
Learnt from: chronark
PR: unkeyed/unkey#2294
File: apps/api/src/pkg/keys/service.ts:268-271
Timestamp: 2024-10-20T07:05:55.471Z
Learning: In `apps/api/src/pkg/keys/service.ts`, `ratelimitAsync` is a table relation, not a column selection. When querying, ensure that table relations are included appropriately, not as columns.

Applied to files:

  • go/pkg/db/queries/key_list_live_by_auth_id.sql
  • go/pkg/db/key_list_live_by_auth_id.sql_generated.go
📚 Learning: 2025-04-08T09:34:24.576Z
Learnt from: ogzhanolguncu
PR: unkeyed/unkey#2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use `ctx.workspace.id` directly instead of fetching the workspace separately for better performance and security.

Applied to files:

  • go/pkg/db/key_list_live_by_auth_id.sql_generated.go
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Test API / API Test Local
  • GitHub Check: Test Go API Local / Test
  • GitHub Check: Build / Build
🔇 Additional comments (5)
go/pkg/db/queries/key_list_live_by_auth_id.sql (2)

86-86: Leave the inclusive cursor check (>=)—it’s correct under our “limit + 1” pagination.
We fetch one extra row (LIMIT … + 1) and set the cursor to that row’s ID (which isn’t returned), so the next page must include >= id_cursor to avoid skipping items.

Likely an incorrect or invalid review comment.


81-85: Multi-tenant guard: confirm key_auth_id scoped to caller’s workspace
Listing now filters solely on key_auth_id—double-check the handler only ever passes a key_auth_id tied to ctx.workspace.id (e.g. via an API lookup filtered by workspace_id). Optionally enforce at SQL with a cheap consistency check:

 WHERE k.key_auth_id = sqlc.arg(key_auth_id)
+  AND ka.workspace_id = k.workspace_id
go/pkg/db/querier_generated.go (1)

1365-1455: Generated doc block reflects removed workspace filter; keep it in sync after SQL tweaks.

If you adopt the JSON ordering and pagination changes suggested in the SQL, re-run sqlc to update this block and the compiled query string. Do not hand-edit.

go/pkg/db/key_list_live_by_auth_id.sql_generated.go (2)

98-101: Cursor condition mirrors source; switch to strict ‘>’ if you change SQL.

After adjusting the SQL to k.id > ?, regenerate so this stays consistent.


247-254: Verify arg binding order matches SQL after any edits.

If you touch the WHERE predicates, ensure the placeholders and arg order remain: KeyAuthID, IDCursor, Identity, Identity, Identity, Limit. Regenerate to avoid runtime mismatches.

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: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
go/pkg/db/key_list_live_by_auth_id.sql_generated.go (1)

146-149: Avoid interface{} for JSON columns; use json.RawMessage for zero-copy.

Map aggregated JSON fields (roles, permissions, role_permissions, ratelimits) to json.RawMessage via sqlc.yaml override or CAST(... AS JSON) if needed.

Example sqlc.yaml snippet:

overrides:
  - db_type: "json"
    go_type: "encoding/json.RawMessage"

Regenerate to update field types.

♻️ Duplicate comments (2)
go/pkg/db/querier_generated.go (1)

1326-1348: Add ORDER BY to ratelimits aggregation for stable output.

Specify ORDER BY id inside JSON_ARRAYAGG over combined_rl.

go/pkg/db/key_list_live_by_auth_id.sql_generated.go (1)

69-91: Ratelimits JSON: add ORDER BY for deterministic array.

Order by id within JSON_ARRAYAGG over combined_rl.

-               (SELECT JSON_ARRAYAGG(
-                               JSON_OBJECT(
+               (SELECT JSON_ARRAYAGG(
+                               JSON_OBJECT(
                                        'id', id,
                                        'name', name,
                                        'key_id', key_id,
                                        'identity_id', identity_id,
                                        'limit', `limit`,
                                        'duration', duration,
                                        'auto_apply', auto_apply = 1
-                               )
-                       )
+                               ) ORDER BY id
+                       )
                 FROM (
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between cd05e58 and dc9bd97.

📒 Files selected for processing (2)
  • go/pkg/db/key_list_live_by_auth_id.sql_generated.go (2 hunks)
  • go/pkg/db/querier_generated.go (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.go: Follow comprehensive documentation guidelines for Go code as described in go/GO_DOCUMENTATION_GUIDELINES.md
Every public function/type in Go code must be documented
Prefer interfaces for testability in Go code
Use AIDEV-* comments for complex/important code in Go services

Files:

  • go/pkg/db/key_list_live_by_auth_id.sql_generated.go
  • go/pkg/db/querier_generated.go
**/*.{env,js,ts,go}

📄 CodeRabbit inference engine (CLAUDE.md)

All environment variables must follow the format: UNKEY_<SERVICE_NAME>_VARNAME

Files:

  • go/pkg/db/key_list_live_by_auth_id.sql_generated.go
  • go/pkg/db/querier_generated.go
🧠 Learnings (2)
📓 Common learnings
Learnt from: ogzhanolguncu
PR: unkeyed/unkey#2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: In the Unkey dashboard, when making database queries involving workspaces, use `ctx.workspace.id` directly instead of fetching the workspace separately for better performance and security.
📚 Learning: 2024-10-20T07:05:55.471Z
Learnt from: chronark
PR: unkeyed/unkey#2294
File: apps/api/src/pkg/keys/service.ts:268-271
Timestamp: 2024-10-20T07:05:55.471Z
Learning: In `apps/api/src/pkg/keys/service.ts`, `ratelimitAsync` is a table relation, not a column selection. When querying, ensure that table relations are included appropriately, not as columns.

Applied to files:

  • go/pkg/db/key_list_live_by_auth_id.sql_generated.go
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Test API / API Test Local
  • GitHub Check: Test Go API Local / Test
  • GitHub Check: Build / Build
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (1)
go/pkg/db/querier_generated.go (1)

1271-1277: Adding identity/encryption fields looks correct.

New columns align with downstream row struct and improve API surface.

Copy link
Collaborator

@chronark chronark left a comment

Choose a reason for hiding this comment

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

looks fine to me, but please address/resolve rabbits comments

@Flo4604
Copy link
Member Author

Flo4604 commented Sep 1, 2025

resolved.

@chronark chronark disabled auto-merge September 1, 2025 10:20
@chronark chronark merged commit 33db432 into main Sep 1, 2025
21 of 22 checks passed
@chronark chronark deleted the perf/list-keys-query branch September 1, 2025 10:20
@coderabbitai coderabbitai bot mentioned this pull request Nov 10, 2025
19 tasks
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.

Fix: List Keys Performance

2 participants