<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Laravel Performance and Operations on Brian Faust</title><link>https://vacua.dev/series/laravel-performance-and-operations/</link><description>Recent content in Laravel Performance and Operations on Brian Faust</description><generator>Hugo</generator><language>en-US</language><lastBuildDate>Mon, 01 Jan 0001 00:00:00 +0000</lastBuildDate><atom:link href="https://vacua.dev/series/laravel-performance-and-operations/rss.xml" rel="self" type="application/rss+xml"/><item><title>A Cache Hit Is Not a Freshness Guarantee</title><link>https://vacua.dev/a-cache-hit-is-not-a-freshness-guarantee/</link><pubDate>Tue, 01 Apr 2025 00:00:00 +0000</pubDate><guid>https://vacua.dev/a-cache-hit-is-not-a-freshness-guarantee/</guid><description>&lt;p&gt;A catalogue price is cached at revision 1. The database commits revision 2.
Three seconds later, Laravel finds the key and returns revision 1 from inside
its declared five-second fresh window.&lt;/p&gt;
&lt;p&gt;The cache hit is real. So is the stale price.&lt;/p&gt;
&lt;p&gt;In the &lt;a href="https://vacua.dev/reproductions/lar-06/"&gt;LAR-06 fixture&lt;/a&gt;
, the cached JSON is well formed and its
SHA-256 hash matches its body. Nothing has expired or become corrupt. The
failure is semantic: &lt;code&gt;current-price&lt;/code&gt; requires the acknowledged source
revision, while the cache only knows that a key still contains bytes.&lt;/p&gt;</description></item><item><title>Octane Removes Repeated Boot and Adds State</title><link>https://vacua.dev/octane-removes-repeated-boot-and-adds-state/</link><pubDate>Sat, 01 Mar 2025 00:00:00 +0000</pubDate><guid>https://vacua.dev/octane-removes-repeated-boot-and-adds-state/</guid><description>&lt;p&gt;A catalogue endpoint returns the same 249-byte JSON response from two Laravel
runtimes. One boots a fresh CLI process for every request. The other sends
every request through one Octane RoadRunner worker.&lt;/p&gt;
&lt;p&gt;In the &lt;a href="https://vacua.dev/reproductions/lar-05/"&gt;LAR-05 fixture&lt;/a&gt;
, their local median latencies at
concurrency one are 43.2 ms and 1.3 ms. That is the tempting part of Octane.&lt;/p&gt;
&lt;p&gt;Now send tenant A and then tenant B through the Octane worker. A mutable
singleton returns tenant A twice. A singleton holding the configuration
repository remembers tenant A twice. A singleton created with Laravel&amp;rsquo;s
bootstrap request never sees either tenant.&lt;/p&gt;</description></item><item><title>Eager Loading Is Not a Query Shape</title><link>https://vacua.dev/eager-loading-is-not-a-query-shape/</link><pubDate>Sat, 01 Feb 2025 00:00:00 +0000</pubDate><guid>https://vacua.dev/eager-loading-is-not-a-query-shape/</guid><description>&lt;p&gt;A catalogue endpoint returns 25 names and the number of active variants behind
each one. The first implementation runs 26 queries. Adding
&lt;code&gt;with('activeVariants')&lt;/code&gt; reduces that to two.&lt;/p&gt;
&lt;p&gt;The N+1 defect is gone. The new implementation still hydrates 1,250 &lt;code&gt;Variant&lt;/code&gt;
models, including a synthetic 4 KiB metadata column, to produce 25 integers.&lt;/p&gt;
&lt;p&gt;Eager loading solved the repeated-query shape. It did not ask what the response
needed. That is the larger decision: queries, selected rows and columns,
hydrated models, serialization, and the database plan all belong to one read
path.&lt;/p&gt;</description></item><item><title>A Worker Count Is Not a Throughput Plan</title><link>https://vacua.dev/a-worker-count-is-not-a-throughput-plan/</link><pubDate>Wed, 01 Jan 2025 00:00:00 +0000</pubDate><guid>https://vacua.dev/a-worker-count-is-not-a-throughput-plan/</guid><description>&lt;p&gt;Four Laravel queue workers complete 114 waiting jobs per second where one
worker completes 37. Keep the worker count at four, make every job update the
same database row, and the rate falls to 32. Make each job fail twice before
succeeding, and those four workers perform 540 attempts to produce 180 effects.&lt;/p&gt;
&lt;p&gt;Nothing is wrong with the worker setting. It is answering three different
workloads.&lt;/p&gt;
&lt;p&gt;A worker count describes available executors. It says nothing about whether a
job can run in parallel, which dependency must absorb the concurrency, or how
many attempts are needed for one business effect. A throughput plan needs those
parts too.&lt;/p&gt;</description></item><item><title>Cache the Deployment, Not the Assumption</title><link>https://vacua.dev/cache-the-deployment-not-the-assumption/</link><pubDate>Sun, 01 Dec 2024 00:00:00 +0000</pubDate><guid>https://vacua.dev/cache-the-deployment-not-the-assumption/</guid><description>&lt;p&gt;Release B serves &lt;code&gt;/inventory&lt;/code&gt; and labels its catalogue &lt;code&gt;green&lt;/code&gt;. The code is
correct. The environment is correct. Yet the application answers &lt;code&gt;/catalogue&lt;/code&gt;
with &lt;code&gt;blue&lt;/code&gt;, while &lt;code&gt;/inventory&lt;/code&gt; returns &lt;code&gt;404&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Nothing corrupted Laravel&amp;rsquo;s generated files. They contain release A&amp;rsquo;s
configuration and route table because release A created them. The deployment
combined those files with release B and produced a system which is consistently
wrong.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;config:cache&lt;/code&gt; and &lt;code&gt;route:cache&lt;/code&gt; are often introduced as performance switches.
That description misses their more important property: each command turns a
set of inputs into a generated artifact. If the artifact is deployed, it must
carry the same release identity as the code and environment which produced it.&lt;/p&gt;</description></item><item><title>A Slow Request Is Not a Capacity Plan</title><link>https://vacua.dev/a-slow-request-is-not-a-capacity-plan/</link><pubDate>Fri, 01 Nov 2024 00:00:00 +0000</pubDate><guid>https://vacua.dev/a-slow-request-is-not-a-capacity-plan/</guid><description>&lt;p&gt;Five request profiles return the same catalogue data. A sixth path accepts a
background rebuild.&lt;/p&gt;
&lt;p&gt;One profile is the control. One repeats six database calls. One recomputes a
value behind a unique cache key. One spends its time hashing in PHP. One uses a
warm cache and stays close to the control. The rebuild responds &lt;code&gt;202 Accepted&lt;/code&gt;
quickly while ten jobs wait behind one worker.&lt;/p&gt;
&lt;p&gt;“The application is slow” fits the three deliberately expensive catalogue
paths. “Work is delayed” fits the queue. “Add another server” addresses only
one of them, and even there it is a hypothesis.&lt;/p&gt;</description></item></channel></rss>