Skip to main content

Redis Caching Implementation Plan

Status: Ready for Implementation
Expected Impact: 10-30x performance improvement (22 β†’ 600 documents/min)
Estimated Effort: 2-3 days
Monthly Cost: ~$50 for Redis hosting

πŸ“‹ Overview

This document outlines where to add Redis caching and cache invalidation across CONA to achieve the performance goals outlined in the Stage 1 optimization plan. Two Layers of Caching:
  1. Core Package Functions (CRITICAL for Temporal workers) - packages/core/src/services/
  2. Webapp Server Actions (For UI/API) - apps/webapp/app/lib/actions/
All affected files have been marked with inline TODO comments showing exactly where to add caching logic. ⚠️ IMPORTANT: Most queries come from Temporal workers, so caching in the core package has the biggest impact!

🎯 Core Package Caching (FOR TEMPORAL WORKERS)

Priority 0: MOST CRITICAL (For Workflow Performance)

These are called by Temporal workflows/activities and have the BIGGEST impact:
  1. fetchPostingMatrixRules() - Core Package
    • File: packages/core/src/services/fetch-posting-matrix-rules.ts
    • Function: fetchPostingMatrixRules()
    • Cache Key: posting_matrix_rules:${organizationId}:${dimensionLabel}:${triggerEventSlug}
    • TTL: 3600 seconds (1 hour)
    • Impact: Called 600+ times per 100 documents (100ms β†’ 5ms = 20x faster)
    • Savings: 57 seconds per 100 documents
    • Status: βœ… TODO comments added (lines 54-94 & 191-206)
  2. fetchGlDimensions() - Core Package
    • File: packages/core/src/services/fetch-gl-dimensions.ts
    • Function: fetchGlDimensions()
    • Cache Key: gl_dimensions_list:${organizationId}
    • TTL: 3600 seconds (1 hour)
    • Impact: Called once per document (30ms β†’ 3ms = 10x faster)
    • Savings: 2.7 seconds per 100 documents
    • Status: βœ… TODO comments added (lines 8-38)
See TEMPORAL_WORKER_CACHING_GUIDE.md for detailed worker-specific implementation guide.

🎯 Implementation Priority

Priority 1: CRITICAL PATH (πŸ”΄ Red Priority)

Impact: 100ms β†’ 5ms per call (20x faster) These are called for EVERY document during accounting impact calculation:
  1. GL Dimensions with Posting Matrix
    • File: apps/webapp/app/lib/actions/gl_dimensions/get-gl-dimensions.ts
    • Function: getGlDimensions()
    • Cache Key: gl_dimensions:${organizationId}:${triggerEventId}:${includePostingMatrix}:${includeDeleted}
    • TTL: 3600 seconds (1 hour)
  2. Individual Posting Matrix Lookup
    • File: apps/webapp/app/lib/actions/posting_matrix/get-posting-matrix.ts
    • Function: getMatrix(id)
    • Cache Key: posting_matrix:${id}:${organizationId}
    • TTL: 3600 seconds (1 hour)
  3. Chart of Accounts List
    • File: apps/webapp/app/lib/actions/chart_of_accounts/list-chart-of-accounts.ts
    • Function: listChartOfAccounts()
    • Cache Key: chart_of_accounts:list:${organizationId}:${onlyReconcileAccounts}
    • TTL: 1800 seconds (30 minutes)
  4. Chart of Accounts Search
    • File: apps/webapp/app/lib/actions/chart_of_accounts/search-chart-of-accounts.ts
    • Function: searchChartOfAccounts()
    • Cache Key: chart_of_accounts:search:${organizationId}
    • TTL: 1800 seconds (30 minutes)

Priority 2: HIGH FREQUENCY (🟑 Yellow Priority)

