Assist_Design/docs/CDC_EVENT_FLOW_EXPLAINED.md
barsa 1334c0f9a6 Enhance Salesforce integration and caching mechanisms
- Added new environment variables for Salesforce event channels and Change Data Capture (CDC) to improve cache invalidation and event handling.
- Updated Salesforce module to include new guards for write operations, enhancing request rate limiting.
- Refactored various services to utilize caching for improved performance and reduced API calls, including updates to the Orders and Catalog modules.
- Enhanced error handling and logging in Salesforce services to provide better insights during operations.
- Improved cache TTL configurations for better memory management and data freshness across catalog and order services.
2025-11-06 16:32:29 +09:00

10 KiB

CDC Event Flow: Customer-Specific vs Global Cache

🎯 The Key Misunderstanding

What CDC Events Actually Contain

// CDC Event from Salesforce
{
  "payload": {
    "Id": "01t5g000002AbcdEAC",              // Product ID
    "Name": "Internet Home 1G",              // Product Name
    "changeType": "UPDATE",
    "changedFields": ["Name", "UnitPrice"],
    "entityName": "Product2"
  },
  "replayId": 12345
}

Notice:

  • Contains: Product ID, what changed
  • Does NOT contain: Customer ID, User ID, Account ID
  • Does NOT specify: "For Customer A" or "For Customer B"

CDC events are GLOBAL notifications, not customer-specific!


🔄 Complete Flow: What Happens With CDC

Scenario: Price Change for "Internet Home 1G"

TIME: 10:00 AM
SALESFORCE: Admin changes price $50 → $60
     ↓
     ↓ (1 CDC Event sent)
     ↓
PORTAL CDC SUBSCRIBER receives event:
     {
       "Id": "01t123...",
       "changeType": "UPDATE",
       "changedFields": ["UnitPrice"]
     }
     ↓
CACHE INVALIDATION (Global):
     Redis: DELETE "catalog:internet:plans"
     ↓
     Cache key deleted from Redis
     No cache exists anymore for ANYONE

👥 What Happens to Different Customer Types?

Customer A: Online & Active (viewing website)

10:00:00 AM - Viewing catalog page
              ↓ Cache hit (old price $50)
              
10:00:05 AM - CDC event received
              ↓ Cache deleted
              
10:00:10 AM - Refreshes page
              ↓ Cache miss (key deleted)
              ↓ API call to Salesforce
              ↓ Fetches new data (price $60)
              ↓ Stores in cache with 24h TTL
              ↓ Shows new price $60 ✅

Action taken: Cache miss → API call → Fresh data


Customer B: Online & Idle (logged in but not viewing catalog)

10:00:00 AM - Logged in, viewing dashboard
              (Not looking at catalog)
              
10:00:05 AM - CDC event received
              ↓ Cache deleted (global)
              
10:30:00 AM - Clicks "View Plans" for first time
              ↓ Cache miss (was deleted at 10:00)
              ↓ API call to Salesforce
              ↓ Fetches new data (price $60)
              ↓ Stores in cache
              ↓ Shows new price $60 ✅

Action taken: Cache miss → API call → Fresh data


Customer C: Offline (not logged in for 7 days)

Day 1 - 9:00 AM - Customer C logs in
                  ↓ Cache miss
                  ↓ API call (fetches old price $50)
                  ↓ Cache populated
                  
Day 1 - 10:00 AM - CDC event (price changed to $60)
                   ↓ Cache deleted
                   ↓ Customer C logs out
                   
Day 2-7:          - Customer C offline
                   - Cache doesn't exist (deleted on Day 1)
                   - No action needed ✅
                   
