<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: BuildOpsy</title>
    <description>The latest articles on DEV Community by BuildOpsy (@buildopsy).</description>
    <link>https://dev.to/buildopsy</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4167148%2F450cc842-1067-483a-b2ca-2425ddb63fe1.png</url>
      <title>DEV Community: BuildOpsy</title>
      <link>https://dev.to/buildopsy</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/buildopsy"/>
    <language>en</language>
    <item>
      <title>The three meters behind a $65 million observability bill</title>
      <dc:creator>BuildOpsy</dc:creator>
      <pubDate>Thu, 29 Oct 2026 03:59:18 +0000</pubDate>
      <link>https://dev.to/buildopsy/the-three-meters-behind-a-65-million-observability-bill-42dd</link>
      <guid>https://dev.to/buildopsy/the-three-meters-behind-a-65-million-observability-bill-42dd</guid>
      <description>&lt;p&gt;$65 million is a strange number to hear on an earnings call. It gets stranger when you work out what it buys at list price.&lt;/p&gt;

&lt;p&gt;On 4 May 2023, Datadog reported first-quarter billings of $511 million, up 15% year over year, and flagged one exception: a large upfront bill for a single customer in Q1 2022 that did not recur at the same level or timing. A JPMorgan analyst, Mark Murphy, did the subtraction on the call and arrived at roughly $65 million. Datadog's CFO, David Obstler, confirmed the shape without the name: a crypto company, an early optimizer, now billed in smaller chunks.&lt;/p&gt;

&lt;p&gt;A week later, Gergely Orosz reported that current and former Coinbase engineers had confirmed to him that the $65 million was Coinbase's Datadog bill for 2021, settled in the first quarter of 2022. Coinbase has never confirmed it publicly. Datadog never named the customer. Everything below rests on that sourcing.&lt;/p&gt;

&lt;p&gt;The number is worth taking seriously rather than gawking at, because it is not really a story about one company overspending. It is a story about three meters that compound independently, and what happens when nobody owns any of them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmm0oxxwkb25x4ig8uz2p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmm0oxxwkb25x4ig8uz2p.png" alt="Datadog observability bill waterfall: host fleet, log ingest, and indexed events combine into the monthly meter, with Coinbase's $65M 2021 bill as the scale check" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Three meters, three units, three growth curves
&lt;/h2&gt;

&lt;p&gt;Observability vendors do not sell one meter. At Datadog, the ones that stack are these, at the annual-billing list rates published today:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Meter&lt;/th&gt;
&lt;th&gt;List price&lt;/th&gt;
&lt;th&gt;What grows it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Infrastructure, Enterprise&lt;/td&gt;
&lt;td&gt;$23 per host per month&lt;/td&gt;
&lt;td&gt;Fleet size&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Log ingest&lt;/td&gt;
&lt;td&gt;$0.10 per GB ingested&lt;/td&gt;
&lt;td&gt;Verbosity × volume&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Standard indexing, 15-day retention&lt;/td&gt;
&lt;td&gt;$1.70 per million events&lt;/td&gt;
&lt;td&gt;Event volume × retention&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;APM&lt;/td&gt;
&lt;td&gt;$31 per host per month, with infrastructure attached&lt;/td&gt;
&lt;td&gt;Services × hosts&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;On-demand rates are higher across the board: $27 per host, $2.55 per million indexed events, $36 per APM host.&lt;/p&gt;

&lt;p&gt;The compounding is the point. Hosts grow with the fleet. Ingested gigabytes grow with log verbosity, and retention pressure multiplies whatever you ship. Indexed events grow with cardinality, and custom metrics beyond the per-host allotment — 100 on Pro, 200 on Enterprise — bill by usage, so every new tag value on a custom metric mints billable series. A team that adds hosts, turns up log verbosity, and tags everything has tripled its bill three independent ways before writing a single alert.&lt;/p&gt;

&lt;h2&gt;
  
  
  What $65 million means in each unit
&lt;/h2&gt;

&lt;p&gt;At $23 per host per month, $65 million is about 235,000 host-years of Enterprise infrastructure monitoring. That is arithmetic on list price, not a claim about what Coinbase bought, but it is the right order of magnitude for a company running a large fleet at the peak of a growth phase.&lt;/p&gt;

&lt;p&gt;The small-scale version is more useful. Twenty hosts at $23 comes to $460 a month. A thousand gigabytes of ingest at $0.10 adds $100. That is $560 a month, or $6,720 a year — the default output of a cost calculator I built for exactly this, and a modeled figure rather than anyone's real invoice.&lt;/p&gt;

&lt;p&gt;Divide one into the other: $65 million is roughly 9,700 years of that model.&lt;/p&gt;

&lt;p&gt;The gap between $6,720 and $65,000,000 is not a pricing scandal. It is what four orders of magnitude of fleet growth, log verbosity, and cardinality look like when no individual decision was ever expensive enough to argue about.&lt;/p&gt;

&lt;h2&gt;
  
  
  How 2021 produced that number
&lt;/h2&gt;

&lt;p&gt;Coinbase went public in 2021 and had a year most companies never see. Infrastructure cost was not a constraint anyone enforced, because revenue covered everything. AWS, Snowflake, and Datadog all grew. A bill that size is what three meters running at startup speed produce when nobody is assigned to watch them.&lt;/p&gt;

&lt;p&gt;Then the market turned. Revenue fell hard through 2022, and the bills that growth had hidden became the agenda. Datadog's CEO described the pattern on the same call: a customer whose business got cut to a third or a quarter gets its contract restructured, because the vendor would rather be part of the solution than part of the problem. The customer stayed, on chunked billing matched to a smaller business.&lt;/p&gt;

