Skip to content

Conversation

@joelhooks
Copy link
Contributor

@joelhooks joelhooks commented Feb 7, 2026

Sanity calls are a mostly-dead override layer but we’re still paying the roundtrip on every loadLesson().

Observed (since deploy):

  • lesson.loadLessonMetadataFromSanity.groq is ~100ms avg
  • Only ~4% of lesson loads actually have Sanity data

This change caches both hits and misses in Vercel KV:

  • Hit: cache merged Sanity metadata for 1h
  • Miss: cache a negative marker for 24h

That should collapse the Sanity call volume quickly without needing a brittle allowlist.

Added structured logs:

  • KV get/set failures: lesson.loadLessonMetadataFromSanity.kv_get_error / ...kv_set_error
  • Sanity fetch timing stays visible via lesson.loadLessonMetadataFromSanity.groq

Validation:

  • pnpm exec tsc -p tsconfig.json --noEmit
  • pnpm test:ci

Refs: #1560

Summary by CodeRabbit

  • Performance
    • Lesson metadata is now cached to improve load times and reduce server requests.
    • Enhanced error handling for lesson data retrieval operations.

@vercel
Copy link

vercel bot commented Feb 7, 2026

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

Project Deployment Actions Updated (UTC)
egghead-io-nextjs Ready Ready Preview, Comment Feb 7, 2026 11:15pm

Request Review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 7, 2026

📝 Walkthrough

Walkthrough

The changes introduce a KV-based caching layer for Sanity lesson metadata using SHA-1 hashed cache keys. The implementation checks the cache before querying Sanity, stores results with differentiated TTLs (1 hour for hits, 24 hours for misses), and includes error handling for cache operations.

Changes

Cohort / File(s) Summary
KV Cache Integration for Lesson Metadata
src/lib/lessons.ts
Adds caching layer with SHA-1 hashed cache keys, cache read/write logic with conditional TTLs, error handling for KV operations, and internal types for cache value representation. Merged lesson metadata is now cached and retrieved from KV before querying Sanity.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Cache as KV Cache
    participant Sanity as Sanity API
    
    Client->>Cache: Check cache (SHA-1 key)
    alt Cache Hit
        Cache-->>Client: Return cached lesson metadata
    else Cache Miss or Error
        Cache-->>Client: No cached value
        Client->>Sanity: Fetch lesson metadata
        Sanity-->>Client: Return metadata
        Client->>Client: Merge base + derived values
        Client->>Cache: Store result (TTL: 1h for hit, 24h for miss)
        Cache-->>Client: Cache stored
        Client-->>Client: Return merged metadata
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A cache so swift, a rabbit's delight,
SHA-1 hashes gleaming bright,
Sanity queries now skip with grace,
Stored in KV's speedy place,
One hour fresh, then a day's repose,
Where lesson data swiftly flows! 🏃‍♂️✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: introducing KV-based caching for Sanity lesson overrides with negative cache support.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch perf/sanity-lesson-cache

No actionable comments were generated in the recent review. 🎉

🧹 Recent nitpick comments
src/lib/lessons.ts (3)

74-85: Consider adding a version segment to the cache key.

If lessonQuery or deriveDataFromBaseValues changes in a future deploy, cached entries (up to 24 h for misses) will serve stale data shaped by the old schema. A low-cost mitigation is embedding a version constant in the key prefix (e.g. sanity:lesson:v1:<hash>), which you bump on schema-breaking changes.

This isn't urgent since the hit TTL is only 1 hour, but the 24-hour miss TTL means a lesson that gains Sanity overrides won't surface them until the negative marker expires.

Example
-const SANITY_LESSON_CACHE_PREFIX = 'sanity:lesson'
+const SANITY_LESSON_CACHE_VERSION = 'v1'
+const SANITY_LESSON_CACHE_PREFIX = `sanity:lesson:${SANITY_LESSON_CACHE_VERSION}`

127-128: Narrow the cast from as any to preserve some type safety.

as any silences all type checking. A slightly more targeted cast would still work and keep the intent visible:

Suggested change
-    const hasSanity = !isEmpty((merged as any)?.slug)
+    const hasSanity = !isEmpty((merged as Record<string, unknown>)?.slug)

136-151: await kv.set() adds write latency to every cache-miss response.

Since this PR is about reducing latency, note that the await on line 137 blocks the return (line 151) until the KV write completes. For a write-behind cache, you could fire the set without awaiting so the caller gets the response immediately:

Fire-and-forget KV write
     try {
-      await kv.set(cacheKey, valueToCache, {
+      // fire-and-forget: don't block the response on the cache write
+      kv.set(cacheKey, valueToCache, {
         ex: hasSanity
           ? SANITY_LESSON_CACHE_TTL_SECONDS
           : SANITY_LESSON_CACHE_MISS_TTL_SECONDS,
-      })
+      }).catch(() => {
+        logEvent(
+          'warn',
+          'lesson.loadLessonMetadataFromSanity.kv_set_error',
+          {slug, has_sanity: hasSanity},
+          logContext,
+        )
+      })
     } catch {
       logEvent(
         'warn',
         'lesson.loadLessonMetadataFromSanity.kv_set_error',
         {slug, has_sanity: hasSanity},
         logContext,
       )
     }

With the .catch() handler, the outer try/catch for kv.set becomes redundant and can be removed.


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.

@joelhooks joelhooks merged commit 3baccdb into main Feb 7, 2026
6 checks passed
@joelhooks joelhooks deleted the perf/sanity-lesson-cache branch February 7, 2026 23:17
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