A table that never remembers yesterday

Ask any DBA what a database is for, and they'll say: keeping data. Now imagine a MySQL table that does the opposite. You SELECTfrom it and get live statistics about every request your web application served in the last 15 minutes. Query it an hour later β€” that data is gone forever. Restart MySQL β€” everything vanishes, and nobody is upset.

That table is powered byPinba, and the amnesia is the whole point. Pinba is not a database; it's a monitoring server wearing a database costume. The costume matters: instead of learning yet another query DSL, you inspect your production traffic with plain SQL:

SELECT script_name, req_count, req_time_total, req_time_percentFROM pinba.report_by_script_nameORDER BY req_time_total DESCLIMIT 5;
Five lines, and you know which pages are eating your servers alive β€” right now, not in yesterday's logs.

Born inside a dating site

The story starts at Badoo, one of the largest dating platforms of the late 2000s. Hundreds of PHP servers, dozens of deploys a week, and a monitoring stack (Zabbix and friends) that could tell you a server was unhappy but never which piece of code made it unhappy.

The first in-house version was built by Andrey Nigmatulin β€” the same engineer who gave the PHP world PHP-FPM. Then Antony Dovgal, a long-time PHP core developer known as tony2001, took the idea further: he rebuilt the collector as a MySQL storage engine plugin and open-sourced it in 2009. The name is a manifesto in itself: PHP Is Not A Bottleneck Anymore.

The design decisions made back then look almost provocatively simple, and they are the reason the tool survived its own abandonment:1. Metrics travel over UDP, fire-and-forget.At the end of every request, the PHP process serializes its stats into a tiny Protocol Buffers packet and throws it at the Pinba server. No connection, no handshake, no waiting for an answer. If the packet gets lost β€” so be it; you're aggregating millions of them, and a 0.1% loss changes nothing. If the monitoring server is down, your website doesn't even notice. Monitoring that can take production down with it is worse than no monitoring at all β€” Pinba is architecturally incapable of that.2. Everything lives in RAM, in circular buffers.At startup, the engine pre-allocates fixed-size pools β€” by default one million request slots, plus pools for timers. New records overwrite the oldest ones, like a dashcam recording over old footage. No disk I/O, no malloc churn under load, no "monitoring DB ran out of disk at 3 a.m." incidents. Memory usage is flat and predictable forever.3. Reports are computed on the fly, inside the engine. Aggregations β€” requests per second per script, per host, per virtual host, percentiles β€” are maintained incrementally as packets arrive. Querying report_by_script_name doesn't scan a million rows; the answer is already sitting in memory.

The result is a monitoring server with near-zero overhead on the application, near-zero operational burden, and an interface every backend developer already knows: SQL.

The abandonment

Real commits to the upstream repository stopped around 2016. In 2018 the repo got its last update β€” a link pointing to an experimental successor. And that was it.

Meanwhile, the ground kept moving. MySQL 5.7 gave way to 8.0, then 8.4 LTS. The storage engine plugin API β€” a deeply internal, version-locked interface β€” changed under the project's feet. The original code wouldn't even compile against a modern server. Companies that had built their operational muscle memory around Pinba faced a nasty choice: freeze production databases on a MySQL from the mid-2010s, or lose the tool entirely.

The original repository still has nearly 500 stars β€” and until recently, it pointed all of them at build instructions that no longer worked.

The revival

I'm a backend engineer with twenty years in the industry, and Pinba is part of my daily toolkit β€” my company has been watching dozens of production sites through it for over a decade. In 2024 I decided that "it doesn't compile anymore" was not an acceptable end for a tool this good, and forked it.

The first plan was modest: patch the engine back to health on MySQL 5.6 and 5.7. It worked β€” and immediately felt wrong. Nobody should run MySQL 5.7 in 2025; I'd be restoring a museum piece. So the goal grew more ambitious: make Pinba build and run on every database I'd actually deploy today β€” MySQL 8.0 and 8.4 LTS, and MariaDB 10.11 and 11.8 LTS β€” on the operating systems I personally use: Ubuntu 24.04 and 26.04, Fedora 43 and 44, AlmaLinux 9 and 10.

