Skip to main content
Each recipe includes the problem, solution code, and rationale.

When to cache

Problem: You need data from one resource type when processing another (e.g., resolving user IDs to emails when emitting grants). When caching helps: When NOT to cache:
  • Across sync runs (connector restarts clear caches anyway)
  • Large datasets that don’t fit in memory
  • Data that changes frequently during sync

Thread-safe caching with sync.Map

Problem: Cache data that’s populated in one method and read in another, possibly concurrently. Solution:
Why: sync.Map is safe for concurrent reads and writes. The SDK may call different builders concurrently.

Cross-resource lookups

Problem: When emitting grants, you have member IDs but need to determine if they’re users or groups. Solution:
Why: Many APIs return member IDs without type information. Caching during List() avoids expensive lookups during Grants().

Cache warming order

Problem: Grants() runs before the cache is populated. Solution: The SDK processes resource types in the order they’re registered. Register types that populate caches first:
Why: SDK processes syncers in order. If roles need user lookups, users must be synced first.

Anti-pattern: package-level caches

This is a critical anti-pattern. Package-level sync.Map variables persist state across sync runs in daemon mode, causing data corruption and phantom grants.
Real example found in production connectors:
What goes wrong:
  1. Sync 1 runs, populates cache with users A, B, C
  2. User B is deleted from the target system
  3. Sync 2 runs in daemon mode (same process)
  4. Cache still contains user B
  5. Grants referencing user B appear valid but point to deleted user
  6. Access reviews show phantom access that doesn’t exist
Correct pattern - struct-scoped cache:
How to verify your connector:
If you find any, refactor them to struct fields.

Cache lifetime in daemon mode

Problem: In daemon mode, the connector runs continuously processing multiple syncs. Caches need explicit lifetime management. Runtime modes: Solution: Clear or recreate caches at sync boundaries:
Cache lifetime expectations:

Memory-bounded caching

Problem: Caching all users exhausts memory in large organizations. Solution: For very large datasets, use LRU cache or skip caching entirely:
Alternative: For truly large datasets, accept the N+1 lookup cost or batch lookups:
Why: Memory is finite. A connector that OOMs is worse than one that makes extra API calls.