After studying this material, you should be able to:
A batch job is a scheduled process that runs at fixed intervals (e.g., nightly) to process accumulated data.
Timeline Example:
Monday 2:00 PM → Guest browses 10 listings
Monday 8:00 PM → Guest searches again
❌ System still uses YESTERDAY'S data
Tuesday 2:00 AM → Nightly batch job runs
Tuesday 2:00 PM → Updated recommendations finally available
⏱️ Total lag: ~24-48 hours
Key Insight: A guest actively planning a trip changes their preferences rapidly. Yesterday's browsing data may no longer reflect today's intent.
| User Action | Batch System Response | Ideal Response |
|---|---|---|
| Views beach listings | Updates tomorrow | Updates immediately |
| Changes dates | Updates tomorrow | Updates immediately |
| Searches new city | Updates tomorrow | Updates immediately |
Airbnb's original system separated heavy computation from real-time serving:
STAGE 1: Offline (Nightly Batch)
┌─────────────────────────────────────┐
│ Guest History Data │
│ (bookings + reviews + browsing) │
│ ↓ │
│ Transformer Sequence Encoder │
│ (computationally expensive) │
│ ↓ │
│ Guest Embedding stored in │
│ Low-Latency Store │
└─────────────────────────────────────┘
STAGE 2: Online (Real-Time, milliseconds)
┌─────────────────────────────────────┐
│ Guest runs a search │
│ ↓ │
│ Fetch embedding from store │
│ + Combine with live search query │
│ ↓ │
│ Score and rank listings │
└─────────────────────────────────────┘
Key Concept — Embedding: A numerical vector representation of a guest's preferences and history, compressed into a format the ranking model can use quickly.
Before seeing the solution, understand what makes real-time updates difficult:
Challenge 1: TRIGGERING
"How do we know WHEN to update an embedding?"
→ Batch: easy — run every night
→ Real-time: must detect each individual activity event
Challenge 2: COMPUTATION ON THE CRITICAL PATH
"Transformers are slow — we can't run them during a search request"
→ Must run inference BEFORE the search, not during it
Challenge 3: INTEGRATION
"How do we update without breaking the existing serving system?"
→ The ranking model already reads from the low-latency store
→ Solution must write to the same place
Old approach (Pull/Poll model):
Downstream system: "Any new data?" → checks every N minutes
"Any new data?" → checks again
"Any new data?" → checks again
"Yes, here it is" → finally acts
New approach (Push/Event-driven model):
Streaming job: detects new guest activity
→ immediately publishes notification
→ downstream consumer acts RIGHT NOW
Analogy: The difference between checking your email every hour (pull) versus receiving a push notification the moment a message arrives (push).
Push Mode Flow:
Guest views listing
↓
Streaming job captures event
↓
Feature value updated
↓
Push notification sent immediately ← NEW CAPABILITY
↓
Downstream process triggered
Old approach:
Option A: Embed model logic in application code (inflexible, hard to update)
Option B: Wait for batch job to run inference (slow, stale)
New approach:
NRT Model Transform:
Streaming pipeline → calls deployed Transformer model → writes result as feature value
(same model as batch pipeline) (immediately available)
Key Insight: The same Transformer model used in the nightly batch job is now called from within the streaming pipeline — no need to maintain two separate models.
COMPLETE NEAR-REAL-TIME PIPELINE:
1. Guest Event Occurs
(page view, new search, listing click)
↓
2. Streaming Job Captures Event
+ merges with existing long-term & short-term sequence data
↓
3. PUSH MODE signals: "New sequence data is ready"
↓
4. NRT MODEL TRANSFORM runs Transformer sequence encoder
→ produces fresh embedding
↓
5. Embedding written to LOW-LATENCY STORE
(same store the batch job always used)
↓
6. Next search request reads fresh embedding
→ ranking model uses up-to-date guest representation
Total time: 10-30 seconds (vs. up to 2 days before)
| Component | Before | After |
|---|---|---|
| Transformer model | Runs nightly | Same model, runs in streaming pipeline |
| Low-latency store | Written by batch job | Written by streaming pipeline |
| Ranking model | Reads from store | Reads from same store (unchanged) |
| Embedding freshness | Up to 2 days stale | Under 1 minute |
| Trigger mechanism | Scheduled clock | Activity event |
Before: ~48 hours lag
After: 10-30 seconds lag
Improvement: ~5,760x faster updates
NDCG (Normalized Discounted Cumulative Gain)
Result: +1.67% NDCG improvement
→ This is a very large gain for a decade-old, highly optimized system
Business Impact
~0.33% increase in uncancelled bookings
Key insight confirmed: Freshness itself improves ranking quality
→ The SAME guest representation becomes MORE valuable simply by being current
1. REACT → detect an activity event (Push Mode)
2. MERGE → combine with existing state
3. INFER → run model immediately (NRT Model Transform)
4. SERVE → write result to serving store
This pattern is not specific to Airbnb's use case — it applies to any system where:
- User behavior changes rapidly
- Model inference is too expensive for the critical serving path
- Freshness directly impacts recommendation quality
| Approach | Description | Limitation |
|---|---|---|
| Pipeline-specific solution | Build custom real-time logic for this one use case | Other teams must rebuild from scratch |
| Platform capability | Add Push Mode + NRT Transform to Chronon | Any team using Chronon benefits immediately |
Key Engineering Principle: Solve problems at the platform level when the solution generalizes — don't build one-off integrations.
PROBLEM
└── Batch embeddings are stale (up to 2 days)
└── Recent activity is most relevant to current intent
SOLUTION
├── Push Mode
│ └── Event-driven triggering instead of scheduled polling
│
└── NRT Model Transform
└── Run Transformer inference inside streaming pipeline
RESULT
├── Technical: 10-30 second freshness (vs. 2 days)
├── Quality: +1.67% NDCG improvement
├── Business: ~0.33% more uncancelled bookings
└── Strategic: Reusable platform foundation for future models