&lt;p&gt;One thing to be careful about: an anonymous commenter claimed the $65 million covered a multi-year prepayment. Orosz could not verify that and neither can anyone else, so it stays out. What is documented is the single-year bill, the settlement quarter, and the renegotiation.&lt;/p&gt;

&lt;h2&gt;
  
  
  The build-vs-buy line, stated properly
&lt;/h2&gt;

&lt;p&gt;Coinbase's response was to price the alternative: a dedicated observability team with a mandate to move onto Grafana, Prometheus, and ClickHouse, built for control and ownership first, with cost as the second motive once the market turned. They double-wrote the new stack for months before cutting anything over.&lt;/p&gt;

&lt;p&gt;The arithmetic that mattered is a single inequality:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;infrastructure cost + platform team cost &amp;lt; vendor bill
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Orosz's version: ten senior and staff engineers in the Bay Area cost under $5 million a year, with infrastructure on top in the low double-digit millions. Against $65 million, building is not a close call. Against a well-managed $2 million bill, it is a way to hire a team whose job is maintaining dashboards.&lt;/p&gt;

&lt;p&gt;His rule of thumb for where the question becomes real is $2–5 million a year in vendor spend — roughly where a team of four or five engineers plus a manager, at $150–400K fully loaded each, starts to pencil against the invoice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The breakeven moves with the invoice.&lt;/strong&gt; Which is why the invoice has to be measured before the decision is made, not after someone sees a headline and asks for a migration plan.&lt;/p&gt;

&lt;h2&gt;
  
  
  They didn't leave
&lt;/h2&gt;

&lt;p&gt;The part the invoice-watchers usually miss: with the replacement stack validated and the plug ready to pull, Coinbase got terms it could accept and stayed. The customer is still on Datadog.&lt;/p&gt;

&lt;p&gt;That is not a contradiction. Deep integrations — custom tracing, per-service instrumentation, dashboards wired into every team's workflow — make migration expensive in a way the license math never captures. That lock-in cut both ways: it gave Coinbase leverage for a steep discount, and it gave Datadog a retained logo.&lt;/p&gt;

&lt;p&gt;The lesson is uncomfortable for both sides of the table. Price your exit before you threaten it, or the threat is theater.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four things to do on Monday
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Budget tag cardinality like headcount.&lt;/strong&gt; Every new tag value on a custom metric mints series, and series beyond the per-host allotment bill by usage. Audit which teams emit the most series per host before approving more hosts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tier retention by access pattern.&lt;/strong&gt; Logs you investigate regularly earn standard indexing with alerting. Everything else belongs in a cheaper tier or an archive with rehydration on demand. Retention is the quietest multiplier on the invoice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Decide sampling as a cost input.&lt;/strong&gt; Full-fidelity tracing on every request is a second invoice hiding inside the first. Pick the sampling rate when you design the tracing, not when you are debugging.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Alert on ingest slope, not the total.&lt;/strong&gt; Gigabytes per day times price per gigabyte, tracked against the monthly commit, turns the bill into a metric the on-call actually owns. A meter nobody watches is a meter without a limit.&lt;/p&gt;

&lt;p&gt;And the cheapest one of all: assign the bill to the teams that generate it. Chargeback turns "observability is expensive" into named owners with named levers. Coinbase assigned a whole team to the problem. Most companies only need to assign the invoice.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this doesn't establish
&lt;/h2&gt;

&lt;p&gt;The identification rests on Orosz's reporting, not on a Coinbase or Datadog statement. Neither company has confirmed the customer. The $65 million covers one year of spend, not a multi-year contract that anyone has verified.&lt;/p&gt;

&lt;p&gt;Coinbase's current spend is undisclosed, so there is no way to check how much the renegotiation actually saved, or whether the in-house stack would have been cheaper over five years.&lt;/p&gt;

&lt;p&gt;The list prices are Datadog's published rates as of October 2026 and will not match any negotiated contract, including the one in this story. And no vendor published utilisation figures, so the three-meter model here describes the structure of the bill, not its actual composition.&lt;/p&gt;

&lt;p&gt;Three clocks ran in this story: the meters compounded through 2021, the invoice came due in Q1 2022, and the renegotiation reset the economics after that. Treating any one of them as "the Datadog incident" misses how metered billing actually fails — slowly, across independent units, and only discovered when growth stops covering it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://blog.pragmaticengineer.com/datadog-65m-year-customer-mystery/" rel="noopener noreferrer"&gt;The Pragmatic Engineer: Datadog's $65M/year customer mystery solved&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://thenewstack.io/datadogs-65m-bill-and-why-developers-should-care/" rel="noopener noreferrer"&gt;The New Stack: Datadog's $65M bill and why developers should care&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.datadoghq.com/pricing/" rel="noopener noreferrer"&gt;Datadog pricing&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devops</category>
      <category>sre</category>
      <category>cloud</category>
      <category>observability</category>
    </item>
    <item>
      <title>A cancelled query can destroy your database connection</title>
      <dc:creator>BuildOpsy</dc:creator>
      <pubDate>Thu, 29 Oct 2026 03:37:59 +0000</pubDate>
      <link>https://dev.to/buildopsy/a-cancelled-query-can-destroy-your-database-connection-6ea</link>
      <guid>https://dev.to/buildopsy/a-cancelled-query-can-destroy-your-database-connection-6ea</guid>
      <description>&lt;p&gt;Two short deadlines took down Inngest's database path twice in one afternoon, and neither was misconfigured. They expired exactly on time. That is what made the failure so effective.&lt;/p&gt;