That meant dragging a 2009 codebase, written against 2010-era MySQL internals, into the modern world:

  • Build system:autoconf gave way to CMake, and the code now compiles as C++23.
  • Protobuf stack:the engine had drifted into a strange museum of vendored protobuf artifacts. It now cleanly uses the same C++ protobuf runtime that MySQL itself ships, with the wire format β€” untouched since 2015 β€” verified byte-for-byte.
  • One source tree, five databases:MySQL 8.0 and 8.4 plugins are not ABI-compatible with each other (let alone with MariaDB), so the project builds and tests separate artifacts for each, from a single codebase.
  • Modern guardrails:clang-format, clang-tidy and cppcheck on every PR; AddressSanitizer and UndefinedBehaviorSanitizer runs; code coverage; CodeQL security scanning; automated releases.

A confession that is also a brag: this pace would have been impossible without AI-assisted development. Modernizing threading code, protobuf handling and memory management written for a fifteen-year-old MySQL is exactly the kind of work where an AI pair programmer that never gets bored of reading handler.h earns its keep. The project and the models grew up together β€” every few months the tooling got smarter, and so did the roadmap. Eighty-nine pull requests landed in the engine in the first four months of the public revival.

The bug that waited 15 years for MariaDB

Here's my favorite part, because it shows why "it works in production" and "it's correct" are different statements.

When MariaDB support landed, the server started crashing on shutdown β€” but only sometimes, and only under MariaDB. MySQL builds of the same code had run for fifteen years without a hiccup.

The culprit was in the engine's thread pool. When creating its job queue, the code allocated a sentinel node for the free list β€” and never initialized its next pointer. If the queue was never used, the destructor would happily walk off that node into garbage memory. So why had it never crashed before? Because glibc's malloc tends to hand back freshly zeroed pages for early allocations, so next just happened to be NULL. MySQL's startup allocation pattern preserved that accident. MariaDB's didn't.

Fifteen years of production uptime, courtesy of undefined behavior landing on the lucky side of a coin flip.

And it wasn't alone. Once the modern tooling was switched on, the skeletons lined up politely:

  • LeakSanitizercaught a slow memory leak in the report map: overwriting an existing key leaked a freshly duplicated string β€” a path that production traffic hits routinely.
  • CodeQLflagged unchecked- snprintfarithmetic building report identifiers β€” the kind of buffer-length math that works until the day someone creates a table with unusually long tag names.
  • A null-pointer guard, a misused reallocpattern, precision loss in histogram output β€” each one small, each one ancient, each one now covered by a test.

None of these were exotic. They were simply invisible until someone pointed 2026-grade tooling at 2009-grade code. If you maintain an old C codebase and haven't run it under sanitizers yet β€” I have news for you, and you may not like it.

Monitoring the monitoring: releases on autopilot

The last piece of the revival isn't code β€” it's logistics. A storage engine plugin must match the exact server version it loads into, and MySQL ships patch releases on its own schedule. Chasing that by hand is a part-time job nobody wants.

So the fork watches for you: a scheduled CI workflow monitors upstream MySQL releases; when a new patch version appears, it updates the build matrix, opens a pull request, and β€” once checks pass β€” new packages flow out automatically: multi-arch Docker images (x86_64 and ARM, smoke-tested before publishing), DEB packages in an Ubuntu PPA, and RPM packages in Fedora COPR for Fedora and Enterprise Linux. The Docker images alone are pulled about 3,100 times a month.

A tool built to watch production is now itself watched by robots. There's a certain justice in that.

Try it in one minute

docker run -d --name pinba \ -e MYSQL_ROOT_PASSWORD=secret \ -p 3306:3306 -p 30002:30002/udp \ xolegator/pinba-engine:8.4
Point the Pinba PHP extension at port 30002 (that's Part 2 of this series), and start SELECT-ing your production traffic.

  • Engine sources: github.com/XOlegator/pinba_engine
  • Docker images: hub.docker.com/r/xolegator/pinba-engine
  • Ubuntu PPA: ppa:xolegator/packagesΒ· Fedora/EL RPMs: COPR xolegator/pinba

Next in the series: the PHP extension that instruments your app with two lines of php.ini β€” and the use-after-free bug that only surfaced after rebuilding PHP itself with AddressSanitizer.