Skip to content

fix: delete root key returns wrong code#3636

Merged
Flo4604 merged 4 commits intomainfrom
eng-1942-a-deleted-root-key-should-return-the-correct-error-in-v2
Jul 22, 2025
Merged

fix: delete root key returns wrong code#3636
Flo4604 merged 4 commits intomainfrom
eng-1942-a-deleted-root-key-should-return-the-correct-error-in-v2

Conversation

@Flo4604
Copy link
Member

@Flo4604 Flo4604 commented Jul 21, 2025

What does this PR do?

Fixes #3625

The issue was that swr didn't tell us the status code of the cache entry so if we did a miss and set the key to null bc it doesnt exist we would just return a null value and not detect that.

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?

  • Test A
  • Test B

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

    • Added comprehensive tests to validate cache hit, miss, and null states for the caching system.
    • Enhanced cache handling to explicitly report cache hit status in relevant API responses and service methods.
  • Bug Fixes

    • Improved cache invalidation checks to ensure accurate status reporting after deletions or cache misses.
  • Tests

    • Updated and expanded test coverage for caching behavior, including new scenarios for stale and null cache states.
    • Refactored test code to simplify generic type usage.
  • Refactor

    • Updated method signatures to consistently return cache hit status across the caching system.

@linear
Copy link

linear bot commented Jul 21, 2025

@changeset-bot
Copy link

changeset-bot bot commented Jul 21, 2025

⚠️ No Changeset found

Latest commit: 72684c3

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 Jul 21, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

2 Skipped Deployments
Name Status Preview Comments Updated (UTC)
dashboard ⬜️ Ignored (Inspect) Visit Preview Jul 22, 2025 7:59am
engineering ⬜️ Ignored (Inspect) Visit Preview Jul 22, 2025 7:59am

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jul 21, 2025

📝 Walkthrough

Walkthrough

The changes update the cache interface and implementation to return an explicit cache hit status in addition to the value and error. All usages and wrappers of the cache's SWR method are updated accordingly. Tests are added and updated to verify cache hit/miss/null semantics. Some handler and service logic now branches on the new cache hit status to provide more accurate error handling.

Changes

File(s) Change Summary
go/pkg/cache/cache.go
go/pkg/cache/interface.go
go/pkg/cache/noop.go
go/pkg/cache/middleware/tracing.go
Updated the SWR method signature to return an additional CacheHit value, and adjusted all implementations and middleware to propagate and handle this new return value.
go/pkg/cache/cache_test.go
go/pkg/cache/simulation_test.go
Removed explicit type parameters from cache.New calls in tests, relying on type inference. No logic changes.
go/pkg/cache/swr_test.go Added a new comprehensive test suite TestSWR_CacheHit to verify cache hit, miss, and null behaviors under various scenarios using SWR.
go/apps/api/routes/v2_apis_delete_api/cache_validation_test.go Updated the test to capture and assert cache hit status (Hit or Null) when checking for cache presence or invalidation.
go/apps/api/routes/v2_ratelimit_limit/handler.go Modified handler logic to branch on the new cache hit status, returning a specific error if the namespace is missing due to a cache null state.
go/internal/services/keys/get.go Updated service logic to branch on the cache hit status, immediately returning a "key does not exist" error if the cache indicates a null hit.

Sequence Diagram(s)

sequenceDiagram
    participant Caller
    participant Service
    participant Cache
    participant Origin

    Caller->>Service: Get(ctx, sess, key)
    Service->>Cache: SWR(ctx, key, refreshFromOrigin, op)
    alt Cache Hit
        Cache-->>Service: value, Hit, nil
        Service-->>Caller: value, nil
    else Cache Null
        Cache-->>Service: nil, Null, nil
        Service-->>Caller: StatusNotFound, "key does not exist"
    else Cache Miss/Error
        Cache->>Origin: refreshFromOrigin(ctx)
        Origin-->>Cache: value/error
        Cache-->>Service: value, Miss, error
        Service-->>Caller: value/error
    end
Loading

Estimated code review effort

3 (~45 minutes)


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f82797c and 72684c3.

📒 Files selected for processing (5)
  • go/apps/api/routes/v2_ratelimit_limit/handler.go (3 hunks)
  • go/pkg/cache/cache.go (3 hunks)
  • go/pkg/cache/interface.go (1 hunks)
  • go/pkg/cache/middleware/tracing.go (1 hunks)
  • go/pkg/cache/noop.go (1 hunks)
🧠 Learnings (1)
go/apps/api/routes/v2_ratelimit_limit/handler.go (4)