&lt;p&gt;On 10 October 2026, both of Inngest's connection poolers lost every connection to their primary database at 14:00 UTC. They lost them again at 15:30. Run scheduling and queue processing stalled for most accounts in both windows, 14:00–14:48 and 15:30–16:25, and the mitigation that followed caused intermittent delays until 04:17 the next morning.&lt;/p&gt;

&lt;p&gt;The report is worth reading because the mechanism generalises to any stack that puts a connection pooler with a wait queue between the application and Postgres.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1n3zyv7ykgezzfm07veu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1n3zyv7ykgezzfm07veu.png" alt="Component schematic: a client row with an expired deadline, a PgBouncer waiting queue, a gate admitting one new connection at a time, and a rack of connection slots marked closed rather than returned to the pool" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The sequence, in four steps
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;A frequently used lookup carried a short deadline.&lt;/li&gt;
&lt;li&gt;The deadline expired while its query was still queued inside PgBouncer, waiting for a database connection. The pgx driver cancelled the query and closed its client connection.&lt;/li&gt;
&lt;li&gt;PgBouncer had nothing running to cancel, because the query had never been handed a server connection. It later ran the abandoned query on a database connection.&lt;/li&gt;
&lt;li&gt;It then closed that connection entirely instead of returning it to the pool.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Step 4 is the whole failure. The client's timeout cost the system a database connection, not a request, and the cost landed after the client had stopped watching. No single request looked expensive. Individually, every one of these is a wasted query. Collectively they are a pool that empties itself.&lt;/p&gt;

&lt;p&gt;Two amplifiers arrived at the same moment. Both collapses began at the top and bottom of the hour, when cron-scheduled work roughly triples the query rate, and a newly rolled out set of lookups was failing in large numbers, adding cancellations to a pool that was already busy. Inngest reproduced the behaviour in a test environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why restarting and resizing both failed
&lt;/h2&gt;

&lt;p&gt;Restarting the poolers did not reliably recover them. Raising the pool size did not help either. The report is explicit on why: opening connections was the bottleneck, not the pool limit.&lt;/p&gt;

&lt;p&gt;Each pooler opens new database connections one at a time. So the refill path is serial while the destruction path is parallel. As a steady-state sketch rather than a model of this incident, if usable connections are destroyed at &lt;code&gt;d&lt;/code&gt; per second and a pooler needs &lt;code&gt;t&lt;/code&gt; seconds on average to establish a replacement, the refill ceiling is &lt;code&gt;1/t&lt;/code&gt; and the pool drains whenever:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;d &amp;gt; 1/t
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The sketch assumes constant destruction and handshake times, and it ignores the point at which the pooler stops trying. Inngest published neither number, so treat this as the shape of the constraint rather than a measurement of it. To see the shape: at 100 ms per connection a single pooler refills at ten per second; at 50 ms, twenty.&lt;/p&gt;

&lt;p&gt;The second collapse ended by changing paths, not by growing the pool. The team moved all services onto poolers run alongside the database by the database provider, and processing recovered between 16:10 and 16:25.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix that caused a second incident
&lt;/h2&gt;

&lt;p&gt;The new route reached the database through NAT gateways, and some connections through those gateways were silently dropped. Queries on a dropped connection hung for up to 16 minutes before failing, which stalled processing intermittently and most visibly hit cron functions. Customers reported delays at 03:01 on 11 October; the affected service moved back to the original poolers at 04:17, and gateway routing was improved on 12 October.&lt;/p&gt;

&lt;p&gt;Sixteen minutes of silent hang is worse than a fast failure for a scheduler. A connection that errors invites a retry. One that simply stops answering consumes a worker slot and a deadline, and you learn about it from a customer. A failover drill that measures successful requests will not catch this. One that measures how long a hung connection stays hung will.&lt;/p&gt;

&lt;h2&gt;
  
  
  The contract that broke underneath it
&lt;/h2&gt;

&lt;p&gt;The part of the report with the longest reach is not about pools. Internal failures counted against functions' configured retries, and a step that failed permanently on an internal error left its run in &lt;code&gt;Running&lt;/code&gt; instead of marking it failed. &lt;code&gt;onFailure&lt;/code&gt; handlers did not fire. Functions waiting on that run through &lt;code&gt;step.invoke&lt;/code&gt; were not notified.&lt;/p&gt;

&lt;p&gt;That is a broken guarantee, not an availability number. A durable execution engine sells the promise that every run reaches a terminal state and that failure handlers run. Under this bug the engine could hold a run in &lt;code&gt;Running&lt;/code&gt; indefinitely, after spending the retry budget it was given — the customer's allowance consumed by the platform's own faults, and the transition that would have reported it never sent.&lt;/p&gt;

&lt;p&gt;The recovery instructions confirm the state of those runs: they will not continue on their own, and customers can cancel and replay them, with the caveat that a replay starts from the beginning, so completed steps run again. Anyone with non-idempotent side effects in a completed step had a decision the platform could not make for them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four things to check in your own stack
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Alert on net connection drain, not pool utilisation.&lt;/strong&gt; Graph opens and closes per pooler per second, and alert when the close rate exceeds the open rate for a sustained interval. A pool at 30% with a negative slope is in more danger than one at 70% that is stable, and utilisation alone shows neither.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bound the wait, not just the query.&lt;/strong&gt; PgBouncer's &lt;code&gt;query_wait_timeout&lt;/code&gt; disconnects a client whose query has not been assigned a server within the limit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="c"&gt;; pgbouncer.ini — the ordering matters more than the value
&lt;/span&gt;&lt;span class="py"&gt;query_wait_timeout&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;5    ; must sit below your client's own deadline&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The point of setting it below the client deadline is to remove the queued entry before the client gives up. That is the reasoning, not proof that it preserves backend connections. Inngest describes the pooler running the abandoned query and then closing that connection; an independent load test from July 2025 puts the waste a step earlier, with the pooler finishing a login for a client that has already gone and discarding the fresh backend connection. The same engineer reported that lowering the timeout helped without being enough on its own. Test it against your driver's real cancellation path before adopting it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Know what your driver does on cancel.&lt;/strong&gt; In pgx v5, the thread reports replacing the default cancellation behaviour with a context watcher handler that sets a connection deadline instead of closing immediately, and describes the effect in their load test as large. If your code path cancels queued queries, this is the boundary to inspect.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fence the lease before you force a terminal state.&lt;/strong&gt; If you mark a run failed because its step lease expired without a heartbeat, late writes from a worker that is still running can land afterwards, and a replay produces a second copy of the side effects. Mark it failed only when the engine can reject writes from the old lease, or when the step is confirmed not to be in flight.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the report does not establish
&lt;/h2&gt;