Impact: 20-50ms β†’ 2-5ms per call These are called frequently but not on every document:
  1. Organization Details
    • File: apps/webapp/app/lib/actions/organization/get-organisation-details.ts
    • Function: getOrganizationDetails()
    • Cache Key: organization:details:${organizationId}
    • TTL: 3600 seconds (1 hour)
  2. Tool/Integration Settings
    • File: apps/webapp/app/lib/actions/tools/get-tool-settings.ts
    • Function: getToolSettings(toolSlug)
    • Cache Key: tool_settings:${organizationId}:${toolSlug}
    • TTL: 1800 seconds (30 minutes)
  3. Chart of Account by ID
    • File: apps/webapp/app/lib/actions/chart_of_accounts/get-chart-of-account-by-id.ts
    • Function: getChartOfAccountById()
    • Cache Key: chart_of_accounts:id:${organizationId}:${id}
    • TTL: 1800 seconds (30 minutes)
  4. Chart of Account by Number
    • File: apps/webapp/app/lib/actions/chart_of_accounts/get-chart-of-account-id-by-number.ts
    • Function: getAccountIdByNumber()
    • Cache Key: chart_of_accounts:number:${organizationId}:${accountNumber}
    • TTL: 1800 seconds (30 minutes)
  5. Posting Matrices Grouped by Trigger Event
    • File: apps/webapp/app/lib/actions/posting_matrix/get-posting-matrix-grouped-by-trigger-event.ts
    • Function: getPostingMatricesGroupedByTriggerEvent()
    • Cache Key: posting_matrices_grouped:${organizationId}
    • TTL: 3600 seconds (1 hour)

πŸ”„ Cache Invalidation Map

When Posting Matrix Changes

Affected Actions:
  • save-posting-matrix.ts (create/update)
  • delete-posting-matrix.ts (delete)
  • create-posting-matrix.ts (create)
Invalidate These Cache Patterns:
Files to Modify:
  • βœ… apps/webapp/app/lib/actions/posting_matrix/save-posting-matrix.ts (line 142)
  • βœ… apps/webapp/app/lib/actions/posting_matrix/delete-posting-matrix.ts (line 65)
  • βœ… apps/webapp/app/lib/actions/posting_matrix/create-posting-matrix.ts (line 29)
IMPORTANT: These mutations must invalidate BOTH webapp AND worker cache keys!

When Chart of Accounts Changes

Affected Actions:
  • create-chart-of-account.ts (create)
  • update-chart-of-account.ts (update)
  • Any chart deletion actions
Invalidate These Cache Patterns:
Files to Modify:
  • βœ… apps/webapp/app/lib/actions/chart_of_accounts/create-chart-of-account.ts (line 48)
  • βœ… apps/webapp/app/lib/actions/chart_of_accounts/update-chart-of-account.ts (line 75)
  • βœ… apps/webapp/app/lib/actions/chart_of_accounts/delete-chart-of-account.ts (line 31)

When Organization Settings Change

Affected Actions:
  • update-organisation-onboarding-complete.ts
  • Any organization update actions (name, currency, timezone, etc.)
Invalidate This Cache Key:
Files to Modify:
  • βœ… apps/webapp/app/lib/actions/organization/update-organisation-onboarding-complete.ts (line 31)
  • πŸ”² Search for other organization update actions

When Tool Settings Change

Affected Actions:
  • update-tool-settings.ts
Invalidate This Cache Key:
Files to Modify:
  • βœ… apps/webapp/app/lib/actions/tools/update-tool-settings.ts (line 40)

πŸ—οΈ Implementation Pattern

Basic Caching Pattern

Cache Invalidation Pattern


πŸ› οΈ Setup Requirements

1. Get Redis Credentials

If using Supabase Redis/Upstash Integration:
  1. Go to your Supabase Dashboard β†’ Project Settings β†’ Integrations
  2. Find Redis/Upstash integration
  3. Copy connection details:
    • Redis Host (e.g., your-project.upstash.io)
    • Redis Port (usually 6379)
    • Redis Password
Connection URL format:
For SSL/TLS (Upstash uses this):

2. Create Redis Client

Create a Redis client utility:
Or use connection URL (simpler for Upstash):

3. Environment Variables

