Caching Strategy
🎯 Caching Strategy Matrix
✅ EXCELLENT for Caching (Long TTL: Hours/Days)
// Farm structure - rarely changes, expensive to compute
const farmLayout = await cache.get('farm_layout', async () => {
return await supabase.from('farms').select(`
*, rows(*, racks(*, shelves(*)))
`);
}, { ttl: '24h' });
// Plant growth parameters - static reference data
const plantConfigs = await cache.get('plant_configs', async () => {
return await supabase.from('plant_varieties').select('*');
}, { ttl: '12h' });
🔄 GOOD for Caching (Medium TTL: Minutes + Realtime Invalidation)
// User preferences - cache but invalidate on changes
const userSettings = await cache.get(`user_settings_${userId}`, fetchUserSettings, { ttl: '30m' });
// Listen for changes and invalidate cache
supabase.channel('user_changes')
.on('postgres_changes', {
event: 'UPDATE',
schema: 'public',
table: 'user_preferences'
}, () => {
cache.invalidate(`user_settings_${userId}`);
});
❌ NEVER Cache (Use Realtime Only)
// Live sensor data - defeats the purpose of realtime
supabase.channel('sensor_readings')
.on('postgres_changes', {
event: 'INSERT',
schema: 'public',
table: 'sensor_data'
}, (payload) => {
updateDashboard(payload.new); // Real-time updates
});
// Equipment status - needs immediate updates
supabase.channel('equipment_status')
.on('postgres_changes', {
event: 'UPDATE',
schema: 'public',
table: 'equipment'
}, (payload) => {
updateEquipmentUI(payload.new); // Instant status changes
});
📊 Data Classification for Vertical Farm
🚀 Cache-Friendly Data (Static/Slow-Changing)
Data Type |
TTL |
Invalidation Strategy |
Farm layout (rows/racks/shelves) |
24h |
Manual on structure changes |
Plant varieties & growth params |
12h |
Manual on config updates |
User profiles & permissions |
6h |
Event-based on user changes |
Historical reports & analytics |
1h |
Time-based regeneration |
System configuration |
24h |
Manual on admin changes |
⚡ Real-Time Data (Never Cache)
Data Type |
Why Real-Time |
Update Frequency |
Sensor readings (temp, humidity, pH) |
Safety critical |
Every 30s-5min |
Equipment status (pumps, lights, fans) |
Operational control |
Immediate |
Alerts & alarms |
Safety & notifications |
Immediate |
User presence & activity |
Collaboration |
Real-time |
System health monitoring |
Operational |
Every 1-30s |
🔄 Hybrid Data (Cache + Realtime)
Data Type |
Cache TTL |
Realtime Trigger |
Recent sensor trends |
5min |
New aggregated data |
Equipment schedules |
Until next change |
Schedule updates |
User notifications |
Read status cached |
New notifications real-time |
Task queue status |
1min |
Task state changes |
🏗️ Implementation Patterns
Pattern 1: Cache-First with Realtime Invalidation
class FarmDataService {
async getFarmLayout(farmId: string) {
// Try cache first
const cached = await this.cache.get(`farm_layout_${farmId}`);
if (cached) return cached;
// Fetch and cache
const layout = await this.fetchFarmLayout(farmId);
await this.cache.set(`farm_layout_${farmId}`, layout, { ttl: '24h' });
return layout;
}
setupRealtimeInvalidation() {
supabase.channel('farm_structure_changes')
.on('postgres_changes', {
event: '*',
schema: 'public',
table: 'farms'
}, (payload) => {
this.cache.invalidate(`farm_layout_${payload.new.id}`);
});
}
}
Pattern 2: Hybrid Dashboard Loading
async function loadDashboard(farmId: string) {
// Fast initial load with cached data
const [farmLayout, userSettings, plantConfigs] = await Promise.all([
cache.get(`farm_layout_${farmId}`, () => fetchFarmLayout(farmId)),
cache.get(`user_settings_${userId}`, () => fetchUserSettings(userId)),
cache.get('plant_configs', () => fetchPlantConfigs())
]);
// Render initial UI immediately
renderDashboard({ farmLayout, userSettings, plantConfigs });
// Start realtime subscriptions for live data
subscribeToSensorData(farmId);
subscribeToEquipmentStatus(farmId);
subscribeToAlerts(farmId);
}
Pattern 3: Background Task Optimization
async function processAutomationTask(taskId: string) {
// Cache expensive configuration lookups
const [farmConfig, automationRules, plantParams] = await Promise.all([
cache.get(`farm_config_${farmId}`, () => fetchFarmConfig(farmId)),
cache.get(`automation_rules_${farmId}`, () => fetchAutomationRules(farmId)),
cache.get('plant_parameters', () => fetchPlantParameters())
]);
// Get live sensor data (never cached)
const currentSensorData = await fetchLiveSensorData(farmId);
// Process with cached config + live data
return processTask(farmConfig, automationRules, plantParams, currentSensorData);
}
⚠️ Anti-Patterns to Avoid
❌ Caching Real-Time Data
// DON'T DO THIS - defeats realtime purpose
const sensorData = await cache.get('current_sensors', () =>
fetchCurrentSensorReadings()
);
❌ Not Invalidating Cache on Changes
// DON'T DO THIS - stale data issues
await updateFarmStructure(farmId, newLayout);
// Missing: cache.invalidate(`farm_layout_${farmId}`);
❌ Over-Caching User-Specific Data
// DON'T DO THIS - privacy and staleness issues
const userActivity = await cache.get(`user_activity_${userId}`, () =>
fetchUserActivity(userId), { ttl: '1h' }
); // User activity should be real-time
🎯 Optimal Strategy for Vertical Farm
Dashboard Architecture
// 1. Fast initial load with cache
const staticData = await loadCachedData();
renderInitialUI(staticData);
// 2. Subscribe to real-time updates
const realtimeSubscription = supabase
.channel('farm_updates')
.on('postgres_changes', { table: 'sensor_data' }, updateSensorDisplay)
.on('postgres_changes', { table: 'equipment' }, updateEquipmentStatus)
.on('postgres_changes', { table: 'alerts' }, showNewAlert)
.subscribe();
// 3. Cache invalidation for structural changes
supabase.channel('structure_changes')
.on('postgres_changes', { table: 'farms' }, invalidateFarmCache)
.subscribe();
- Initial Load: 70% faster with cached farm structure
- Real-Time Updates: Immediate sensor data and alerts
- Background Tasks: 50% faster with cached configurations
- Mobile Experience: Offline capability with cached data
Data Consistency
- Critical Data: Always real-time (sensors, alerts, equipment)
- Reference Data: Cached with invalidation (structure, configs)
- User Data: Hybrid approach based on update frequency
This strategy gives you the best of both worlds: lightning-fast initial loads through caching, combined with immediate updates through Supabase realtime features.