&lt;p&gt;Inngest disclosed no connection counts, handshake timings, pool sizes or customer totals, so the arithmetic here is the report's timestamps and nothing more. The 44-minute, 48-minute and 55-minute windows are my subtraction on its UTC boundaries, not figures it published.&lt;/p&gt;

&lt;p&gt;The report also links pgx issue 1780 as the known interaction behind the collapse. I read that issue: it was opened on 26 October 2023 and was still open at its latest comment in February 2026. The upstream record offers a compatible description of the boundary and no confirmed root cause, so Inngest's account is a well-documented instance of an unresolved interaction rather than evidence that it is fixed.&lt;/p&gt;

&lt;p&gt;The structural change is the one to watch. Inngest is consolidating onto a single data centre acting as one cell, which removes the cross-data-centre failure class behind the 9 October incident. That trades one failure shape for another: fewer paths that can break, and more consequence when one does.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.inngest.com/blog/2026-10-15-incident-report-for-october-9-and-10-2026" rel="noopener noreferrer"&gt;Inngest: Incident report for October 9 and 10, 2026&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jackc/pgx/issues/1780" rel="noopener noreferrer"&gt;jackc/pgx issue 1780&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks for reading :)&lt;/p&gt;

</description>
      <category>database</category>
      <category>postgres</category>
      <category>sre</category>
      <category>architecture</category>
    </item>
    <item>
      <title>OpenRouter's February 2026 Outages: How a Cold Cache Overwhelmed the Database</title>
      <dc:creator>BuildOpsy</dc:creator>
      <pubDate>Sun, 25 Oct 2026 11:38:07 +0000</pubDate>
      <link>https://dev.to/buildopsy/openrouters-february-2026-outages-how-a-cold-cache-overwhelmed-the-database-3in5</link>
      <guid>https://dev.to/buildopsy/openrouters-february-2026-outages-how-a-cold-cache-overwhelmed-the-database-3in5</guid>
      <description>&lt;p&gt;The database was struggling. The API said the user didn't exist.&lt;/p&gt;

&lt;p&gt;That is the part of OpenRouter's February outages worth looking at closely.&lt;/p&gt;

&lt;p&gt;The reported failure involved cache connectivity and the database lookups behind API-key authentication. When the cache stopped serving those lookups, the database had to absorb work it normally wouldn't see. Requests started failing with 401 User not found.&lt;/p&gt;

&lt;p&gt;There is an important distinction here: an authentication failure and an authentication infrastructure failure are not the same thing. If a database timeout becomes a 401, a client may treat a temporary service failure as an invalid credential. Retries, alerts and incident diagnosis can all head in the wrong direction.&lt;/p&gt;

&lt;p&gt;The capacity problem is just as interesting. A database can appear comfortably provisioned because a cache absorbs most of the read traffic. That doesn't mean the database can survive the workload when the cache disappears. Recovery can create its own traffic spike, precisely when the dependency underneath is least able to handle it.&lt;/p&gt;

&lt;p&gt;This is why cache-failure testing shouldn't stop at checking whether the application stays online. It should also examine database concurrency, fallback request rates, cache repopulation, retry behavior and the errors returned to clients. A fallback path that works for a few misses may behave very differently when every request takes it at once.&lt;/p&gt;

&lt;p&gt;There is also a subtle observability problem here. The first outage coincided with a denial-of-service attack, which initially complicated the investigation. When the caching failure recurred in February without the same accompanying attack, the underlying cause became clearer. Multiple problems can overlap; temporal correlation alone doesn't establish root cause.&lt;/p&gt;

&lt;p&gt;The remediation needs to address both load and semantics. Circuit breakers and controlled cache repopulation can limit the burst reaching the database. Returning 503 Service Unavailable rather than 401 Unauthorized when an infrastructure failure prevents an authentication lookup helps clients distinguish an unavailable service from invalid credentials.&lt;/p&gt;

&lt;p&gt;A useful test for any cache-backed authentication system: invalidate the cache under realistic concurrency and measure the database's behavior during recovery. How quickly does load rise? Are concurrent misses coalesced? Do retries amplify the spike? Can the service shed work before the database saturates?&lt;/p&gt;

&lt;p&gt;Those answers reveal more about the system's failure tolerance than its cache-hit ratio during normal operation.&lt;/p&gt;

&lt;p&gt;The full postmortem reconstructs both outages, separates published facts from illustrative capacity calculations, and identifies what the available evidence does—and doesn't—establish about the remediation.&lt;/p&gt;

