Luther Monson, Staff Software Engineer at SUSE

Luther Monson

Go by way of PHP · Containers · VMs · Windows · Kubernetes

Clustered SQLite in ePHPm, Part 3: CDC Over the Cluster Channel

ePHPm ships Turso's change data capture stream between nodes over one authenticated, multiplexed TCP connection. This covers how that works and the two failover bugs that showed up when the e2e suite killed the primary.

15-Minute Read

Two ePHPm nodes connected by gossip on UDP 7946, the KV data plane on TCP 7947 and the cluster channel on TCP 7948 carrying cdc, snapshot and sql streams

By the end of Part 2 ePHPm v0.7.0 ran exactly one SQLite engine in process. That was Turso, for single-node and clustered mode alike. The sidecar was gone. What I skipped over is how a replica actually gets the primary’s writes, and what happens when the primary dies with data on it. That’s this post.

Security and Performance Hardening of a Rust Database Proxy

What a week of hardening litewire turned up. A dependency had a memory bug, one site could open its neighbor's database, a security rule lived only in a comment and every query paid for a needless 1 MB clear.

11-Minute Read

Bar chart of point SELECT throughput over the litewire MySQL frontend at three revisions: before the command filter, with the filter, and with the poll_read fix

litewire is the piece of ePHPm that lets PHP apps talk MySQL to a SQLite file. I wrote about how it works and about the MySQL quirks WordPress depends on. This one is the less visible half of August:

What Does One More WordPress Site Cost?

I measured what each extra WordPress tenant on one ePHPm instance costs in RAM, file descriptors, CPU and disk. The sweep ran from 10 to 1000 sites.

9-Minute Read

Line chart of ePHPm steady RSS against tenant site count: linear at about 2.2 MB per site with every site open, flat past the default LRU cap of 256

ePHPm v0.7.0 shipped per-site multi-tenancy. Point [server] sites_dir at a directory and every Host maps to its own docroot and its own Turso database file. One process serves all of them. The first question anybody asks about that is “how many sites fit on a box?” The honest way to answer it is to turn the question around. What does one more site cost? If you know the marginal RAM, file descriptors, CPU and disk for a tenant, the box size falls out of arithmetic.

Clustered SQLite in ePHPm, Part 2: Adding Turso

How I decided a beta database engine was ready by testing it instead of reading its version number. ePHPm v0.7.0 replaces both rusqlite and sqld with Turso.

9-Minute Read

Bar chart of four SQLite lanes at 16 concurrent connections: rusqlite and Turso single node, sqld and Turso CDC clustered

Part 1 ended with ePHPm running two SQLite engines. rusqlite ran in-process for a single node, and an embedded sqld child process ran for a cluster. The sidecar fell off a cliff past four concurrent writers. The best fix I could ship was a semaphore set to 1. This post is about replacing both engines with one, and about how I decided that engine was ready.

Rolling Your Own KV Store Instead of Embedding Redis

Why ePHPm has its own Redis-compatible cache built in instead of running Redis. It also covers a bug that hid in it for four months and how it compares to real Redis on the same machine.

11-Minute Read

Bar chart of redis-benchmark GET and SET throughput against Redis 7.4, ePHPm v0.5.2 and ePHPm v0.2.0

Every PHP deployment I’ve run eventually grew a Redis. Sessions, the WordPress object cache, Laravel’s cache and locks, rate limiting. ePHPm is supposed to be one binary that replaces nginx + php-fpm + Redis, so it needed a cache in the box. I didn’t want to write one. This post covers why I did anyway and how it’s built. It also covers the bug that sat in its eviction code from March until last month. Then I compare it against a stock Redis on the same machine with…

Faking MySQL's Quirks Well Enough to Fool WordPress

Passing WordPress's install tests didn't mean litewire behaved like MySQL. This is the second round of fixes, for the small MySQL behaviors that WordPress and Laravel quietly depend on.

10-Minute Read

A table of MySQL behaviors WordPress relies on, with what litewire answered before and after the August fixes

Last month I wrote about running SQLite behind a MySQL socket with litewire. I ended on “passing an install suite is not the same thing as behaving like MySQL”. That turned out to be accurate. On August 10th a few things happened at once:

Database Connection Pooling with Smart Reset

PHP opens a new MySQL connection on every request, but ePHPm reuses them and cleans up only the ones that need it. Here's how that works, two bugs it had and how much load it takes off MySQL.

10-Minute Read

PHP request connecting to the ePHPm MySQL proxy, which lends it a pooled backend connection and resets it only if the session was dirtied

PHP’s share-nothing model is one of its best properties. It has one expensive side effect: a request can’t keep a database connection for the next request. php-fpm gives you PDO::ATTR_PERSISTENT and mysqli’s p: hosts. But those are per-worker-process. PDO’s keep whatever session state the last request left behind. mysqli at least resets the session with COM_CHANGE_USER on reuse. And almost nobody turns them on. So in practice every PHP request does a TCP connect, a MySQL…

Clustered SQLite in ePHPm, Part 1: Replication by Sidecar

ePHPm first made SQLite highly available by embedding sqld as a child process. This is how that worked and the list of things that went wrong with it.

9-Minute Read

Two ePHPm nodes, each running an embedded sqld child process, connected by gossip, a KV data plane and sqld gRPC

Most PHP sites I have run in the last twenty years did not need a MySQL server. They needed a database, and MySQL was the one every framework spoke. With ePHPm I have been trying to get rid of that server for the common case. The piece that makes it possible is litewire. PHP connects with plain pdo_mysql. litewire speaks the MySQL wire protocol, and the queries land in a SQLite file. WordPress and Laravel don’t know the difference. No mysqld, no child processes, just ephpm and a .db file.

SQLite as a Production Database via Protocol Translation

litewire speaks the MySQL wire protocol on one side and SQLite on the other. That lets WordPress and Laravel run against a .db file without a code change.

10-Minute Read

A PHP app connecting over the MySQL wire protocol on port 3306 to litewire, which translates the SQL and runs it against a SQLite file

Most of the PHP sites I’d put on ePHPm don’t need a MySQL server. A brochure WordPress, an internal Laravel tool, a preview environment that lives for an afternoon. What they do need is something that looks like MySQL, because mysqli and pdo_mysql are the only database drivers their code has ever been pointed at. I mentioned this in the ePHPm pragmatism post as a one-paragraph “because”. This is the longer version, now that WordPress 7.0 and Laravel 13 both install and…

Recent Posts

Categories

About

Luther is a 20+ year software veteran specializing in backend APIs and architecting products. Employed at SUSE as the Team Lead for the Containers on Windows Team working on Rancher + Kubernetes