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

413 lines
10 KiB
Markdown

# CDC Event Flow: Customer-Specific vs Global Cache
## 🎯 The Key Misunderstanding
### What CDC Events Actually Contain
```json
// 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:
```typescript
// 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:
```typescript
// 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):
```typescript
// 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):
```typescript
// 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:**
```json
{
"payload": {
"AccountId": "801xxx", // ← Specific customer!
"Internet_Eligibility__c": "Home 10G"
}
}
```
**Action:**
```typescript
// 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! 🎉