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

推荐订阅源

Martin Fowler
Martin Fowler
WordPress大学
WordPress大学
月光博客
月光博客
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
大猫的无限游戏
大猫的无限游戏
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
博客园 - 聂微东
Apple Machine Learning Research
Apple Machine Learning Research
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
雷峰网
雷峰网
小众软件
小众软件
酷 壳 – CoolShell
酷 壳 – CoolShell
博客园 - 叶小钗
美团技术团队
宝玉的分享
宝玉的分享
Hugging Face - Blog
Hugging Face - Blog
阮一峰的网络日志
阮一峰的网络日志
A
About on SuperTechFans
Jina AI
Jina AI
D
Docker
Last Week in AI
Last Week in AI
MongoDB | Blog
MongoDB | Blog
Stack Overflow Blog
Stack Overflow Blog
Microsoft Azure Blog
Microsoft Azure Blog

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 Moment the JSON Config Parser Became the Enemy
pretty ncube · 2026-05-27 · via DEV Community

The Problem We Were Actually Solving

The treasure-hunt server receives 50 MB/s of dynamic map events—player moves, loot spawns, fog-of-war reveals—and must broadcast deltas to 100 k sockets without re-serializing the entire world every tick.
The public docs show a simple YAML snippet under config.yaml:

world:
 width: 1024
 height: 1024
 chunk_size: 32

Enter fullscreen mode Exit fullscreen mode

What they do not mention is the hidden oltp_workers: 4 knob that the YAML parser silently casts to a u16 and then divides by the core count.
Our perf profile at 28 k sessions with perf record -F99 -g -p <pid> showed 42 % of CPU burned in serde_yaml::from_reader waiting for the lock around the global IndexMap.
The real constraint was never CPU or GC; it was the JSON/YAML bridge that blocked on every config reload even though the server never changed those values at runtime.

What We Tried First (And Why It Failed)

We started with serde_yaml because the helm chart shipped a ConfigMap volume.
After profiling with flamegraph-rs we saw 1.8 μs per config reload, but multiplied by 28 k sessions and the Kubernetes watch events, we added 50 ms of tail latency every time the ConfigMap updated—even when the file content was identical.
The stack trace was:

serde_yaml::indexmap::IndexMap<K,V>::entry
└── _raw_vec::RawVec<T,A>::reserve

Enter fullscreen mode Exit fullscreen mode

The IndexMap kept reallocating the backing array on every watch trigger.
We tried serde_json with the same file; the parser was 2× faster, but the blocking I/O still destroyed tail latency.
The benchmark at 10 k players showed p99 = 34 ms; we needed < 50 ms to pass the load-test gate.

The Architecture Decision

We ripped out the whole config layer and replaced it with a two-part system:

  1. A compile-time constants module generated from a tiny TOML file (constants.toml) with build.rs.
  2. A sidecar gRPC service that only accepts runtime state diffs and streams them to the main process over a Unix domain socket.

The constants are embedded in the binary, so the treasure-hunt server never parses anything at runtime.
We moved the dynamic knobs—collision radius, loot table seed, rate limits—into a separate protobuf schema served by the sidecar.
The protobuf schema is versioned, delta-encoded, and uses the tonic async runtime, so the config change path is lock-free and non-blocking.
The gRPC sidecar itself uses Rust, but the main server now spends zero CPU on config parsing and zero wall time on file I/O.

What The Numbers Said After

After the change we re-ran the 28 k session test with perf stat -e cache-misses,instructions -d and saw:

Before:
 42.1 % cache misses
 1.3 s p99 /w config updates
 2 RTS (runtime scaling stalls)
After:
 11.8 % cache misses
 29 ms p99
 8 RTS (no stalls)

Enter fullscreen mode Exit fullscreen mode

Tail latency at 1 ms granularity (collected with tokio-console) dropped from 48 ms to 6 ms.
The sidecar measured 120 B/s of traffic even under load, so the diff protocol is effectively free.
We also removed the jemalloc dependency in the main process because the config hot path was gone; RSS dropped from 1.4 GB to 920 MB.

What I Would Do Differently

We should have asked on day one: Which subsystems are actually dynamic?
The docs hint at a combined.yaml that mixes compile-time constants with runtime overrides; that hint is a footgun.
Next time I see a YAML file in the critical path I will pre-process it with serde during build, emit a header file, and #include it—no runtime parsing, no locks, no surprises.
The only runtime configuration that survives will be the gRPC diff service, and that path is already async and lock-free by design.

The moment the JSON config parser became the enemy was the moment we stopped reading the docs and started profiling the real bottleneck.


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