Learnt from: chronark
PR: #2544
File: apps/api/src/pkg/env.ts:4-6
Timestamp: 2024-10-23T12:05:31.121Z
Learning: The cloudflareRatelimiter type definition in apps/api/src/pkg/env.ts should not have its interface changed; it should keep the limit method returning Promise<{ success: boolean }> without additional error properties.

Learnt from: ogzhanolguncu
PR: #2707
File: apps/dashboard/lib/trpc/routers/ratelimit/createOverride.ts:63-63
Timestamp: 2024-12-05T13:27:55.555Z
Learning: In apps/dashboard/lib/trpc/routers/ratelimit/createOverride.ts, when determining the maximum number of rate limit overrides (max), the intentional use of const max = hasWorkspaceAccess("ratelimitOverrides", namespace.workspace) || 5; allows max to fall back to 5 when hasWorkspaceAccess returns 0 or false. This fallback behavior is expected and intended in the codebase.

Learnt from: ogzhanolguncu
PR: #3380
File: apps/dashboard/app/(app)/ratelimits/[namespaceId]/namespace-navbar.tsx:35-41
Timestamp: 2025-06-19T10:39:16.254Z
Learning: For critical components like namespace navigation that fetch essential data, error handling may not be necessary if query failures would indicate system-wide infrastructure issues rather than recoverable errors.

Learnt from: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.464Z
Learning: For debugging database replica usage in go/pkg/db/replica.go, it's acceptable to mark QueryRowContext operations as "success" even though SQL errors only surface during row.Scan() calls. The timing metrics are the primary concern for debugging replica performance patterns.

🧰 Additional context used
🧠 Learnings (1)
go/apps/api/routes/v2_ratelimit_limit/handler.go (4)

Learnt from: chronark
PR: #2544
File: apps/api/src/pkg/env.ts:4-6
Timestamp: 2024-10-23T12:05:31.121Z
Learning: The cloudflareRatelimiter type definition in apps/api/src/pkg/env.ts should not have its interface changed; it should keep the limit method returning Promise<{ success: boolean }> without additional error properties.

Learnt from: ogzhanolguncu
PR: #2707
File: apps/dashboard/lib/trpc/routers/ratelimit/createOverride.ts:63-63
Timestamp: 2024-12-05T13:27:55.555Z
Learning: In apps/dashboard/lib/trpc/routers/ratelimit/createOverride.ts, when determining the maximum number of rate limit overrides (max), the intentional use of const max = hasWorkspaceAccess("ratelimitOverrides", namespace.workspace) || 5; allows max to fall back to 5 when hasWorkspaceAccess returns 0 or false. This fallback behavior is expected and intended in the codebase.

Learnt from: ogzhanolguncu
PR: #3380
File: apps/dashboard/app/(app)/ratelimits/[namespaceId]/namespace-navbar.tsx:35-41
Timestamp: 2025-06-19T10:39:16.254Z
Learning: For critical components like namespace navigation that fetch essential data, error handling may not be necessary if query failures would indicate system-wide infrastructure issues rather than recoverable errors.

Learnt from: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.464Z
Learning: For debugging database replica usage in go/pkg/db/replica.go, it's acceptable to mark QueryRowContext operations as "success" even though SQL errors only surface during row.Scan() calls. The timing metrics are the primary concern for debugging replica performance patterns.

⏰ 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). (7)
  • GitHub Check: Test Go API Local / Test
  • GitHub Check: Test Agent Local / test_agent_local
  • GitHub Check: Build / Build
  • GitHub Check: Test API / API Test Local
  • GitHub Check: Test Packages / Test
  • GitHub Check: autofix
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (5)
go/apps/api/routes/v2_ratelimit_limit/handler.go (1)

73-74: LGTM!

The SWR method call correctly captures the new hit return value to handle cache hit states.

go/pkg/cache/interface.go (1)

22-22: LGTM!

The updated SWR signature provides explicit cache hit status information, enabling better handling of cache states throughout the codebase.

go/pkg/cache/noop.go (1)

25-27: LGTM!

The noop cache correctly implements the updated SWR interface, appropriately returning Miss for all operations.

go/pkg/cache/middleware/tracing.go (1)

89-103: LGTM!

The tracing middleware correctly propagates the cache hit status while maintaining its tracing functionality.

go/pkg/cache/cache.go (1)

242-263: LGTM!

The SWR method correctly returns the cached entry's hit status for both fresh and stale cache hits.

✨ Finishing Touches
  • 📝 Generate Docstrings

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.
    • Explain this complex logic.
    • 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. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • 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 src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

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

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai 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