&lt;p&gt;Read teh full teardown here: &lt;a href="https://buildopsy.com/system-design/openrouter-cache-thundering-herd-postmortem.html" rel="noopener noreferrer"&gt;https://buildopsy.com/system-design/openrouter-cache-thundering-herd-postmortem.html&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>productivity</category>
      <category>architecture</category>
    </item>
    <item>
      <title>🚀Rocket thrusters on a car</title>
      <dc:creator>BuildOpsy</dc:creator>
      <pubDate>Sun, 18 Oct 2026 10:05:09 +0000</pubDate>
      <link>https://dev.to/buildopsy/rocket-thrusters-on-a-car-57e6</link>
      <guid>https://dev.to/buildopsy/rocket-thrusters-on-a-car-57e6</guid>
      <description>&lt;p&gt;Tesla promised rocket thrusters on a car. Naturally, the next step is to ask the engineers what the physics has to say about that.&lt;/p&gt;

&lt;p&gt;Marketing has a wonderful relationship with the future. Engineering has a slightly less romantic relationship with mass, thrust, pressure, and the laws of physics.&lt;/p&gt;

&lt;p&gt;So we audited the Tesla Roadster’s thruster claims as an engineering problem, not a headline-writing competition.&lt;/p&gt;

&lt;p&gt;What’s theoretically possible? What has actually been demonstrated? What does the available evidence support, and where does the gap between a claim and a verified capability begin?&lt;/p&gt;

&lt;p&gt;Because there’s a rather important difference between a technology that &lt;em&gt;could work in principle&lt;/em&gt; and one that has been demonstrated to work as advertised.&lt;/p&gt;

&lt;p&gt;We went through the claims, examined the evidence, and separated the physics from the promises.&lt;/p&gt;

&lt;p&gt;Turns out, “the laws of physics don’t forbid it” and “the product can actually do it” are two very different engineering milestones.&lt;/p&gt;

&lt;p&gt;A useful reminder that even when your company builds rockets, the burden of proof doesn’t magically disappear when you put the word &lt;em&gt;Tesla&lt;/em&gt; on the slide.&lt;/p&gt;

&lt;p&gt;Full engineering audit: &lt;a href="https://buildopsy.com/system-design/tesla-roadster-thruster-claims-audit.html" rel="noopener noreferrer"&gt;https://buildopsy.com/system-design/tesla-roadster-thruster-claims-audit.html&lt;/a&gt;&lt;/p&gt;

</description>
      <category>distributedsystems</category>
      <category>webdev</category>
      <category>programming</category>
      <category>software</category>
    </item>
    <item>
      <title>When Go GC Burns 60% CPU: The Container Memory Limit Problem</title>
      <dc:creator>BuildOpsy</dc:creator>
      <pubDate>Thu, 15 Oct 2026 18:27:31 +0000</pubDate>
      <link>https://dev.to/buildopsy/when-go-gc-burns-60-cpu-the-container-memory-limit-problem-5g2d</link>
      <guid>https://dev.to/buildopsy/when-go-gc-burns-60-cpu-the-container-memory-limit-problem-5g2d</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnb9n11d0g7ox8cn256yl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnb9n11d0g7ox8cn256yl.png" alt="A technical illustration of Go garbage collection pacing against container memory limits" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Banner: Go garbage collection, memory pacing, and the container limits that can make a difference.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A Go service can burn CPU collecting garbage without having a memory leak.&lt;/p&gt;

&lt;p&gt;That is the counterintuitive part of Zomato's Go garbage collection story.&lt;/p&gt;

&lt;p&gt;The reported symptoms were striking: garbage collection consuming 25–60% of CPU in some services, alongside containers getting OOM-killed without an obvious memory leak.&lt;/p&gt;

&lt;p&gt;These look like separate problems. But they can share a common cause: &lt;strong&gt;the garbage collector's pacing decisions don't automatically account for the container's actual memory budget.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem with doubling the live heap
&lt;/h2&gt;

&lt;p&gt;Go's garbage collector uses &lt;code&gt;GOGC&lt;/code&gt; to control when the next collection should happen.&lt;/p&gt;

&lt;p&gt;With the default &lt;code&gt;GOGC=100&lt;/code&gt;, the GC target is roughly based on the live heap growing by 100% relative to the previous cycle, with additional accounting for GC roots and other implementation details.&lt;/p&gt;

&lt;p&gt;In simplified terms:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Next GC target ≈ live heap × (1 + GOGC/100)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At &lt;code&gt;GOGC=100&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Live heap:       500 MiB
Approx. target: 1,000 MiB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That target is reasonable as a starting point when the process has enough memory available.&lt;/p&gt;

&lt;p&gt;But containers introduce another constraint: &lt;strong&gt;the process has a memory budget imposed from outside the Go runtime.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The GC's heap-growth target and the container's memory ceiling are two different things.&lt;/p&gt;

&lt;p&gt;When they aren't coordinated, two failure modes become possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure mode 1: OOM kills without a leak
&lt;/h2&gt;

&lt;p&gt;Suppose a container has a memory limit of 1 GiB.&lt;/p&gt;

&lt;p&gt;The Go process has a live heap of 600 MiB. A simplified &lt;code&gt;GOGC=100&lt;/code&gt; calculation puts the next target around 1,200 MiB.&lt;/p&gt;

&lt;p&gt;But the container cannot safely accommodate that much memory. The process also needs room for goroutine stacks, runtime metadata, native allocations, and other memory outside the live heap.&lt;/p&gt;

&lt;p&gt;The container can hit its limit before the collector's normal heap target becomes appropriate.&lt;/p&gt;

&lt;p&gt;The result can be an OOM kill even when reachable memory isn't growing uncontrollably.&lt;/p&gt;

&lt;p&gt;That doesn't mean every OOM without an obvious leak is a GC pacing issue. Transient allocation spikes, native memory, and other causes still need investigation.&lt;/p&gt;

&lt;p&gt;The important point is that a heap-growth target alone isn't a container memory budget.&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure mode 2: Excessive GC CPU
&lt;/h2&gt;

