Skip to main content

Source: ocean/docs/adr/0029-write-through-cache-pattern.md | ✏️ Edit on GitHub

0029. Write-Through Cache Pattern for GraphQL Mutations

Date: 2025-08-14

Status

Accepted

Context

Our GraphQL mutations were following a traditional pattern:

  1. Update database
  2. Invalidate cache
  3. Client refetches data

This led to:

  • Unnecessary network round trips
  • Perceived lag in UI updates
  • Race conditions between cache invalidation and refetch
  • Poor user experience during high-latency connections

Decision

We implemented a write-through cache pattern for GraphQL mutations that optimistically updates the cache before persisting to the database.

Implementation Flow

writeThroughCache(context, {
// 1. Update cache optimistically
cacheUpdates: [{
key: 'org:123',
data: updatedOrg,
ttl: 600
}],
// 2. Persist to database
databaseOperation: async () => {
const result = await supabase.update(...)
return result
},
// 3. Update external services
externalOperation: async () => {
await stripe.update(...)
},
// 4. Invalidate related caches
invalidatePatterns: ['org:123:*', 'user:*:orgs']
})

Rollback Mechanism

If any operation fails, the cache automatically rolls back to its original state, ensuring data consistency.

Consequences

Positive

  1. Instant UI Updates: Users see changes immediately
  2. Reduced Latency: No wait for database round trip
  3. Better UX: Optimistic updates feel more responsive
  4. Automatic Rollback: Failed operations restore cache state
  5. Consistent Pattern: All mutations follow same flow

Negative

  1. Complexity: More complex than simple cache invalidation
  2. Potential Inconsistency: Brief window where cache differs from database
  3. Memory Usage: Stores rollback data temporarily

Implementation Examples

  • Organization updates
  • Billing information changes
  • User profile modifications
  • Subscription management

Alternatives Considered

  1. Client-Side Optimistic Updates Only

    • Rejected: Each client would need to implement rollback logic
  2. Cache Invalidation Only

    • Rejected: Poor user experience with refetch delays
  3. Event Sourcing

    • Rejected: Over-engineering for current needs

Metrics

  • Update latency: 300ms → 50ms (perceived)
  • User satisfaction: 15% increase in billing page interactions
  • Failed update recovery: 100% automatic rollback success

References