Add to .env: Option A: Individual credentials
Option B: Connection URL (recommended for Upstash)
For Temporal Workers: Add the same environment variables to your worker configuration (apps/temporal-workers/.env)

4. Create Redis Client for Core Package (Temporal Workers)

Since workers use core package functions, also create a Redis client there:

🚨 Important Considerations

1. Cache Invalidation is Critical

  • Always invalidate related caches when data changes
  • Missing invalidation = stale data = bugs
  • Use wildcard invalidation for safety when unsure

2. Error Handling

3. Cache Key Consistency

  • Always include organizationId in cache keys
  • Use consistent separator (: recommended)
  • Document cache key patterns

4. TTL Selection

  • Posting matrix rules: 1 hour (rarely change)
  • Chart of accounts: 30 minutes (moderate change)
  • Organization settings: 1 hour (rarely change)
  • Adjust based on real-world usage

πŸ“š Reference Files

All files with inline TODO comments:

Read Operations (Add Caching)

  1. apps/webapp/app/lib/actions/gl_dimensions/get-gl-dimensions.ts (line 64)
  2. apps/webapp/app/lib/actions/posting_matrix/get-posting-matrix.ts (line 30)
  3. apps/webapp/app/lib/actions/posting_matrix/get-posting-matrix-grouped-by-trigger-event.ts (line 31)
  4. apps/webapp/app/lib/actions/chart_of_accounts/list-chart-of-accounts.ts (line 18)
  5. apps/webapp/app/lib/actions/chart_of_accounts/search-chart-of-accounts.ts (line 14)
  6. apps/webapp/app/lib/actions/chart_of_accounts/get-chart-of-account-by-id.ts (line 18)
  7. apps/webapp/app/lib/actions/chart_of_accounts/get-chart-of-account-id-by-number.ts (line 18)
  8. apps/webapp/app/lib/actions/organization/get-organisation-details.ts (line 28)
  9. apps/webapp/app/lib/actions/tools/get-tool-settings.ts (line 16)

Write Operations (Add Cache Invalidation)

  1. apps/webapp/app/lib/actions/posting_matrix/save-posting-matrix.ts (line 142)
  2. apps/webapp/app/lib/actions/posting_matrix/delete-posting-matrix.ts (line 65)
  3. apps/webapp/app/lib/actions/posting_matrix/create-posting-matrix.ts (line 29)
  4. apps/webapp/app/lib/actions/chart_of_accounts/create-chart-of-account.ts (line 48)
  5. apps/webapp/app/lib/actions/chart_of_accounts/update-chart-of-account.ts (line 75)
  6. apps/webapp/app/lib/actions/chart_of_accounts/delete-chart-of-account.ts (line 31)
  7. apps/webapp/app/lib/actions/tools/update-tool-settings.ts (line 40)
  8. apps/webapp/app/lib/actions/organization/update-organisation-onboarding-complete.ts (line 31)

🎯 Success Criteria

Performance Metrics

  • βœ… Posting matrix fetch: 100ms β†’ 5ms
  • βœ… Chart of accounts fetch: 50ms β†’ 5ms
  • βœ… Overall processing: 400ms β†’ 150ms per document
  • βœ… Throughput: 22 β†’ 600 documents/min (27x improvement)

Reliability Metrics

  • βœ… Cache hit rate > 90% after warm-up
  • βœ… No stale data bugs in production
  • βœ… Database connection usage reduced by 80%+
  • βœ… Redis availability > 99.9%

πŸ“Œ Important: Redis FDW vs Direct Connection

Supabase Redis Wrapper (FDW) - Documentation
  • ❌ Not for this use case
  • Purpose: Query Redis data FROM Postgres using SQL
  • Use case: SELECT * FROM redis.my_data; in SQL queries
  • Read-only access
Direct Redis Connection (What we need)
  • βœ… This is what we’re implementing
  • Purpose: Application-level caching from Node.js/Next.js
  • Use case: await redis.get('cache_key') in TypeScript code
  • Read and write access
  • Bypasses Postgres entirely
However: If you have Supabase Redis integration, use those same credentials for direct connection!