&lt;p&gt;Now consider the opposite situation.&lt;/p&gt;

&lt;p&gt;A service has a relatively small live heap and plenty of memory available in its container.&lt;/p&gt;

&lt;p&gt;The GC keeps using a target derived from that live heap. Depending on the allocation rate and workload, it may collect frequently even though the container has significant unused headroom.&lt;/p&gt;

&lt;p&gt;Every collection consumes CPU.&lt;/p&gt;

&lt;p&gt;At fleet scale, unnecessary GC work can become a meaningful compute cost.&lt;/p&gt;

&lt;p&gt;This is why the following combination deserves attention:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;High GC CPU.&lt;/li&gt;
&lt;li&gt;A relatively flat live-heap graph.&lt;/li&gt;
&lt;li&gt;No convincing evidence of sustained heap growth.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is a diagnostic clue, not proof. A service can allocate and discard large volumes of temporary objects while maintaining a flat live heap. Allocation rate, GC frequency, and allocation profiles matter too.&lt;/p&gt;

&lt;p&gt;But before treating every high-GC-CPU incident as a memory leak, inspect the collector's pacing and the container's actual memory constraints.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix: Give Go a realistic memory limit
&lt;/h2&gt;

&lt;p&gt;Go provides &lt;code&gt;GOMEMLIMIT&lt;/code&gt; to let applications communicate a soft memory limit to the runtime.&lt;/p&gt;

&lt;p&gt;Unlike &lt;code&gt;GOGC&lt;/code&gt;, which controls GC aggressiveness in relation to heap growth, &lt;code&gt;GOMEMLIMIT&lt;/code&gt; helps the runtime account for its overall memory footprint when deciding how aggressively to collect.&lt;/p&gt;

&lt;p&gt;Conceptually, the runtime must balance two competing costs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Collect too late, and the process risks exhausting its container memory budget.&lt;/li&gt;
&lt;li&gt;Collect too aggressively, and the application wastes CPU reclaiming memory it could have used safely.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A memory limit gives the runtime another constraint to consider.&lt;/p&gt;

&lt;p&gt;In the Zomato case, the reported approach used container metadata to derive a per-container limit, refreshed it periodically, and combined memory tuning with CPU scheduling adjustments.&lt;/p&gt;

&lt;p&gt;A limit around 90% of the container ceiling can be a starting point for investigation, &lt;strong&gt;not a universal safe value&lt;/strong&gt;. The correct margin depends on the application's non-heap memory, traffic spikes, allocation behavior, and operational requirements.&lt;/p&gt;

&lt;p&gt;The goal is to give the runtime a realistic budget while preserving enough headroom for the rest of the process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why &lt;code&gt;GOMEMLIMIT&lt;/code&gt; isn't a hard wall
&lt;/h2&gt;

&lt;p&gt;This distinction matters in production.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;GOMEMLIMIT&lt;/code&gt; is a soft limit, not a strict process-memory cap. The runtime can exceed it when necessary, and it does not guarantee that a container will avoid OOM kills.&lt;/p&gt;

&lt;p&gt;The runtime also has to balance memory pressure against GC CPU overhead.&lt;/p&gt;

&lt;p&gt;If the limit is too high, it may offer little protection against container memory pressure. If it is too low, the runtime can spend excessive CPU collecting garbage in an attempt to stay within the target.&lt;/p&gt;

&lt;p&gt;That is why blindly setting &lt;code&gt;GOMEMLIMIT&lt;/code&gt; equal to the container limit is not a complete solution.&lt;/p&gt;

&lt;p&gt;A production rollout should account for:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The container's actual memory limit.&lt;/li&gt;
&lt;li&gt;Heap and non-heap memory usage.&lt;/li&gt;
&lt;li&gt;Peak allocation rates and traffic bursts.&lt;/li&gt;
&lt;li&gt;GC CPU and pause behavior.&lt;/li&gt;
&lt;li&gt;OOM events and memory headroom after deployment.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Start with representative workloads, observe the results, and tune based on measurements rather than assuming a single multiplier works everywhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't overlook `
&lt;/h2&gt;

</description>
      <category>go</category>
      <category>performance</category>
      <category>systemdesign</category>
      <category>sre</category>
    </item>
    <item>
      <title>CircleCI's Runners Were Ready. PostgreSQL Couldn't Dispatch Jobs.</title>
      <dc:creator>BuildOpsy</dc:creator>
      <pubDate>Thu, 15 Oct 2026 16:20:37 +0000</pubDate>
      <link>https://dev.to/buildopsy/the-database-was-the-healthiest-part-of-circlecis-fleet-it-still-took-down-3500-jobs-2fi5</link>
      <guid>https://dev.to/buildopsy/the-database-was-the-healthiest-part-of-circlecis-fleet-it-still-took-down-3500-jobs-2fi5</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbs0n0nvqu1xkij8hruc0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbs0n0nvqu1xkij8hruc0.png" alt="CircleCI outage architecture: PostgreSQL WAL saturation blocked job dispatch" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Architecture diagram by BuildOpsy: How PostgreSQL WAL saturation blocked CircleCI's orchestration database, leaving healthy runners idle and thousands of job starts affected.&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  CircleCI's Runners Were Fine. PostgreSQL Wasn't.
&lt;/h1&gt;

&lt;p&gt;On October 6, 2026, CircleCI hit a failure that had nothing to do with runner capacity.&lt;/p&gt;

&lt;p&gt;The culprit was routine PostgreSQL maintenance.&lt;/p&gt;