Documentation and Community

  • 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.

@github-actions github-actions bot added Bug Something isn't working Core Team labels Jul 21, 2025
@github-actions
Copy link
Contributor

github-actions bot commented Jul 21, 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: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bd4f010 and e52df0b.

📒 Files selected for processing (10)
  • go/apps/api/routes/v2_apis_delete_api/cache_validation_test.go (2 hunks)
  • go/apps/api/routes/v2_ratelimit_limit/handler.go (2 hunks)
  • go/internal/services/keys/get.go (3 hunks)
  • go/pkg/cache/cache.go (3 hunks)
  • go/pkg/cache/cache_test.go (4 hunks)
  • go/pkg/cache/interface.go (1 hunks)
  • go/pkg/cache/middleware/tracing.go (1 hunks)
  • go/pkg/cache/noop.go (1 hunks)
  • go/pkg/cache/simulation_test.go (1 hunks)
  • go/pkg/cache/swr_test.go (1 hunks)
🧰 Additional context used
🧠 Learnings (3)
go/internal/services/keys/get.go (2)

Learnt from: chronark
PR: #3560
File: go/deploy/metald/internal/database/repository.go:0-0
Timestamp: 2025-07-15T14:59:30.212Z
Learning: go/deploy/metald cannot currently import helpers from go/pkg/db because it is not yet part of the main Go module; avoid suggesting such imports until the modules are unified.

Learnt from: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.464Z
Learning: For debugging database replica usage in go/pkg/db/replica.go, it's acceptable to mark QueryRowContext operations as "success" even though SQL errors only surface during row.Scan() calls. The timing metrics are the primary concern for debugging replica performance patterns.

go/pkg/cache/swr_test.go (1)

Learnt from: Flo4604
PR: #3606
File: go/pkg/db/replica.go:8-11
Timestamp: 2025-07-16T15:38:53.464Z
Learning: For debugging database replica usage in go/pkg/db/replica.go, it's acceptable to mark QueryRowContext operations as "success" even though SQL errors only surface during row.Scan() calls. The timing metrics are the primary concern for debugging replica performance patterns.

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

Learnt from: chronark
PR: #2544
File: apps/api/src/pkg/env.ts:4-6
Timestamp: 2024-10-23T12:05:31.121Z
Learning: The cloudflareRatelimiter type definition in apps/api/src/pkg/env.ts should not have its interface changed; it should keep the limit method returning Promise<{ success: boolean }> without additional error properties.

Learnt from: ogzhanolguncu
PR: #2872
File: apps/dashboard/lib/trpc/routers/ratelimit/createNamespace.ts:36-39
Timestamp: 2025-04-08T09:34:24.576Z
Learning: When querying or updating namespaces in the Unkey dashboard, always scope the operations to the current workspace using eq(table.workspaceId, ctx.workspace.id) to prevent cross-workspace access.

⏰ 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). (7)
  • GitHub Check: Test Agent Local / test_agent_local
  • GitHub Check: Build / Build
  • GitHub Check: Test API / API Test Local
  • GitHub Check: Test Go API Local / Test
  • GitHub Check: Test Packages / Test
  • GitHub Check: autofix
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (15)
go/pkg/cache/simulation_test.go (1)

109-109: LGTM! Clean type inference usage.

The removal of explicit type parameters from cache.New is a good simplification that leverages Go's type inference while maintaining type safety through the explicitly typed cache.Config[uint64, uint64].

go/apps/api/routes/v2_apis_delete_api/cache_validation_test.go (2)

49-53: Correctly updated to test new cache hit status.

The test properly captures and asserts the cache hit status from the updated SWR method. The assertion cache.Hit after the initial retrieval correctly verifies a cache hit.


74-75: Proper verification of cache invalidation.

The test correctly verifies that after API deletion, the cache returns cache.Null, confirming proper cache invalidation behavior.

go/apps/api/routes/v2_ratelimit_limit/handler.go (1)

73-73: Correctly updated to capture cache hit status.

The SWR call is properly updated to capture the new cache hit status return value.

go/pkg/cache/cache_test.go (1)

18-18: LGTM! Consistent type inference usage.

All cache.New calls have been consistently updated to remove explicit type parameters, leveraging Go's type inference while maintaining type safety through the explicitly typed configuration structs.

Also applies to: 36-36, 59-59, 83-83

go/pkg/cache/interface.go (1)

21-21: Core interface fix for cache hit status reporting.