Day 8 - 8:00 AM - Customer C logs back in
                  ↓ Clicks "View Plans"
                  ↓ Cache miss (doesn't exist)
                  ↓ API call to Salesforce
                  ↓ Fetches new data (price $60)
                  ↓ Shows new price $60 ✅

Action taken: Nothing during offline period. Fresh fetch on login.


🎯 Key Point: ONE Cache Key for ALL Customers

Your catalog cache structure:

// GLOBAL cache keys (shared by ALL customers)
"catalog:internet:plans"      // ← All customers use this
"catalog:sim:plans"           // ← All customers use this
"catalog:vpn:plans"           // ← All customers use this

// USER-SPECIFIC cache keys (per customer)
"catalog:eligibility:801xxx"  // ← Customer A's eligibility
"catalog:eligibility:802xxx"  // ← Customer B's eligibility

When Product2 CDC event arrives:

// Invalidates GLOBAL keys (affects everyone)
await cache.delPattern("catalog:internet:*");
await cache.delPattern("catalog:sim:*");
await cache.delPattern("catalog:vpn:*");

// Does NOT invalidate user-specific keys
// "catalog:eligibility:801xxx" stays intact

💡 Why This Works Perfectly

1. Offline Customers Don't Waste Resources

CDC event arrives → Cache deleted
    ↓
Offline customers:
    - Not requesting data (they're offline)
    - Not using API calls (they're offline)
    - Not consuming memory (cache deleted)
    ↓
Result: ZERO resources wasted ✅

2. Online Customers Get Fresh Data

CDC event arrives → Cache deleted
    ↓
Next request (from ANY online customer):
    - Cache miss
    - 1 API call to Salesforce
    - Fresh data stored in cache
    ↓
Subsequent requests (from ALL online customers):
    - Cache hit
    - 0 API calls
    ↓
Result: Fresh data shared by everyone ✅

3. Memory Stays Lean

Before CDC:
    Redis: "catalog:internet:plans" = [old data]
    Memory: ~500KB
    
CDC event arrives:
    Redis: DELETE "catalog:internet:plans"
    Memory: 0 KB ✅
    
Next customer request:
    Redis: "catalog:internet:plans" = [fresh data]
    Memory: ~500KB (with 24h TTL)

🔄 Complete Example: 100 Customers

SETUP:
- 100 total customers
- 50 online & active (viewing website)
- 30 online & idle (logged in, not viewing catalog)
- 20 offline (not logged in)

TIME: 10:00 AM - Product price changes in Salesforce
    ↓
    ONE CDC event sent (not 100 events!)
    ↓
    Portal receives event
    ↓
    DELETE "catalog:internet:plans" (one global key)
    ↓
    Cache no longer exists for ANYONE

TIME: 10:01 AM - Customer #37 (online, active) refreshes page
    ↓
    Cache miss (key deleted)
    ↓
    1 API call to Salesforce
    ↓
    Fetches fresh data (new price)
    ↓
    Stores in Redis with 24h TTL
    ↓
    Key: "catalog:internet:plans" = [fresh data]

TIME: 10:02 AM - Customer #42 (online, active) refreshes page
    ↓
    Cache HIT (Customer #37 populated it)
    ↓
    0 API calls ✅
    ↓
    Shows fresh data

TIME: 10:03 AM - Customers #1-20 (online, active) view catalog
    ↓
    All cache HITs
    ↓
    0 API calls ✅

TIME: 10:30 AM - Customer #55 (was idle, now viewing catalog)
    ↓
    Cache HIT (still fresh from 10:01 AM)
    ↓
    0 API calls ✅

OFFLINE CUSTOMERS (#81-100):
    ↓
    Not requesting anything (offline)
    ↓
    0 API calls ✅
    ↓
    When they log in later:
        - Cache might exist (if populated by others)
        - OR Cache might be expired (24h TTL)
        - Either way: Fresh data

RESULT:
    CDC event: 1 event for 100 customers
    API calls: 1 call (Customer #37)
    Cache hits: 99 other customers shared the result
    Offline customers: 0 impact, 0 waste

🎯 Direct Answers to Your Questions

Q1: "We received CDC for a customer that's offline, what do we do?"

Answer: CDC is NOT "for a customer" - it's a GLOBAL notification!

CDC Event: "Product X changed"
    ↓
Action: Delete global cache key
    ↓
Offline customer: Does nothing (not requesting data)
    ↓
When they login later: Fetches fresh data

Q2: "What do we do for existing customer?"

Answer: Same action - delete global cache!

CDC Event: "Product X changed"
    ↓
Action: Delete global cache key
    ↓
Online customer: Next request is cache miss
    ↓ 
Fetches fresh data from Salesforce
    ↓
Stores in cache for everyone

🔍 Only USER-SPECIFIC Data Has Per-Customer Logic

Global Cache (CDC invalidates for everyone):

// Products - same for all customers
"catalog:internet:plans"

// Prices - same for all customers  
"catalog:sim:plans"

// Addons - same for all customers
"catalog:vpn:plans"

User-Specific Cache (CDC invalidates per customer):

// Eligibility - different per customer
"catalog:eligibility:801xxx"  Customer A
"catalog:eligibility:802xxx"  Customer B

// Orders - different per customer
"orders:account:801xxx"       Customer A's orders
"orders:account:802xxx"      ← Customer B's orders

Account eligibility CDC:

{
  "payload": {
    "AccountId": "801xxx",  // ← Specific customer!
    "Internet_Eligibility__c": "Home 10G"
  }
}

Action:

// Only invalidate THAT customer's eligibility
await cache.del("catalog:eligibility:801xxx");

// Other customers' eligibility stays cached ✅

📊 Summary Table

Cache Type CDC Event Offline Customer Online Customer
Global Catalog Product2 changed Delete global cache. Customer offline, no impact. When logs in: fresh fetch Delete global cache. Next request: cache miss, fetch fresh
User Eligibility Account X changed Delete cache for Customer X only. Other customers unaffected Delete cache for Customer X only. Next request: fresh fetch
Orders Order X changed Delete cache for Order X & Account. Customer offline, no impact Delete cache for Order X & Account. Next request: fresh fetch

🎓 The Elegance of This Design

Why it works:

  1. CDC is a notification system, not a data distribution system
  2. Cache is deleted, not updated → Zero stale data
  3. Global cache shared by all → Maximum efficiency
  4. Lazy loading → Only fetch when actually requested
  5. Offline users invisible → No special handling needed

Result:

  • Simple logic (no tracking of online/offline)
  • Correct behavior (always fresh data)
  • Efficient (minimal API calls)
  • Memory efficient (deleted cache = 0 bytes)

🚀 Conclusion

When CDC arrives:

  1. Delete the global cache key
  2. Done. That's it.

Offline customers:

  • Not requesting data → No impact
  • No API calls → No cost
  • No memory used → Efficient

Online customers:

  • Next request → Cache miss
  • 1 API call → Fresh data
  • Other customers → Cache hit

You don't need to:

  • Track who's online/offline
  • Check customer status
  • Store data per customer (for global catalog)
  • Do anything special

Just:

  • Delete cache when CDC arrives
  • Let customers fetch on-demand
  • Share cached results globally

Simple, correct, efficient! 🎉