惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

云风的 BLOG
云风的 BLOG
M
MIT News - Artificial intelligence
博客园 - Franky
J
Java Code Geeks
V
Visual Studio Blog
G
Google Developers Blog
罗磊的独立博客
MongoDB | Blog
MongoDB | Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Recent Announcements
Recent Announcements
Last Week in AI
Last Week in AI
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Stack Overflow Blog
Stack Overflow Blog
博客园 - 司徒正美
The GitHub Blog
The GitHub Blog
腾讯CDC
阮一峰的网络日志
阮一峰的网络日志
V
V2EX
博客园 - 【当耐特】
IT之家
IT之家
I
InfoQ
U
Unit 42
C
Check Point Blog
Martin Fowler
Martin Fowler

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
The Day the Treasure Hunt Engine Decided to Lie to Us Abo...
Lisa Zulu · 2026-05-27 · via DEV Community

The Problem We Were Actually Solving

The marketing department wanted treasure hunts to feel instant. Not just responsive, but psychologically immediate—a sub-second confirmation that a chest was open, a key found, a prize unlocked. The CFO signed off on it because higher perceived speed meant higher revenue per session. The system had to return a 200 OK before the players thumb finished lifting from the screen.

The default implementation used an event bus with a fan-out pattern. Every action (open chest, claim prize) was a message published to 3 downstream services: inventory, wallet, analytics. Each service had its own database. The promise was atomic consistency via saga pattern with compensating transactions. In practice, the saga orchestrator added 80–140ms of round-trip latency under load. The marketing dashboard showed 95th percentile latency of 85ms because it only measured the orchestrators completion time, not the time until every downstream had acknowledged.

What We Tried First (And Why It Failed)

We tried sharding each service by player ID. That cut the fan-out path by 60%, but introduced a new failure mode: the dreaded double-credit edge case. A wallet service would process a claim, emit a success event, but die before emitting to analytics. The saga orchestrator, seeing the timeout, rolled back the wallet update. The players balance was restored, but the analytics event had already been consumed by a separate dashboard loader that assumed it was authoritative. The CFO got a report showing revenue spikes that never materialized. We had to refund $87k in two weeks.

Then we tried Redis Streams with consumer groups. The streams promised ordered processing and exactly-once semantics. We turned off the saga orchestrator entirely. The first outage happened when a consumer group rebalance took 4.2 seconds. During that window, 1,800 duplicate treasure chest openings were processed because the consumer offsets didnt advance atomically with the acknowledgment. Our retry budget was 120ms, and the backlog grew faster than we could scale pods.

The Architecture Decision

We ripped out the event bus and went back to one database transaction that updates inventory, wallet, and analytics in a single ACID block. The tradeoff: we can only use PostgreSQL. We sharded the primary key on player ID, and the entire treasure hunt operation is a single UPDATE statement with RETURNING. The latency percentile improved to 15ms 95th, but we sacrificed service independence. If inventory schema changes, wallet breaks. If analytics needs a new column, the entire treasure hunt endpoint must deploy together.

We mitigated the coupling with feature flags. The endpoint first checks a LaunchDarkly flag: if disabled, it falls back to the event bus path with saga plus compensating transactions. We use the flag to gradually roll out the new path, but only to players with session IDs that are multiples of 7. That gives us a 14% canary group, enough to catch edge cases without polluting the global success metrics. We also added a circuit breaker that flips to fallback if 500 errors exceed 0.3% in a 30-second window.

What The Numbers Said After

The new path ran for 47 days without a false-positive treasure award. The 95th percentile latency stayed at 15ms with 99.7% of requests completing in under 50ms. The database CPU spiked during peak hours, so we added a read replica in us-west-2 to absorb analytics reads. We also discovered that 12% of treasure hunts were actually bots—players using automation scripts. By abandoning the saga pattern, we removed two async steps where bots had previously exploited timing gaps to trigger double claims.

The real surprise was operational. Before, when the event bus lagged, we could restart a single service without touching the rest. Now, if inventory schema has a breaking change, we must deploy the entire cluster. The rollback process is a full database restore from an S3 snapshot. We rehearsed the rollback on staging and it took 8 minutes 22 seconds. We decided to keep the old saga path behind the feature flag for emergencies only—its the theatrical version of speed, but its the one that can be rolled back in minutes.

What I Would Do Differently

I would have pushed back on the sub-second requirement from day one. The psychological truth is that anything under 100ms feels instant, but anything under 200ms feels acceptable. We spent three sprints chasing a 15ms gain that only mattered to marketing dashboards. If I had insisted on measuring perceived latency—time from tap to visual feedback rather than endpoint completion—we could have saved months of engineering drama.

I would also have refused the sharded Redis Streams experiment. Consumer group rebalances are non-deterministic in production load. The only streams that work at scale are ones where the partition key aligns with an immutable business key—player ID, session ID. If you cant guarantee that, avoid streams.

Finally, I would have built observability earlier. We added a custom Prometheus metric called treasure_latency_seconds_histogram that measures the time from tap to successful treasure claim confirmation on the client. That metric immediately exposed that 8% of players were seeing 300ms+ latency due to mobile network jitter, not our backend. We fixed it by pushing the confirmation modal only after the client received the 200 OK, not before. The marketing promise remained intact, but the engineering lie was finally visible.