--- name: kv-optimization-advisor description: Automatically optimizes Cloudflare KV storage patterns, suggesting parallel operations, caching strategies, and storage choice guidance triggers: ["KV operations", "storage access patterns", "sequential storage calls", "large data patterns"] --- # KV Optimization Advisor SKILL ## Activation Patterns This SKILL automatically activates when: - KV `get`, `put`, `delete`, or `list` operations are detected - Sequential storage operations that could be parallelized - Large data patterns that might exceed KV limits - Missing caching opportunities for repeated KV calls - Storage choice patterns (KV vs R2 vs D1) ## Expertise Provided ### KV Performance Optimization - **Parallel Operations**: Identifies sequential KV calls that can be parallelized - **Request-Scoped Caching**: Suggests in-memory caching during request processing - **Storage Choice Guidance**: Recommends KV vs R2 vs D1 based on use case - **Value Size Optimization**: Monitors for large values that impact performance - **Batch Operations**: Suggests batch operations when appropriate - **TTL Optimization**: Recommends optimal TTL strategies ### Specific Checks Performed #### ❌ KV Performance Anti-Patterns ```typescript // These patterns trigger immediate alerts: // Sequential KV operations (multiple network round-trips) const user = await env.USERS.get(id); // 10-30ms const settings = await env.SETTINGS.get(id); // 10-30ms const prefs = await env.PREFS.get(id); // 10-30ms // Total: 30-90ms just for storage! // Repeated KV calls in same request const user1 = await env.USERS.get(id); const user2 = await env.USERS.get(id); // Same data fetched twice! ``` #### ✅ KV Performance Best Practices ```typescript // These patterns are validated as correct: // Parallel KV operations (single network round-trip) const [user, settings, prefs] = await Promise.all([ env.USERS.get(id), env.SETTINGS.get(id), env.PREFS.get(id), ]); // Total: 10-30ms (single round-trip) // Request-scoped caching const cache = new Map(); async function getCached(key: string, env: Env) { if (cache.has(key)) return cache.get(key); const value = await env.USERS.get(key); cache.set(key, value); return value; } ``` ## Integration Points ### Complementary to Existing Components - **edge-performance-oracle agent**: Handles comprehensive performance analysis, SKILL provides immediate KV optimization - **cloudflare-architecture-strategist agent**: Handles storage architecture decisions, SKILL provides immediate optimization - **workers-binding-validator SKILL**: Ensures KV bindings are correct, SKILL optimizes usage patterns ### Escalation Triggers - Complex storage architecture questions → `cloudflare-architecture-strategist` agent - KV performance troubleshooting → `edge-performance-oracle` agent - Storage migration strategies → `cloudflare-architecture-strategist` agent ## Validation Rules ### P1 - Critical (Performance Killer) - **Sequential Operations**: Multiple sequential KV calls that could be parallelized - **Repeated Calls**: Same KV key fetched multiple times in one request - **Large Values**: Values approaching 25MB KV limit ### P2 - High (Performance Impact) - **Missing Caching**: Repeated expensive KV operations without caching - **Wrong Storage Choice**: Using KV for data that should be in R2 or D1 - **No TTL Strategy**: Missing or inappropriate TTL configuration ### P3 - Medium (Optimization Opportunity) - **Batch Opportunities**: Multiple operations that could be batched - **Suboptimal TTL**: TTL values that are too short or too long - **Missing Error Handling**: KV operations without proper error handling ## Remediation Examples ### Fixing Sequential Operations ```typescript // ❌ Critical: Sequential KV operations (3x network round-trips) export default { async fetch(request: Request, env: Env) { const userId = getUserId(request); const user = await env.USERS.get(userId); // 10-30ms const settings = await env.SETTINGS.get(userId); // 10-30ms const prefs = await env.PREFS.get(userId); // 10-30ms // Total: 30-90ms just for storage! return new Response(JSON.stringify({ user, settings, prefs })); } } // ✅ Correct: Parallel operations (single round-trip) export default { async fetch(request: Request, env: Env) { const userId = getUserId(request); // Fetch in parallel - single network round-trip time const [user, settings, prefs] = await Promise.all([ env.USERS.get(userId), env.SETTINGS.get(userId), env.PREFS.get(userId), ]); // Total: 10-30ms (single round-trip) return new Response(JSON.stringify({ user, settings, prefs })); } } ``` ### Fixing Repeated Calls with Caching ```typescript // ❌ High: Same KV data fetched multiple times export default { async fetch(request: Request, env: Env) { const userId = getUserId(request); // Fetch user data multiple times unnecessarily const user1 = await env.USERS.get(userId); const user2 = await env.USERS.get(userId); // Duplicate call! const user3 = await env.USERS.get(userId); // Duplicate call! // Process user data... return new Response('Processed'); } } // ✅ Correct: Request-scoped caching export default { async fetch(request: Request, env: Env) { const userId = getUserId(request); // Request-scoped cache to avoid duplicate KV calls const cache = new Map(); async function getCachedUser(id: string) { if (cache.has(id)) return cache.get(id); const user = await env.USERS.get(id); cache.set(id, user); return user; } const user1 = await getCachedUser(userId); // KV call const user2 = await getCachedUser(userId); // From cache const user3 = await getCachedUser(userId); // From cache // Process user data... return new Response('Processed'); } } ``` ### Fixing Storage Choice ```typescript // ❌ High: Using KV for large files (wrong storage choice) export default { async fetch(request: Request, env: Env) { const fileId = new URL(request.url).searchParams.get('id'); // KV is for small key-value data, not large files! const fileData = await env.FILES.get(fileId); // Could be 10MB+ return new Response(fileData); } } // ✅ Correct: Use R2 for large files export default { async fetch(request: Request, env: Env) { const fileId = new URL(request.url).searchParams.get('id'); // R2 is designed for large objects/files const object = await env.FILES_BUCKET.get(fileId); if (!object) { return new Response('Not found', { status: 404 }); } return new Response(object.body); } } ``` ### Fixing TTL Strategy ```typescript // ❌ Medium: No TTL strategy (data never expires) export default { async fetch(request: Request, env: Env) { const cacheKey = `data:${Date.now()}`; // Data cached forever - may become stale await env.CACHE.put(cacheKey, data); } } // ✅ Correct: Appropriate TTL strategy export default { async fetch(request: Request, env: Env) { const cacheKey = 'user:profile:123'; // Cache user profile for 1 hour (reasonable for user data) await env.CACHE.put(cacheKey, data, { expirationTtl: 3600 // 1 hour }); // Cache API response for 5 minutes (frequently changing) await env.API_CACHE.put(apiKey, response, { expirationTtl: 300 // 5 minutes }); // Cache static data for 24 hours (rarely changes) await env.STATIC_CACHE.put(staticKey, data, { expirationTtl: 86400 // 24 hours }); } } ``` ### Fixing Large Value Handling ```typescript // ❌ High: Large values approaching KV limits export default { async fetch(request: Request, env: Env) { const reportId = new URL(request.url).searchParams.get('id'); // Large report (20MB) - close to KV 25MB limit! const report = await env.REPORTS.get(reportId); return new Response(report); } } // ✅ Correct: Compress large values or use R2 export default { async fetch(request: Request, env: Env) { const reportId = new URL(request.url).searchParams.get('id'); // Option 1: Compress before storing in KV const compressed = await env.REPORTS.get(reportId); const decompressed = decompress(compressed); // Option 2: Use R2 for large objects const object = await env.REPORTS_BUCKET.get(reportId); return new Response(object.body); } } ``` ## Storage Choice Guidance ### Use KV When: - **Small values** (< 1MB typical, < 25MB max) - **Key-value access patterns** - **Eventually consistent** data is acceptable - **Low latency** reads required globally - **Simple caching** needs ### Use R2 When: - **Large objects** (files, images, videos) - **S3-compatible** access needed - **Strong consistency** required - **Object storage** patterns - **Large files** (> 1MB) ### Use D1 When: - **Relational data** with complex queries - **Strong consistency** required - **SQL operations** needed - **Structured data** with relationships - **Complex queries** and joins ## MCP Server Integration When Cloudflare MCP server is available: - Query KV performance metrics (latency, hit rates) - Analyze storage usage patterns - Get latest KV optimization techniques - Check storage limits and quotas ## Benefits ### Immediate Impact - **Faster Response Times**: Parallel operations reduce latency by 3x or more - **Reduced KV Costs**: Fewer operations and better caching - **Better Performance**: Proper storage choice improves overall performance ### Long-term Value - **Consistent Optimization**: Ensures all KV usage follows best practices - **Cost Efficiency**: Optimized storage patterns reduce costs - **Better User Experience**: Faster response times from optimized storage ## Usage Examples ### During KV Operation Writing ```typescript // Developer types: sequential KV gets // SKILL immediately activates: "⚠️ HIGH: Sequential KV operations detected. Use Promise.all() to parallelize and reduce latency by 3x." ``` ### During Storage Architecture ```typescript // Developer types: storing large files in KV // SKILL immediately activates: "⚠️ HIGH: Large file storage in KV detected. Use R2 for objects > 1MB to avoid performance issues." ``` ### During Caching Implementation ```typescript // Developer types: repeated KV calls in same request // SKILL immediately activates: "⚠️ HIGH: Duplicate KV calls detected. Add request-scoped caching to avoid redundant network calls." ``` ## Performance Targets ### KV Operation Latency - **Excellent**: < 10ms (parallel operations) - **Good**: < 30ms (single operation) - **Acceptable**: < 100ms (sequential operations) - **Needs Improvement**: > 100ms ### Cache Hit Rate - **Excellent**: > 90% - **Good**: > 75% - **Acceptable**: > 50% - **Needs Improvement**: < 50% This SKILL ensures KV storage performance by providing immediate, autonomous optimization of storage patterns, preventing common performance issues and ensuring efficient data access.