Summary by TwinMind · 12m 48s
Summary by TwinMind · 12m 48s
How I cut our cache by 98.741% (real screenshot btw)
How I cut our cache by 98.741% (real screenshot btw)
Theo - t3․gg
Theo - t3․gg
Problem Overview
- Caching is one of 2-3 hard problems in computer science
- Upload Thing had caching issue causing billing problems (not user-facing)
- Issue: caching what we got vs. caching what we needed
- 4GB bandwidth usage for SL upload thing registry, 2.5GB for upload thing latest in 7-day window
Original Implementation
- Feature: warning alert for old Upload Thing SDK versions
- Shows current version, key info, prompts to update to latest
- Used npm Registry API to get version data
- Cached using Vercel fetch cache with next revalidation tags
- 24-hour cache expiration using revalidation seconds
- Two main functions:
listUploadThingVersions()- gets all versions from npm APIgetMinimumVersion()- filters to find lowest supported version
- Skipped canary and deprecated versions
- Problem: caching entire npm API response (over 1MB of text data)
Root Cause Analysis
- Fetch cache stores everything returned from API calls by default
- npm API response contains massive amounts of unnecessary data
- Each page request pulled full cached response from cache
- Thousands of users = thousands of cache reads of oversized data
- Made things slower, increased bandwidth usage, increased storage costs
- Bandwidth usage was orders of magnitude higher than needed
Solution: Unstable Cache Implementation
- Replaced fetch-level caching with
unstable_cache - Allows granular control over what gets cached
- Removed all next revalidation tags from fetch calls
- Created internal functions for API calls (not exposed)
- Wrapped with cached functions using
unstable_cache - Key change: cache function return values, not fetch responses
getLatestUTVersion()- caches onlynpmResponse.versiongetMinimumUploadThingVersion()- caches only version + reason- Custom cache keys and revalidate times still supported
Results and Performance Impact
- Bandwidth usage plummeted dramatically
- Cache reads went from ~1GB to 5MB in same timeframe
- 90% cache hit rate maintained
- Cache writes increased slightly due to reduced specificity
- Response times improved due to less data transfer
- Orders of magnitude reduction in bandwidth costs
- Vercel UI shows "undefined" for unstable cache (vs URL-based caching)
Caching Philosophy and Best Practices
- Don't cache everything automatically from fetch requests
- Cache with intent based on what you actually need
- Cache as close to final result as possible, not intermediate steps
- Think about cache input/output boundaries logically
- Consider where cache starts and ends in your data pipeline
- Fetch caching by default might not be the right approach
- Pipeline visualization: User Request → npm API → Filter → Render → Page Load
- Better to cache processed results than raw API responses
Future Considerations
unstable_cachewill eventually become stable with different API- Granular caching control will remain important
- Component-level caching possible with Next.js for even better performance
- Consider caching rendered components for maximum optimization
