Skip to content

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();

Performance Benefits

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