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

推荐订阅源

博客园 - 叶小钗
Last Week in AI
Last Week in AI
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
V
V2EX
J
Java Code Geeks
The GitHub Blog
The GitHub Blog
博客园_首页
U
Unit 42
人人都是产品经理
人人都是产品经理
Engineering at Meta
Engineering at Meta
IT之家
IT之家
G
Google Developers Blog
L
LangChain Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
爱范儿
爱范儿
罗磊的独立博客
博客园 - 三生石上(FineUI控件)
Martin Fowler
Martin Fowler
Jina AI
Jina AI
有赞技术团队
有赞技术团队
Apple Machine Learning Research
Apple Machine Learning Research
Vercel News
Vercel News
小众软件
小众软件
H
Help Net Security

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
When the Treasure Hunt Engine Eats Itself: My First Produ...
pretty ncube · 2026-05-27 · via DEV Community

The Problem We Were Actually Solving

The treasure-hunt engine is a state machine that advances an epoch every 10s, recomputing leaderboards and validating 100k+ player claims in a single Lua coroutine. One night the Lua heap counter—yes, we were still using debug.getregistry()—jumped from 64MB to 412MB inside 90 minutes. By 03:47 the kernel started swapping, the GC froze for 1.8s, and the epoch stall propagated to every player session.

The on-call rotation reset the process at 03:49, but I could see the same pattern replaying: epoch duration rising linearly with heap size. The SLA required 95th percentile claim validation under 50ms; we were testing at 23ms in staging with synthetic loads, so the regression felt personal.

What We Tried First (And Why It Failed)

Our first fix was to bump LUA_GCSTEP from 200 to 2000. The theory was that larger steps would let Lua finish collections faster. What actually happened: the major GC cycle took 600ms and paused all player sessions because Lua coroutines arent preemptible. The p99 latency graph developed a comb pattern—good epochs at 30ms, bad epochs at 1.5s.

Next we tried running two Lua states in a sharded cluster. The cross-shard RPC latency added 18ms baseline, and the new Lua states still accumulated memory until they OOMed. The CPU flame graph showed 37% of cycles in luaV_execute, still fighting the interpreter.

The Architecture Decision

We stopped trying to tune the Lua interpreter and wrote a new epoch engine in Rust. Instead of one monolithic Lua coroutine, the Rust version splits the 100k claims into 16 independent segments that parallelize over a Tokio work-stealing scheduler. Each segment uses hashbrowns raw_entry API so we can validate 60k claims/s per core without allocations.

The critical tradeoff: we lost the ability to hot-patch game logic at runtime. Our deployment now requires a binary rollout and a safety check in CI that runs the Rust engine against the exact Lua bytecode we retired. That check caught a bug in the claim expiry logic where we were double-counting a timestamp overflow—something luacheck would never see because the overflow wrapped silently in Luas number type.

What The Numbers Said After

Heres the before and after from the production run the week after cutover. All numbers are 5-minute rolling medians measured on c6g.4xlarge (16 vCPU Graviton2):

Metric Lua Defaults Rust Engine
Heap Growth / hour 127 MB 0 MB
Epoch Duration 23 ms 11 ms
P99 Validation 420 ms 28 ms
RSS After 7 days 1.2 GB 89 MB
GC Pauses > 100ms 47 / hour 0 / hour

The new endpoints also exposed a latent Redis hotspot: the Lua version had been using EVALSHA with a 512-byte script that serialized the entire claim set, so every call touched ~20k keys and caused a 3ms tail latency. The Rust version switched to HSCAN batches of 1000 keys and cut that tail to 1.1ms.

What I Would Do Differently

I would not have assumed Luas defaults were wrong. The LuaJIT defaults are excellent for short-lived scripts, but for a 24/7 service maintaining 100k+ dynamic states, the interpreters GC and scheduler are the wrong abstractions.

I would also have measured memory growth from day one instead of trusting the staging suite. The staging cluster only ran 10k synthetic claims and a 64MB heap was fine; prod was 10x bigger and 100x longer-lived. A single Grafana panel with lua_gc_total_bytes would have saved the outage.

Finally, I would have resisted the temptation to preserve Lua compatibility longer. Every week we kept the dual stack added complexity: two build pipelines, two dependency trees, and two places to deploy. Once the Rust engine passed the bytecode compatibility test, we should have killed the Lua path immediately.


Same principle as removing a memcpy from a hot path: remove the intermediary from the payment path. This is how: https://payhip.com/ref/dev2