This interface change is the fundamental fix that addresses the PR objective. By adding the hit CacheHit return value, the SWR method now properly exposes the cache entry status, enabling callers to distinguish between cache hits, misses, and explicit nulls. This resolves the issue where cache misses were returning null values without proper detection.

go/internal/services/keys/get.go (3)

11-11: Import addition looks correct.

The cache package import is needed to access the CacheHit type for the updated SWR method signature.


64-66: Proper handling of the updated SWR method signature.

The method now correctly captures the additional hit parameter from the updated SWR signature, maintaining the existing error handling logic.


83-89: Excellent fix for the core issue described in the PR.

This explicit handling of cache.Null status directly addresses the bug where cache misses were not properly detected. When a key doesn't exist and was previously cached as null, the system now correctly returns StatusNotFound instead of silently returning a null value.

go/pkg/cache/noop.go (1)

25-28: Correct implementation of the updated SWR signature for noop cache.

The method signature properly includes the additional CacheHit return value, and returning Miss is the correct behavior for a no-operation cache that never actually stores data.

go/pkg/cache/middleware/tracing.go (1)

87-102: Proper propagation of cache hit status through tracing middleware.

The middleware correctly updates the SWR signature to include the CacheHit return value and properly propagates it from the underlying cache implementation. The tracing logic remains appropriately unchanged.

go/pkg/cache/cache.go (3)

242-242: Updated method signature correctly includes cache hit status.

The SWR method now properly returns CacheHit status alongside the cached value and error, enabling callers to distinguish between different cache states.


250-250: Proper propagation of cached entry hit status.

For both fresh and stale cache hits, the method correctly returns e.Hit from the cached entry, preserving the original cache hit status that was stored with the data.

Also applies to: 263-263


284-301: Well-implemented cache hit status determination for cache misses.

The logic correctly handles different scenarios:

  • Returns Miss status when errors occur during refresh
  • Determines hit status based on the operation: WriteValueHit, WriteNullNull, default → Miss
  • This ensures that null cache entries (when keys don't exist) are properly identified with Null status

This directly addresses the core issue described in the PR objectives.

go/pkg/cache/swr_test.go (1)

17-157: Comprehensive test suite excellently validates the cache hit status functionality.

This test suite thoroughly covers all important SWR cache scenarios:

  • Cache miss to hit transition: Tests initial miss that properly caches as Hit status
  • Fresh cache hits: Validates that fresh entries return cached data with correct hit status
  • Null cache hits: Critical test for the PR's main fix - ensures null entries (non-existent keys) return Null status
  • Stale behavior: Confirms stale entries return cached data while triggering background refresh
  • Expiration handling: Tests cache miss behavior after stale period
  • Error scenarios: Validates that errors during refresh return Miss status

The use of mock clock for time-based testing is excellent practice, and the assertions properly validate the three-tuple return (value, hit status, error).

This test suite provides strong confidence that the cache hit status feature works correctly and addresses the original bug described in the PR objectives.

@graphite-app
Copy link

graphite-app bot commented Jul 22, 2025

TV gif. We look up at Rowan Atkinson as Mr. Bean wearing medical scrubs. He pulls down a surgical mask, gives a gloved thumbs up, and smiles maniacally. (Added via Giphy)

@graphite-app
Copy link

graphite-app bot commented Jul 22, 2025

Merge activity

  • Jul 22, 7:54 AM UTC: This pull request can not be added to the Graphite merge queue. Please try rebasing and resubmitting to merge when ready.
  • Jul 22, 7:54 AM UTC: Graphite disabled "merge when ready" on this PR due to: a merge conflict with the target branch; resolve the conflict and try again..

@graphite-app
Copy link

graphite-app bot commented Jul 22, 2025

Graphite Automations

"Post a GIF when PR approved" took an action on this PR • (07/22/25)

1 gif was posted to this PR based on Andreas Thomas's automation.

@Flo4604
Copy link
Member Author

Flo4604 commented Jul 22, 2025

Ah ill fix merge conflicts

@Flo4604 Flo4604 enabled auto-merge July 22, 2025 07:58
@Flo4604 Flo4604 added this pull request to the merge queue Jul 22, 2025
Merged via the queue into main with commit 8891bc4 Jul 22, 2025
17 of 18 checks passed
@Flo4604 Flo4604 deleted the eng-1942-a-deleted-root-key-should-return-the-correct-error-in-v2 branch July 22, 2025 08:52
@coderabbitai coderabbitai bot mentioned this pull request Aug 11, 2025
18 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bug Something isn't working Core Team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A deleted root key should return the correct error in v2

2 participants