← Explore·

#systemsprogramming

Public entries tagged #systemsprogramming

🖥️ CLI11 — Command line parsing for C++ without the pain

Every server tool, every daemon, every diagnostic utility needs command line arguments. And every C++ developer has at some point wrestled with getopt, manually parsed argv, or pulled in Boost just for program_options.

There’s a better way. 🎯

📦 Header-only & C++11
Drop in a single header, done. No build system changes, no extra dependencies, works on any compiler supporting C++11 and up. Perfect for embedded Linux targets and minimal server builds where you control the toolchain.

⚙️ The basics are refreshingly clean:

app.add_option("-p,--port", port, "Port to listen on");
app.add_flag("-v,--verbose", verbose, "Enable verbose output");
CLI11_PARSE(app, argc, argv);

That’s it. Types, defaults, descriptions — all in one line per argument.

🌳 Subcommands — for tools that grow
When your binary does more than one thing, CLI11 handles it naturally:

auto start = app.add_subcommand("start", "Start the server");
auto stop = app.add_subcommand("stop", "Stop the server");

Same pattern as git, docker, systemctl. Your users already know how it works.

🛡️ And because it’s CLI11, you get automatic –help, type validation, and useful error messages for free — without writing a single line of parsing logic yourself.

🐧 Less boilerplate. More server.

Continue reading →

⚡ TCP_NODELAY — The one flag every game server and trading system sets
By default, TCP is trying to be clever. Too clever.

It uses an algorithm called Nagle’s Algorithm — introduced in 1984 — that buffers small packets and waits before sending them. The idea: bundle multiple small writes into one bigger packet to reduce network overhead.
Sounds smart. In the wrong context, it’s a latency killer. 💀

🎮 Why it hurts game servers
Your game sends a tiny position update — 20 bytes. Nagle says: “Wait, maybe more data is coming.” So it holds the packet for up to 200ms hoping to bundle it.
In a first-person shooter, 200ms feels like an eternity.

📈 Why it hurts trading systems
A market order hits your server. Nagle buffers it. Your competitor’s order lands 40ms earlier. You missed the trade.
In high-frequency trading, microseconds are money.

🔧 The fix is one line:
int flag = 1;
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag));

Every packet ships immediately. No buffering, no waiting.

⚠️ One caveat: TCP_NODELAY increases network overhead for chatty protocols. For bulk file transfers or HTTP it’s usually wrong. For real-time systems it’s almost always right.

🐧 Sometimes the smartest thing your OS can do is get out of the way.

Continue reading →

🛑 Graceful Shutdown in C++ — because kill -9 is not a deployment strategy

Your server handles thousands of connections. You deploy a new version. What happens to the clients mid-request when the process dies?

Without graceful shutdown — nothing good. 💀

🔌 Step 1 — Catch the signal
The OS sends SIGTERM before it sends SIGKILL. That’s your window.
signal(SIGTERM, on_shutdown);
Set a flag, don’t do heavy work inside the handler. Simple and safe.

🚫 Step 2 — Stop accepting new connections
Close or stop listening on your socket immediately. New clients will get a clean refusal instead of a sudden reset.

⏳ Step 3 — Drain existing connections
Let active requests finish. Give them a deadline — 5 or 10 seconds is usually enough. After that, you cut them loose. Your call.

📝 Step 4 — Flush your logs
Before the process exits, tell spdlog to flush:
—> spdlog::shutdown();
Because the last thing you want is missing log entries right before a crash or restart.

🔄 Combined with SO_REUSEPORT the full flow becomes elegant:
→ New process starts, binds the port
→ Old process catches SIGTERM
→ Drains connections, flushes logs
→ Exits cleanly

Zero downtime. Zero lost requests. Zero mystery gaps in your logs. 🐧

Continue reading →

🐄 The Thundering Herd Problem — when your server stampedes itself

Imagine 10 worker processes, all waiting on the same socket for new connections. One connection arrives — and the kernel wakes up ALL 10 workers at once.

😴 → ⚡ → 🐄🐄🐄🐄🐄🐄🐄🐄🐄🐄

Only one wins. The other 9 realize there’s nothing to do and go back to sleep. Pure CPU waste — and under burst traffic it gets ugly fast.

🔧 Fix #1 — Kernel 2.6:
Linux added an internal mutex to accept(). Only one process gets woken up. Better, but all workers still share one socket and one queue.

⚡ Fix #2 — SO_REUSEPORT (Kernel 3.9):
Each worker gets its own socket. The kernel decides who gets the connection before waking anyone up. No stampede, no wasted wake-ups.

💡 The herd finally learned to queue. 🐧

Continue reading →

Linux SO_REUSEPORT: The Secret Weapon for High-Performance Networking

Ever wondered how modern servers handle millions of connections without breaking a sweat? One underrated hero is the Linux socket option SO_REUSEPORT.

Introduced in Linux 3.9, it allows multiple sockets to bind to the same IP and port — enabling true kernel-level load balancing across workers, with no proxy layer needed.
Why it matters:

🔀 Kernel distributes connections across all bound sockets

⚡ Each worker gets connections directly — no bottleneck

🔄 New workers can bind before old ones shut down → zero-downtime restarts

Used by nginx, HAProxy, Node.js cluster, and high-frequency trading systems alike.
One socket option. Massive throughput gains. 🐧

Continue reading →

Subscribe to #systemsprogramming entries via RSS feed