&lt;p&gt;An anti-wraparound autovacuum ran against large tables, generating write-ahead log (WAL) faster than the database could archive it. The WAL volume filled, and the orchestration database went offline for 50 minutes.&lt;/p&gt;

&lt;p&gt;The numbers tell the story:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;~3,500 job starts failed&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;50 minutes of database unavailability&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hours of recovery work after the database returned&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's the underlying systems problem.&lt;/p&gt;

&lt;p&gt;Let &lt;code&gt;g&lt;/code&gt; be the WAL generation rate and &lt;code&gt;a&lt;/code&gt; the archival rate.&lt;/p&gt;

&lt;p&gt;When &lt;code&gt;g &amp;gt; a&lt;/code&gt;, unarchived WAL accumulates. Free disk space only determines how long the system can tolerate that imbalance.&lt;/p&gt;

&lt;p&gt;A rough estimate of the remaining time is:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Time to saturation ≈ Free WAL space / (g − a)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This assumes relatively stable rates, but the principle is important: a disk-full alert often arrives too late. By the time it fires, the system may already be out of runway.&lt;/p&gt;

&lt;p&gt;The architectural impact was bigger than a database outage.&lt;/p&gt;

&lt;p&gt;CircleCI's orchestration database sat on the critical path for workflow state, job dispatch, UI updates, and notifications. The runners could be healthy and ready to execute, but without the control plane coordinating work, they couldn't help.&lt;/p&gt;

&lt;p&gt;Then came the second problem: recovery.&lt;/p&gt;

&lt;p&gt;Restoring database availability didn't instantly clear the backlog. Queued jobs still needed to be processed and dispatched, putting additional pressure on the system as it recovered.&lt;/p&gt;

&lt;p&gt;This is the part of incident response that's easy to underestimate: &lt;strong&gt;restoring service and restoring normal operation are two different problems.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The safeguards I'd take from this incident:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Monitor WAL generation versus archival throughput.&lt;/strong&gt; Alert on the rate gap, archive lag, and estimated time to saturation, not just disk utilization.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test autovacuum under realistic workloads.&lt;/strong&gt; Large tables and production-level write activity can expose bottlenecks that smaller test environments miss.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Protect the recovery path.&lt;/strong&gt; Make sure WAL archival and database recovery have enough capacity and clear operational guardrails.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Control backlog redrain.&lt;/strong&gt; Use bounded concurrency, retry limits, and staggered dispatch to avoid overwhelming downstream services.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Design for control-plane failures.&lt;/strong&gt; A healthy execution fleet is useless if the system responsible for scheduling work is unavailable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The deeper lesson is that a healthy execution fleet doesn't guarantee a healthy CI/CD system. The control plane that decides what runs can be the real bottleneck.&lt;/p&gt;

&lt;p&gt;I broke down the failure mechanism, incident timeline, recovery bottleneck, and architectural trade-offs in the full postmortem:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://buildopsy.com/system-design/circleci-wal-autovacuum-postmortem.html" rel="noopener noreferrer"&gt;CircleCI Outage Postmortem: Full WAL Saturation Blocked 3,500 Job Starts&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Question for PostgreSQL engineers:&lt;/strong&gt; Do you monitor WAL generation rate against archival throughput and estimate time-to-full, or are your alerts still mostly based on disk utilization?&lt;/p&gt;

&lt;p&gt;I'd be interested to hear what works in production. Thanks&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>devops</category>
      <category>sre</category>
      <category>software</category>
    </item>
    <item>
      <title>CircleCI’s runners were ready. The database couldn’t tell them what to run.</title>
      <dc:creator>BuildOpsy</dc:creator>
      <pubDate>Mon, 12 Oct 2026 18:08:05 +0000</pubDate>
      <link>https://dev.to/buildopsy/circlecis-runners-were-ready-the-database-couldnt-tell-them-what-to-run-3i0b</link>
      <guid>https://dev.to/buildopsy/circlecis-runners-were-ready-the-database-couldnt-tell-them-what-to-run-3i0b</guid>
      <description>&lt;p&gt;Around 3,500 job starts failed because PostgreSQL’s WAL generation outpaced archival during routine anti-wraparound autovacuum. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F83yic2g6dm43qdgfx85b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F83yic2g6dm43qdgfx85b.png" alt="The control path in the incident: pipeline state and job dispatch depended on one orchestration database and its write-ahead log." width="356" height="201"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The dedicated log volume filled, and CircleCI’s orchestration database went offline for 50 minutes. A maintenance operation had turned into a developer-feedback outage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How Routine Vacuum Work Filled the WAL Disk&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;PostgreSQL autovacuum is routine maintenance, not an optional background nicety. CircleCI's report says an anti-wraparound autovacuum ran on several of its largest tables. On a very large table the process reads every page and produces a large volume of transaction logs. Around 12:40 UTC, this maintenance output plus normal application writes exceeded the rate at which the database could archive and reclaim the dedicated log volume.&lt;/p&gt;

&lt;p&gt;The failure was a rate mismatch, not simply a database that had “too little disk.” If WAL is produced at rate g and archived at rate a, free space falls whenever g &amp;gt; a. Disk headroom buys time, but does not correct the mismatch. CircleCI did not publish those rates or the volume size, so the inequality explains the mechanism without pretending to reconstruct its capacity numbers.&lt;/p&gt;

&lt;p&gt;Alerts began showing brief orchestration errors at 10:02. They cleared within about 15 minutes and looked similar to alerts responders had seen before. As the WAL volume filled, slowdowns became intermittent around 13:30, then continuous by 15:30. The incident was declared at 14:54. CircleCI's retrospective says the noisy, short-lived alerts and the absence of direct alerts on WAL volume and archive lag delayed recognition of the combined failure.&lt;/p&gt;

