The Churn Problem
Player churn is the single biggest threat to a mobile game's revenue. Studies show that acquiring a new player costs 5-7x more than retaining an existing one. Yet most game studios detect churn only when it is too late -- after a player has already stopped logging in for 14+ days. By then, re-engagement rates drop below 5%.
CCFish needed a proactive approach: detect churn signals early, trigger automated re-engagement campaigns, and measure effectiveness. This post details the serverless churn detection pipeline built on Cloudflare D1 and Workers.
Architecture: Real-Time Churn Scoring
The system uses a multi-signal scoring model that runs every 6 hours via a cron-triggered Cloudflare Worker:
- **Login recency**: days since last session (weight: 40%)
- **Session frequency delta**: sessions per week this month vs. last month (30%)
- **Purchase recency**: days since last IAP (20%)
- **Feature engagement**: number of distinct features used last 7 days (10%)
Each signal contributes to a Churn Risk Score (0-100). Players scoring above 60 enter the watchlist. Those above 80 trigger immediate re-engagement.
Step 1: The Scoring Query
A single D1 query computes the churn risk for all players in under 500ms:
```sql
SELECT
p.id, p.fcm_token, p.email,
ROUND(
CASE
WHEN julianday('now') - julianday(p.last_login) > 7 THEN 40
ELSE (julianday('now') - julianday(p.last_login)) * 5.7
END +
CASE
WHEN s.sessions_last_month > 0 THEN
30 * (1 - CAST(s.sessions_last_week AS REAL) /
GREATEST(s.sessions_last_month / 4.0, 1))
ELSE 30
END +
CASE
WHEN julianday('now') - julianday(p.last_purchase) > 30 THEN 20
ELSE (julianday('now') - julianday(p.last_purchase)) * 0.67
END +
CASE
WHEN COALESCE(f.features_used, 0) < 3 THEN 10
ELSE (3 - COALESCE(f.features_used, 0)) * 3.3
END
) as churn_score
FROM players p
LEFT JOIN session_stats s ON s.player_id = p.id
LEFT JOIN feature_usage f ON f.player_id = p.id
WHERE p.push_opt_out = 0
HAVING churn_score > 60
ORDER BY churn_score DESC;
```
The query runs in a single D1 invocation, returning all at-risk players with their scores and contact tokens. Total execution time averages 320ms for CCFish's player base.
Step 2: Personalized Re-Engagement Campaigns
Once at-risk players are identified, the system triggers personalized campaigns based on churn score and player history:
| Churn Score | Campaign | Channel | Offer |
|---|---|---|---|
| 60-70 | Soft nudge | Push notification | New levels added |
| 70-85 | Incentive | Push + Email | Free gem bundle + login bonus |
| 85+ | Urgent save | Push + Email + SMS | 50% off any IAP + exclusive item |
Each campaign template is stored in D1 and rendered at send time with player-specific variables. The system pulls the right template based on churn score range and fills in player-specific data like name and days since login.
Step 3: Automated Campaign Optimization
The system tracks campaign effectiveness and auto-adjusts:
- Re-engagement rate: percentage of players who return within 7 days
- Cost per saved player: offer cost divided by number of re-engaged players
- Best channel analysis: which channel performs per segment
After each campaign cycle, the Worker updates a D1 optimization table. When a segment-channel-variant reaches 100 samples, the system automatically shifts budget to the highest-performing combination. This continuous optimization loop improves campaign ROI by roughly 15% month-over-month.
Results from Production
Over 12 weeks of automated churn management:
- Monthly churn rate: 18.2% (down from 31% baseline, a 41% reduction)
- Re-engagement success: 34% of at-risk players returned within 7 days
- Revenue recovery: $3,200 per month in salvaged IAP revenue
- False positive rate: 8% (players scored 60+ but were not actually churning)
- Infrastructure cost: $0 (within Workers free tier and D1 5GB storage)
Key Takeaways
- Real-time churn scoring via D1 eliminates the latency gap between signal and response
- Personalization at scale works: targeted offers re-engage 34% of at-risk players
- Serverless architecture keeps costs at zero for indie game volumes
- Multi-channel campaigns outperform single-channel by 2.3x
- Continuous optimization loops improve campaign ROI month-over-month
Data Collection Pipeline
The churn scoring system depends on high-quality behavioral data. CCFish captures player events through a lightweight SDK that sends structured events to a Cloudflare Worker endpoint:
```json
{
"player_id": "abc123",
"event": "session_start",
"timestamp": "2026-05-09T12:00:00Z",
"properties": {
"session_duration": 840,
"level": 12,
"device": "iPhone 15"
}
}
```
Events are batched client-side (every 5 events or 60 seconds, whichever comes first) and sent to the Worker endpoint. The Worker validates the schema, enriches with server-side data (IP geolocation, user-agent parsing), and bulk-inserts into D1 using prepared statements. This batching approach handles up to 10,000 events per second without exceeding D1's write limits.
Handling False Positives
An 8% false positive rate means some players flagged as at-risk are actually still engaged. The system mitigates this through:
- **Confidence bands**: Players scoring 60-65 receive only a soft nudge (no offers) to avoid wasting incentives
- **Cool-off period**: If a player opens the app within 24 hours of a re-engagement campaign, they are removed from the watchlist for 14 days
- **Feedback loop**: Players who return organically after being flagged are analyzed to improve the scoring model
This balance ensures that aggressive re-engagement does not annoy genuinely active players while still capturing nearly all true churn cases.
Scaling Beyond CCFish
The churn detection pipeline is designed as a reusable module. The same Worker code and D1 schema can be deployed for any game or app by changing:
- The event schema mapping (which fields map to churn signals)
- The scoring weights (configurable per-segment via D1 config table)
- The campaign templates (stored in D1, fully customizable)
AIKit's plugin marketplace already offers a packaged version of this pipeline as a drop-in plugin for EmDash-powered game sites. Setup time: approximately 15 minutes from install to first churn report.