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

推荐订阅源

Microsoft Security Blog
Microsoft Security Blog
J
Java Code Geeks
GbyAI
GbyAI
aimingoo的专栏
aimingoo的专栏
L
LangChain Blog
I
InfoQ
D
Docker
F
Fortinet All Blogs
Y
Y Combinator Blog
Martin Fowler
Martin Fowler
月光博客
月光博客
B
Blog
Engineering at Meta
Engineering at Meta
T
Tailwind CSS Blog
罗磊的独立博客
博客园_首页
G
Google Developers Blog
Stack Overflow Blog
Stack Overflow Blog
Recent Announcements
Recent Announcements
D
DataBreaches.Net
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
B
Blog RSS Feed
IT之家
IT之家
V
V2EX

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 Your Search Tree Becomes the Bottleneck in a Distrib...
pretty ncube · 2026-05-27 · via DEV Community

The Problem We Were Actually Solving

In Hytales Veltrix region server, each treasure hunt request had to traverse every placed container, ore vein, and hidden chest within a 256-block radius. The server runs at 60 ticks per second with 120 concurrent players, so per-player search latency had to stay below 16ms. What I measured on a representative region was 28–42ms for a single search call, and that was with LuaJITs JIT already hot.

The real problem wasnt Luas speed; it was the index. We stored treasure locations in a flat Lua table keyed by chunk coordinates, then filtered with a hand-written loop. On regions with 12k chunks, the loop touched 12k entries per search. A profiler flame graph showed 63% of CPU time inside luaH_getstr—hash lookups—plus 22% in the Lua VM loop. The index didnt scale; the language wasnt the bottleneck.

What We Tried First (And Why It Failed)

I tried three Lua-based optimizations before touching the runtime:

  1. A bloom filter over chunk coordinates to skip empty ones. Result: bloom false-positive rate 11% causing extra hash table probes, latency variance spiked to 78ms on hot paths.
  2. A C module that precomputed spatial hashes in a flat array. Result: still Lua-facing memory allocations caused GC pauses up to 5ms at 95th percentile.
  3. LuaJITs FFI to call quickjss JSONPath. Result: GC pressure moved from Lua to JS VM, plus the call boundary added 300ns per search—negligible per call, but multiplied across 120 players it was 36ms extra per tick.

Every fix moved the constraint but didnt remove it. At that point I accepted the truth: the problem wasnt Luas speed; it was the data structure and the runtimes GC behavior under load.

The Architecture Decision

We picked Rust for the indexer and moved the treasure search workload into a separate process. The rationale was fourfold:

  1. Zero-cost abstractions: an R-tree from the rstar crate could index 2D points with O(log n) queries and no dynamic dispatch.
  2. No GC: allocations in the indexer process wouldnt pause the main game loop.
  3. Serialization boundary: we could use flatbuffers to serialize only the search results back to Lua, reducing cross-process data transfer.
  4. Safety: we had already hit segfaults in Lua C modules when the game patched memory in-place; Rusts borrow checker eliminated that class of bugs.

The tradeoff was latency: round-trip via FlatBuffers added 150µs per search, but we gained predictability. More importantly, the indexer process could grow its heap without affecting the LuaJIT GC pause times.

What The Numbers Said After

After the switch, we ran identical 10-minute load tests on the same region with 120 bots. Metrics collected with perf_4.19 and flamegraph.pl:

  • LuaJIT main loop: 2.1ms per tick median, 3.8ms 95th percentile (was 6.4ms / 12.1ms)
  • Treasure search per request: 1.8ms median, 3.9ms 95th percentile (was 28ms / 42ms)
  • Indexer RSS: 48MB resident, growing 2MB per 1000 searches (stable)
  • GC pauses in LuaJIT: 0.1ms median, max 1.2ms at 99.9th percentile (was 4.2ms / 5.8ms)

The system still saturates CPU at 105 players, but the treasure search component is now 20x faster and no longer a contributor to tick jitter. The allocation rate in the indexer process, measured via /proc/[pid]/smaps, is 1.4 allocations per search, totaling 3.8KB per second at 120 players.

What I Would Do Differently

I would not have moved the entire treasure logic to Rust. The cross-process serialization cost is small in absolute terms, but it adds complexity in logging, debugging, and versioning the FlatBuffers schema. Next time Id keep Lua for the high-level hunt API and use Rust only for the spatial index and culling.

I would also avoid rstars default R*-tree if the dataset is static for long periods. We measured 3ms to rebuild the tree on region load; switching to a packed Hilbert R-tree from the quadtree crate cut rebuild time to 0.4ms without changing query performance.

Finally, I would instrument the indexer process with tikv-jemalloc-rs from day one. We did post hoc analysis and found the jemalloc arena used 32MB at startup; by pre-tuning arenas and background threads we shaved an extra 0.7ms off 99th percentile latency.