&lt;p&gt;The more interesting part? Bringing the database back wasn’t enough. The storage change cut processing speed to roughly half its normal rate, and the backlog kept hurting customers for hours. It’s a good reminder that database recovery and service recovery are two very different things.&lt;/p&gt;

&lt;p&gt;We broke down the failure mechanism, recovery timeline, architectural bottleneck, and the safeguards worth adding.&lt;/p&gt;

&lt;p&gt;Full postmortem: &lt;a href="https://buildopsy.com/system-design/circleci-wal-autovacuum-postmortem.html" rel="noopener noreferrer"&gt;https://buildopsy.com/system-design/circleci-wal-autovacuum-postmortem.html&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>productivity</category>
      <category>devops</category>
      <category>distributedsystems</category>
    </item>
    <item>
      <title>How fast Starlink is when it is up. as India debates entry, latency is the number that decides whether satellite broadband can carry gaming, calls, and real-time trading at all

https://buildopsy.com/system-design/starlink-latency-whitepaper-teardown.html</title>
      <dc:creator>BuildOpsy</dc:creator>
      <pubDate>Mon, 12 Oct 2026 04:01:26 +0000</pubDate>
      <link>https://dev.to/buildopsy/how-fast-starlink-is-when-it-is-up-as-india-debates-entry-latency-is-the-number-that-decides-19he</link>
      <guid>https://dev.to/buildopsy/how-fast-starlink-is-when-it-is-up-as-india-debates-entry-latency-is-the-number-that-decides-19he</guid>
      <description>&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://buildopsy.com/system-design/starlink-latency-whitepaper-teardown.html" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbuildopsy.com%2Fimages%2Farticles%2Fstarlink-latency-meme.png" height="420" class="m-0" width="800"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://buildopsy.com/system-design/starlink-latency-whitepaper-teardown.html" rel="noopener noreferrer" class="c-link"&gt;
            Starlink Latency, Priced: How SpaceX Cut Peak Median From 48.5ms Toward 20ms | Buildopsy
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Physics allows under 10ms. SpaceX measured 48.5ms. The 15.5ms gap was buffers, queues, WiFi, and ground layout — and they priced every millisecond.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbuildopsy.com%2Fimages%2Ffavicon.png" width="457" height="412"&gt;
          buildopsy.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


</description>
      <category>networking</category>
      <category>performance</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>A small search win: Google's AI Overview cited my Figma multiplayer teardown</title>
      <dc:creator>BuildOpsy</dc:creator>
      <pubDate>Tue, 06 Oct 2026 18:49:32 +0000</pubDate>
      <link>https://dev.to/buildopsy/a-small-search-win-googles-ai-overview-cited-my-figma-multiplayer-teardown-1639</link>
      <guid>https://dev.to/buildopsy/a-small-search-win-googles-ai-overview-cited-my-figma-multiplayer-teardown-1639</guid>
      <description>&lt;p&gt;I write about production system design on Buildopsy. Recently, I published a teardown of Figma’s multiplayer architecture: how edits move between browsers and servers, how conflicts get resolved, and where the system’s scaling costs come from.&lt;/p&gt;

&lt;p&gt;I later searched for “how Figma’s multiplayer technology works.” Figma’s own engineering post appeared in the results. In the AI Overview below it, I noticed two Buildopsy citations beside explanations of optimistic rendering and the server’s role in ordering edits.&lt;/p&gt;

&lt;h2&gt;
  
  
  What appeared in the result
&lt;/h2&gt;

&lt;p&gt;I saved a screenshot because I didn’t expect to see my article cited there. It’s one result for one query—not proof of a lasting ranking or a traffic increase.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsdcmlvput9gqt4t2h8if.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsdcmlvput9gqt4t2h8if.png" alt="Google AI Overview citing Buildopsy for Figma multiplayer details" width="800" height="1423"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The interesting part is how edits get reconciled
&lt;/h2&gt;

&lt;p&gt;A browser applies an edit immediately, then sends the operation over a WebSocket to the process that owns that document. The owner orders the change and broadcasts the accepted version to the other clients.&lt;/p&gt;

&lt;p&gt;The conflict rule is more specific than “the server wins.” In the architecture described in my teardown, the server orders operations, same-property conflicts use last-writer-wins, and tree changes get an additional validation step to prevent cycles. That’s a product-specific trade-off: a design canvas can tolerate some conflicts that would be painful in a collaborative text editor.&lt;/p&gt;

&lt;p&gt;The Rust rewrite addressed another boundary. A slow document could stall unrelated documents sharing a TypeScript worker. Figma moved document operations into a Rust child process while Node.js kept handling the network layer. Figma reported that serialization became more than 10× faster in its &lt;a href="https://www.figma.com/blog/rust-in-production-at-figma/" rel="noopener noreferrer"&gt;engineering write-up&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The numbers make the next question more interesting. In one scenario, 1.5 million concurrent users sending two coalesced cursor frames per second produce &lt;strong&gt;3 million ephemeral frames per second&lt;/strong&gt;. That’s modeled math, not a Figma disclosure. The design question is: which events need durable ordering, and which can be dropped when they’re stale?&lt;/p&gt;

&lt;p&gt;The full teardown works through that boundary, a 65k-RPS scenario, and a modeled monthly cost range. The cost figures are my assumptions, not Figma’s reported spend: &lt;a href="https://buildopsy.com/system-design/figma-system-design.html" rel="noopener noreferrer"&gt;Read the Figma multiplayer teardown&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I don’t know whether the citation will persist or bring readers. It was still encouraging to see a technical explanation appear alongside the primary source it discusses.&lt;/p&gt;

</description>
      <category>figma</category>
      <category>systemdesign</category>
      <category>architecture</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
