<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Performance on Luther Monson</title>
    <link>https://luth.io/tags/performance/</link>
    <description>Recent content in Performance on Luther Monson</description>
    <generator>Hugo</generator>
    <language>en</language>
    <lastBuildDate>Thu, 15 Oct 2026 19:45:00 -0700</lastBuildDate>
    <atom:link href="https://luth.io/tags/performance/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Fourth Place in Worker Mode, and Why It Wasn&#39;t PHP</title>
      <link>https://luth.io/blog/2026/10/ephpm-worker-mode-performance/</link>
      <pubDate>Thu, 15 Oct 2026 19:45:00 -0700</pubDate>
      <guid>https://luth.io/blog/2026/10/ephpm-worker-mode-performance/</guid>
      <description>&lt;p&gt;We added &lt;a href=&#34;https://github.com/ephpm/ephpm&#34;&gt;ePHPm&lt;/a&gt; to the&#xA;&lt;a href=&#34;https://github.com/Yii3-Benchmarks/app-api&#34;&gt;Yii3 runtime benchmark&lt;/a&gt;. It runs the same Yii3 API app on a bunch of PHP&#xA;runtimes and compares them. Our fork with the ePHPm runner is &lt;a href=&#34;https://github.com/ephpm/app-api&#34;&gt;ephpm/app-api&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Most of the results were good. ePHPm came first in classic mode and first on the database endpoint in both modes.&#xA;Classic mode is the PHP-FPM style, where every request starts fresh.&lt;/p&gt;&#xA;&lt;p&gt;Worker mode was the surprise. In worker mode the app boots once and stays in memory, like Octane or FrankenPHP&amp;rsquo;s&#xA;worker mode. We came fourth of ten at about 50.7k requests a second. Workerman (~65k) and Swoole (~62k) were ahead.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Kata Containers for CI: Boot Cost Isn&#39;t the Problem, File I/O Is</title>
      <link>https://luth.io/blog/2026/09/kata-containers-for-ci/</link>
      <pubDate>Thu, 03 Sep 2026 21:35:00 -0700</pubDate>
      <guid>https://luth.io/blog/2026/09/kata-containers-for-ci/</guid>
      <description>&lt;p&gt;Of the three platforms &lt;a href=&#34;https://luth.io/blog/2026/08/ephemerd-one-daemon-every-os/&#34;&gt;ephemerd&lt;/a&gt; runs jobs on, Linux has the weakest&#xA;isolation. Windows jobs get a Hyper-V isolated container with its own kernel. macOS jobs get a whole VM. A Linux job is&#xA;an ordinary runc container sharing the host kernel. A kernel escape from a Linux job is a host compromise.&#xA;&lt;a href=&#34;https://katacontainers.io/&#34;&gt;Kata Containers&lt;/a&gt; fixes that by putting each container in a lightweight VM with its own&#xA;kernel. It plugs into containerd as just another runtime handler. So I added it in&#xA;&lt;a href=&#34;https://github.com/ephpm/ephemerd/pull/155&#34;&gt;#155&lt;/a&gt;, measured it, and left it off by default. This post is why.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Security and Performance Hardening of a Rust Database Proxy</title>
      <link>https://luth.io/blog/2026/08/hardening-rust-database-proxy/</link>
      <pubDate>Thu, 27 Aug 2026 20:30:00 -0700</pubDate>
      <guid>https://luth.io/blog/2026/08/hardening-rust-database-proxy/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://github.com/ephpm/litewire&#34;&gt;litewire&lt;/a&gt; is the piece of ePHPm that lets PHP apps talk MySQL to a SQLite file. I&#xA;wrote about &lt;a href=&#34;https://luth.io/blog/2026/07/sqlite-production-database-protocol-translation/&#34;&gt;how it works&lt;/a&gt; and about &lt;a href=&#34;https://luth.io/blog/2026/08/faking-mysql-quirks-wordpress/&#34;&gt;the MySQL quirks&#xA;WordPress depends on&lt;/a&gt;. This one is the less visible half of August:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;an audit pass on litewire&lt;/li&gt;&#xA;&lt;li&gt;a multi-tenant mode that turned a database proxy into a security boundary&lt;/li&gt;&#xA;&lt;li&gt;a performance regression I shipped and then had to go find&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;It&amp;rsquo;s written in Rust. Memory safety is supposed to be the part I don&amp;rsquo;t have to think about, and it mostly is. The&#xA;use-after-free in this story was in a dependency. The worst bug wasn&amp;rsquo;t a memory bug at all.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Rolling Your Own KV Store Instead of Embedding Redis</title>
      <link>https://luth.io/blog/2026/08/rolling-your-own-kv-store/</link>
      <pubDate>Mon, 17 Aug 2026 20:44:00 -0700</pubDate>
      <guid>https://luth.io/blog/2026/08/rolling-your-own-kv-store/</guid>
      <description>&lt;p&gt;Every PHP deployment I&amp;rsquo;ve run eventually grew a Redis. Sessions, the WordPress object cache, Laravel&amp;rsquo;s cache and locks,&#xA;rate limiting. &lt;a href=&#34;https://github.com/ephpm/ephpm&#34;&gt;ePHPm&lt;/a&gt; is supposed to be one binary that replaces nginx + php-fpm +&#xA;Redis, so it needed a cache in the box. I didn&amp;rsquo;t want to write one. This post covers why I did anyway and how it&amp;rsquo;s&#xA;built. It also covers the bug that sat in its eviction code from March until last month. Then I compare it against a&#xA;stock Redis on the same machine with &lt;code&gt;redis-benchmark&lt;/code&gt; doing the talking.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Database Connection Pooling with Smart Reset</title>
      <link>https://luth.io/blog/2026/08/db-connection-pooling-smart-reset/</link>
      <pubDate>Sun, 09 Aug 2026 16:25:00 -0700</pubDate>
      <guid>https://luth.io/blog/2026/08/db-connection-pooling-smart-reset/</guid>
      <description>&lt;p&gt;PHP&amp;rsquo;s share-nothing model is one of its best properties. It has one expensive side effect: a request can&amp;rsquo;t keep a&#xA;database connection for the next request. php-fpm gives you &lt;code&gt;PDO::ATTR_PERSISTENT&lt;/code&gt; and &lt;code&gt;mysqli&lt;/code&gt;&amp;rsquo;s &lt;code&gt;p:&lt;/code&gt; hosts. But&#xA;those are per-worker-process. PDO&amp;rsquo;s keep whatever session state the last request left behind. mysqli at least resets&#xA;the session with &lt;code&gt;COM_CHANGE_USER&lt;/code&gt; on reuse. And almost nobody turns them on. So in practice every PHP request does a&#xA;TCP connect, a MySQL handshake and an auth exchange. Then it maybe sends some &lt;code&gt;SET&lt;/code&gt;s, runs its queries and sends&#xA;&lt;code&gt;COM_QUIT&lt;/code&gt;. Multiply that by your request rate and MySQL spends a real share of its time authenticating clients it&#xA;just said goodbye to.&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
