Phase 5: Caching Plan
Goal
Reduce Prisma operations, SQL statements, and user-visible latency by caching safe repeated reads.
Prisma Accelerate provides global query caching for reads using TTL and stale-while-revalidate patterns. Prisma's pricing page includes Prisma Accelerate and cache invalidation details by plan. Sources: Prisma pricing, Prisma query optimization.
Cache candidates
| Data | Suggested TTL | Invalidation |
|---|---|---|
| Workspace shell metadata (non-auth only) | 5-15 min | Workspace display/settings update |
| Brand profile / brand voice | 5-30 min | Brand update |
| Social account list | 1-5 min | Connect/disconnect/sync |
| Calendar summary counts | 30-120 sec | Create/update/delete/schedule |
| Content list first page | 30-120 sec | Create/update/delete/archive/schedule |
| Campaign list | 1-5 min | Campaign create/update/archive |
| Static platform metadata | 1 hour+ | Deploy/config change |
| AI prompt templates | 5-30 min | Template edit |
Cache layers
Layer A: Prisma Accelerate query cache
Use for repeated read queries where short staleness is acceptable.
Good targets:
workspace shell metadata that excludes role, membership, owner, billing, OAuth, and permission decisions
brand settings
calendar summaries
content list summaries
platform metadata
campaign listsAvoid for:
auth-critical permission checks
OAuth token reads/writes
billing-critical state
publishing queue state
write-after-read consistency flowsLayer B: Request-scope cache
Use a per-request cache to prevent the same workspace/user context from being loaded multiple times inside one API action.
Example:
const requestCache = new Map<string, Promise<unknown>>();
export function oncePerRequest<T>(key: string, loader: () => Promise<T>): Promise<T> {
if (!requestCache.has(key)) {
requestCache.set(key, loader());
}
return requestCache.get(key) as Promise<T>;
}Layer C: Short in-process cache
Useful for Cloud Run warm instances, but not durable.
type CacheEntry<T> = {
value: T;
expiresAt: number;
};
const cache = new Map<string, CacheEntry<unknown>>();Use only for small, safe, non-critical values.
Layer D: Private HTTP cache
For authenticated read-heavy routes:
Cache-Control: private, max-age=30, stale-while-revalidate=60Use carefully because TendSocial data is workspace-private.
Layer E: Public edge cache
Use Vercel/Cloudflare edge caching only for public content:
marketing pages
docs pages
public landing data
public blog/SEO pages if anyDo not cache authenticated workspace API responses at the public edge.
Cache invalidation strategy
Create a single invalidation utility:
export async function invalidateWorkspaceCaches(workspaceId: string, keys: string[]) {
// Prisma Accelerate cache tags if used
// in-process cache delete
// optional Redis/KV delete later
}Example tags:
workspace:{workspaceId}:shell
workspace:{workspaceId}:brands
workspace:{workspaceId}:social-accounts
workspace:{workspaceId}:content-list
workspace:{workspaceId}:calendar-summary
workspace:{workspaceId}:campaignsMutation handlers must invalidate relevant tags:
| Mutation | Invalidate |
|---|---|
| Update workspace display/settings | workspace shell metadata |
| Update member/role/owner | invalidate affected non-auth shell caches; auth decisions stay uncached |
| Update brand voice | brand profile |
| Connect social account | social account list |
| Create/update/archive content | content list, calendar summary |
| Schedule/unschedule content | calendar summary, content list |
| Update campaign | campaign list/detail |
Measurement requirements
For each cached route/action, compare:
cold run
warm run
after invalidationReport:
| Field | Meaning |
|---|---|
| coldOrmOps | DB operations without cache |
| warmOrmOps | DB operations after cache |
| reductionPct | Operation reduction |
| staleRisk | Low/medium/high |
| invalidationPath | Mutation path that clears cache |
Acceptance criteria
| Item | Target |
|---|---|
| Workspace shell repeated load operation reduction | 50%+ |
| Dashboard repeated load operation reduction | 30%+ |
| Calendar repeated same-range load reduction | 30%+ |
| Brand profile repeated load reduction | 50%+ |
| Auth/permission stale bugs | 0 |
| Cache invalidation paths documented | Yes |
| Cached routes covered in operation tests | Yes |