Chaos is Not a Bug, It's a Feature: Introduction to Chaos Engineering in C++
\nImagine: your C++ service has been running for months without failures. Then, bam! — a disk failure, packet loss, or an unexpected SIGKILL. And that's it. The system crashes, users are furious, and you're frantically searching for a bug in the logs. Sound familiar?
We are used to testing software in ideal conditions: a stable network, a fast disk, unlimited memory. But reality is harsh. This is precisely where Chaos Engineering comes to the rescue — a discipline that teaches us to intentionally break the system to make it stronger. And today we'll talk about how to apply this approach to C++ applications.
\nChaos Engineering for C++ is not about "launching a chaos monkey and praying." It's about controlled experiments that reveal weaknesses before users find them. We'll learn to inject memory failures, emulate slow I/O, and break network connections — all using pure C++ code.
\n\nWhy C++ Especially Needs Chaos Engineering?
\nThe C++ language gives the developer immense power: manual memory management, low-level resource access, no garbage collector. But this power is a double-edged sword. An error in delete[], a leak in an RAII wrapper, or an incorrect reinterpret_cast can turn a stable system into a fragile house of cards.
Traditional unit testing rarely checks how code will behave under:
\n- \n
- Insufficient RAM (
std::bad_alloc) \n - Unexpected closure of a file descriptor \n
- Network delays (latency spikes) \n
- Data corruption in shared memory \n
Chaos Engineering for C++ fills these gaps. We don't just write tests — we provoke the system into errors and see if it survives.
\n\nToolkit: How to Introduce Chaos into C++ Code
\nTo conduct experiments, we need Fault Injection tools. In C++, we can operate at different levels:
\n- \n
- At the code level: intercepting system calls via LD_PRELOAD or
dlsym\n - At the library level: replacing standard allocators and sockets \n
- At the OS level: using
ptraceor eBPF to interfere with process execution \n
We'll take the most practical path — write our own library for fault injection that can be easily integrated into any C++ project.
\n\nExample 1: Simulating a Memory Allocation Failure
\nOne of the most common problems in C++ is an unexpected std::bad_alloc. How will your code behave if new throws an exception? Let's check.
// chaos_memory.h\n#pragma once\n#include <new>\n#include <cstdlib>\n#include <atomic>\n#include <cstddef>\n\nclass ChaosAllocator {\npublic:\n // Set the failure probability (0.0 - 1.0)\n static void set_failure_probability(double prob) {\n failure_probability_ = prob;\n }\n\n static void* allocate(std::size_t size) {\n if (should_fail()) {\n throw std::bad_alloc();\n }\n return std::malloc(size);\n }\n\n static void deallocate(void* ptr) {\n std::free(ptr);\n }\n\nprivate:\n static bool should_fail() {\n // Simple random number generator for demonstration\n static std::atomic<unsigned int> seed = 42;\n seed = seed * 1103515245 + 12345;\n return (seed / 65536) % 100 < failure_probability_ * 100;\n }\n\n inline static double failure_probability_ = 0.0;\n};\nNow we can replace the standard allocator and check how our code handles memory failures:
\n// main.cpp\n#include <iostream>\n#include <vector>\n#include "chaos_memory.h"\n\n// Custom allocat