<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Benchmarks on tsvinc</title>
    <link>https://tsvinc.dev/tags/benchmarks/</link>
    <description>Recent content in Benchmarks on tsvinc</description>
    <generator>Hugo</generator>
    <language>en-us</language>
    <lastBuildDate>Tue, 07 Jul 2026 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://tsvinc.dev/tags/benchmarks/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>2492, 2495, 2508</title>
      <link>https://tsvinc.dev/posts/benchmark-measuring-orm/</link>
      <pubDate>Tue, 07 Jul 2026 00:00:00 +0000</pubDate>
      <guid>https://tsvinc.dev/posts/benchmark-measuring-orm/</guid>
      <description>&lt;p&gt;&lt;em&gt;Part 19 of &lt;a href=&#34;https://tsvinc.dev/series/micronaut-256mb/&#34;&gt;the Micronaut native image series&lt;/a&gt;. Follows &lt;a href=&#34;https://tsvinc.dev/posts/eight-connections-zero-available/&#34;&gt;Part 18&lt;/a&gt;, which chased a connection pool cancellation leak. Assumes familiarity with JDBC, R2DBC, and JPA/Hibernate.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-table-that-ended-the-argument&#34;&gt;The Table That Ended the Argument&lt;/h2&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Workload&lt;/th&gt;&#xA;          &lt;th&gt;Framework&lt;/th&gt;&#xA;          &lt;th&gt;rps&lt;/th&gt;&#xA;          &lt;th&gt;p95&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;jdbc-synth&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Quarkus&lt;/td&gt;&#xA;          &lt;td&gt;2508.4&lt;/td&gt;&#xA;          &lt;td&gt;1.2 ms&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;jdbc-synth&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Micronaut&lt;/td&gt;&#xA;          &lt;td&gt;2495.0&lt;/td&gt;&#xA;          &lt;td&gt;1.8 ms&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;jdbc-synth&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Spring Boot&lt;/td&gt;&#xA;          &lt;td&gt;2491.8&lt;/td&gt;&#xA;          &lt;td&gt;1.6 ms&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;r2dbc-synth&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Quarkus&lt;/td&gt;&#xA;          &lt;td&gt;2501.2&lt;/td&gt;&#xA;          &lt;td&gt;1.4 ms&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;r2dbc-synth&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Micronaut&lt;/td&gt;&#xA;          &lt;td&gt;2480.2&lt;/td&gt;&#xA;          &lt;td&gt;2.2 ms&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;r2dbc-synth&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Spring Boot&lt;/td&gt;&#xA;          &lt;td&gt;2478.7&lt;/td&gt;&#xA;          &lt;td&gt;2.3 ms&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;Native images at 256 MB. Same k6 load. Same Postgres. Zero errors across the board.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Eight Connections, Zero Available</title>
      <link>https://tsvinc.dev/posts/eight-connections-zero-available/</link>
      <pubDate>Tue, 07 Jul 2026 00:00:00 +0000</pubDate>
      <guid>https://tsvinc.dev/posts/eight-connections-zero-available/</guid>
      <description>&lt;p&gt;&lt;em&gt;Part 18 of &lt;a href=&#34;https://tsvinc.dev/series/micronaut-256mb/&#34;&gt;the Micronaut native image series&lt;/a&gt;. Follows &lt;a href=&#34;https://tsvinc.dev/posts/28-apis-same-throughput/&#34;&gt;Part 17&lt;/a&gt;, which found that reactive and imperative produced identical throughput on the 28-API workload. Assumes familiarity with R2DBC and Project Reactor.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;608-and-599&#34;&gt;60.8 and 59.9&lt;/h2&gt;&#xA;&lt;p&gt;Two frameworks. Different HTTP servers, different dependency trees, different teams maintaining them. Under load, both reactive stacks land at almost the same throughput:&lt;/p&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Framework&lt;/th&gt;&#xA;          &lt;th&gt;Model&lt;/th&gt;&#xA;          &lt;th&gt;Runtime&lt;/th&gt;&#xA;          &lt;th&gt;Mem&lt;/th&gt;&#xA;          &lt;th&gt;rps&lt;/th&gt;&#xA;          &lt;th&gt;p95&lt;/th&gt;&#xA;          &lt;th&gt;k6 errors&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Micronaut&lt;/td&gt;&#xA;          &lt;td&gt;Reactive&lt;/td&gt;&#xA;          &lt;td&gt;Native&lt;/td&gt;&#xA;          &lt;td&gt;128 MB&lt;/td&gt;&#xA;          &lt;td&gt;60.8&lt;/td&gt;&#xA;          &lt;td&gt;15000.6 ms&lt;/td&gt;&#xA;          &lt;td&gt;14.1%&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Spring Boot&lt;/td&gt;&#xA;          &lt;td&gt;Reactive&lt;/td&gt;&#xA;          &lt;td&gt;Native&lt;/td&gt;&#xA;          &lt;td&gt;128 MB&lt;/td&gt;&#xA;          &lt;td&gt;59.9&lt;/td&gt;&#xA;          &lt;td&gt;13548.7 ms&lt;/td&gt;&#xA;          &lt;td&gt;2.0%&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Micronaut&lt;/td&gt;&#xA;          &lt;td&gt;Imperative&lt;/td&gt;&#xA;          &lt;td&gt;Native&lt;/td&gt;&#xA;          &lt;td&gt;256 MB&lt;/td&gt;&#xA;          &lt;td&gt;251.9&lt;/td&gt;&#xA;          &lt;td&gt;2554.8 ms&lt;/td&gt;&#xA;          &lt;td&gt;0%&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Spring Boot&lt;/td&gt;&#xA;          &lt;td&gt;Imperative&lt;/td&gt;&#xA;          &lt;td&gt;Native&lt;/td&gt;&#xA;          &lt;td&gt;256 MB&lt;/td&gt;&#xA;          &lt;td&gt;220.6&lt;/td&gt;&#xA;          &lt;td&gt;2588.9 ms&lt;/td&gt;&#xA;          &lt;td&gt;0%&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Quarkus&lt;/td&gt;&#xA;          &lt;td&gt;Reactive&lt;/td&gt;&#xA;          &lt;td&gt;Native&lt;/td&gt;&#xA;          &lt;td&gt;256 MB&lt;/td&gt;&#xA;          &lt;td&gt;258.7&lt;/td&gt;&#xA;          &lt;td&gt;—&lt;/td&gt;&#xA;          &lt;td&gt;0%&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;Imperative hits 220–252. Quarkus reactive hits 259. Micronaut and Spring Boot reactive: 60.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Native Image Grew to 734 MB</title>
      <link>https://tsvinc.dev/posts/five-numbers-18-branches/</link>
      <pubDate>Tue, 07 Jul 2026 00:00:00 +0000</pubDate>
      <guid>https://tsvinc.dev/posts/five-numbers-18-branches/</guid>
      <description>&lt;p&gt;&lt;em&gt;Part 20 of &lt;a href=&#34;https://tsvinc.dev/series/micronaut-256mb/&#34;&gt;the Micronaut native image series&lt;/a&gt;. Field notes from running benchmark scenarios across 18 branches — three frameworks, three runtimes, four memory tiers. No narrative arc. Just five things that surprised me.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;1-native-is-not-automatically-small&#34;&gt;1. Native is not automatically small&lt;/h2&gt;&#xA;&lt;p&gt;The pitch: GraalVM native images are lean. Sub-100 MB footprint. Container-friendly. Cloud-native.&lt;/p&gt;&#xA;&lt;p&gt;The CRUD workload confirms this. Native RSS across all frameworks ranges from 31 to 86 MB. JVM containers running the same code: 351-643 MB. The marketing copy is accurate — for CRUD.&lt;/p&gt;</description>
    </item>
    <item>
      <title>16,700 Classes Before Your First Request</title>
      <link>https://tsvinc.dev/posts/quarkus-metaspace-floor-256mb/</link>
      <pubDate>Mon, 06 Jul 2026 00:00:00 +0000</pubDate>
      <guid>https://tsvinc.dev/posts/quarkus-metaspace-floor-256mb/</guid>
      <description>&lt;p&gt;&lt;em&gt;Part 24 of &lt;a href=&#34;https://tsvinc.dev/series/micronaut-256mb/&#34;&gt;the memory benchmark series&lt;/a&gt;. Assumes familiarity with JVM memory regions and container memory limits.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;96-at-idle&#34;&gt;96% at Idle&lt;/h2&gt;&#xA;&lt;p&gt;Quarkus JVM, jdbc-synth scenario, 256 MB container (&lt;code&gt;--memory=256m&lt;/code&gt;, swap disabled). The app starts. Prometheus reports heap usage: 37 MB used out of a 62 MB cap. Healthy. Normal. Unremarkable.&lt;/p&gt;&#xA;&lt;p&gt;Container RSS at idle: 246 MB.&lt;/p&gt;&#xA;&lt;p&gt;The first k6 ramp hits, allocates a few buffers, and the OOM killer fires. Twenty-six restarts. 42.8 req/s. 53% error rate. FAILED.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Every Route Registered. Every Request 404&#39;d.</title>
      <link>https://tsvinc.dev/posts/mono-zip-empty-404/</link>
      <pubDate>Mon, 06 Jul 2026 00:00:00 +0000</pubDate>
      <guid>https://tsvinc.dev/posts/mono-zip-empty-404/</guid>
      <description>&lt;p&gt;Thirty-three routes registered. Startup log confirms it. The &lt;code&gt;/routes&lt;/code&gt; management endpoint lists every one. And under load, every single request returns 404.&lt;/p&gt;&#xA;&lt;p&gt;This is the reactive Micronaut branch of the &lt;a href=&#34;https://tsvinc.dev/series/micronaut-256mb/&#34;&gt;space observatory benchmark&lt;/a&gt; — 28 upstream API calls, 10 HTTP clients, all pointed at a Go mock server that deliberately injects failures (HTTP 500, 504-after-delay, abrupt connection close). The imperative sibling branch works. The lighter synth variant works. This one: 100% 404.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Three Front Doors, One Engine</title>
      <link>https://tsvinc.dev/posts/three-front-doors-one-engine/</link>
      <pubDate>Sun, 05 Jul 2026 00:00:00 +0000</pubDate>
      <guid>https://tsvinc.dev/posts/three-front-doors-one-engine/</guid>
      <description>&lt;p&gt;&lt;em&gt;Part 22 of &lt;a href=&#34;https://tsvinc.dev/series/micronaut-256mb/&#34;&gt;the Micronaut native image series&lt;/a&gt;. Follows &lt;a href=&#34;https://tsvinc.dev/posts/two-ways-mistranslate-retry/&#34;&gt;Part 21&lt;/a&gt;, where a retry was mistranslated two ways. Assumes familiarity with reactive Java (Mono/Flux/Uni) and at least one of the three frameworks.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-experiment&#34;&gt;The experiment&lt;/h2&gt;&#xA;&lt;p&gt;Same app. Same Java 25. Same GraalVM CE 25. Same Postgres. Same Liquibase schema. Same benchmark harness. Three frameworks.&lt;/p&gt;&#xA;&lt;p&gt;The app fans out to 28 HTTP APIs concurrently, computes risk scores across seismic, atmospheric, and space weather data, and writes one row to Postgres. Six endpoints. The heaviest hits all 28 APIs in four parallel blocks. Built on Micronaut 5.0.0 with reactive R2DBC. Ported to Spring Boot 4.1 and Quarkus 3.33.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Quarkus 12,563 vs Spring 820 (Both Numbers Are Wrong)</title>
      <link>https://tsvinc.dev/posts/counter-reset-benchmark-lied/</link>
      <pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate>
      <guid>https://tsvinc.dev/posts/counter-reset-benchmark-lied/</guid>
      <description>&lt;p&gt;&lt;em&gt;Part 18 of &lt;a href=&#34;https://tsvinc.dev/series/micronaut-256mb/&#34;&gt;the Micronaut native image series&lt;/a&gt;. Follows &lt;a href=&#34;https://tsvinc.dev/posts/28-apis-same-throughput/&#34;&gt;Part 17&lt;/a&gt;, which found that reactive and imperative hit the same throughput ceiling. Assumes familiarity with Prometheus counters.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;two-numbers-that-cant-both-be-right&#34;&gt;Two numbers that can&amp;rsquo;t both be right&lt;/h2&gt;&#xA;&lt;p&gt;The framework comparison benchmark finished. Three frameworks, same app, same k6 profile, same 256 MB native image container. The analysis script printed:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Spring Boot:  HTTP served:  820 requests   avg=2.341ms&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Quarkus:      HTTP served:  12,563 requests  avg=1.892ms&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Quarkus crushes Spring by 15x. Write the blog post. Ship the chart.&lt;/p&gt;</description>
    </item>
    <item>
      <title>The Throughput Was Always 115</title>
      <link>https://tsvinc.dev/posts/28-apis-same-throughput/</link>
      <pubDate>Thu, 02 Jul 2026 00:00:00 +0000</pubDate>
      <guid>https://tsvinc.dev/posts/28-apis-same-throughput/</guid>
      <description>&lt;p&gt;&lt;em&gt;Part 17 of &lt;a href=&#34;https://tsvinc.dev/series/micronaut-256mb/&#34;&gt;the Micronaut native image series&lt;/a&gt;. Follows &lt;a href=&#34;https://tsvinc.dev/posts/designed-reactive-benchmark/&#34;&gt;Part 16&lt;/a&gt;, which compared reactive vs imperative on a lighter workload. Assumes familiarity with Project Reactor and virtual threads.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;115&#34;&gt;115&lt;/h2&gt;&#xA;&lt;p&gt;That&amp;rsquo;s the number. Requests per second. For all twelve scenarios.&lt;/p&gt;&#xA;&lt;p&gt;Reactive R2DBC on native: 115. Imperative JDBC on JVM: 115. Virtual threads with loom-carrier: 114.5. Reactive on JVM with 512 MB memory limit: 115.2. Imperative on JVM unlimited: 115.1.&lt;/p&gt;&#xA;&lt;p&gt;Twelve benchmark runs. Two codebases. Three runtime configurations. Six memory/container permutations each. 0% failure rate across the board. And the same number, over and over, like a metronome that doesn&amp;rsquo;t care what&amp;rsquo;s playing.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Designed a Reactive Benchmark. Imperative Won.</title>
      <link>https://tsvinc.dev/posts/designed-reactive-benchmark/</link>
      <pubDate>Tue, 30 Jun 2026 00:00:00 +0000</pubDate>
      <guid>https://tsvinc.dev/posts/designed-reactive-benchmark/</guid>
      <description>&lt;p&gt;&lt;em&gt;Part 16 of &lt;a href=&#34;https://tsvinc.dev/series/micronaut-256mb/&#34;&gt;the Micronaut native image series&lt;/a&gt;. Follows &lt;a href=&#34;https://tsvinc.dev/posts/seven-scenarios-one-table/&#34;&gt;Part 15&lt;/a&gt;, which benchmarked the reactive service alone. Assumes familiarity with Project Reactor and virtual threads.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-hypothesis&#34;&gt;The Hypothesis&lt;/h2&gt;&#xA;&lt;p&gt;The workload was designed to make reactive shine. Six endpoints exercising parallel fan-out, scatter-gather, bounded concurrency, and backpressure. A Go mock server injecting realistic latency jitter — ISS at 10-50ms with 3% failures, NEO at 30-150ms, connection resets via &lt;code&gt;Hijack()&lt;/code&gt; + &lt;code&gt;conn.Close()&lt;/code&gt;. k6 ramping to 200 VUs then sustaining 500 req/s constant arrival. If reactive Micronaut had a home-court advantage anywhere, it was here.&lt;/p&gt;</description>
    </item>
    <item>
      <title>265 req/s on All Three Runtimes (Until You Add a Memory Limit)</title>
      <link>https://tsvinc.dev/posts/seven-scenarios-one-table/</link>
      <pubDate>Sat, 27 Jun 2026 00:00:00 +0000</pubDate>
      <guid>https://tsvinc.dev/posts/seven-scenarios-one-table/</guid>
      <description>&lt;p&gt;&lt;em&gt;Part 15 of &lt;a href=&#34;https://tsvinc.dev/series/micronaut-256mb/&#34;&gt;the Micronaut native image series&lt;/a&gt;. Assumes familiarity with reactive Java (Mono/Flux) and container memory limits.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-draw&#34;&gt;The Draw&lt;/h2&gt;&#xA;&lt;p&gt;Native image, JVM, and virtual threads walk into a benchmark. Same reactive Micronaut service. Same k6 load profile. Same PostgreSQL. Same mock APIs with realistic latency jitter.&lt;/p&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Runtime&lt;/th&gt;&#xA;          &lt;th&gt;Req/s&lt;/th&gt;&#xA;          &lt;th&gt;Errors&lt;/th&gt;&#xA;          &lt;th&gt;RSS (avg)&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Native (unlimited)&lt;/td&gt;&#xA;          &lt;td&gt;264&lt;/td&gt;&#xA;          &lt;td&gt;1.0%&lt;/td&gt;&#xA;          &lt;td&gt;273 MB&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;JVM (unlimited)&lt;/td&gt;&#xA;          &lt;td&gt;266&lt;/td&gt;&#xA;          &lt;td&gt;1.0%&lt;/td&gt;&#xA;          &lt;td&gt;551 MB&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Virtual threads (unlimited)&lt;/td&gt;&#xA;          &lt;td&gt;265&lt;/td&gt;&#xA;          &lt;td&gt;1.0%&lt;/td&gt;&#xA;          &lt;td&gt;543 MB&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;265 req/s. All three. The error rates match the mock server&amp;rsquo;s configured failure rate — the application itself isn&amp;rsquo;t failing. Reactive code runs identically on native image, HotSpot, and loom-carrier Netty.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Loom Carrier on a Reactive Stack: 78 MB for Free</title>
      <link>https://tsvinc.dev/posts/loom-carrier-reactive/</link>
      <pubDate>Wed, 17 Jun 2026 00:00:00 +0000</pubDate>
      <guid>https://tsvinc.dev/posts/loom-carrier-reactive/</guid>
      <description>&lt;p&gt;&amp;ldquo;Virtual threads don&amp;rsquo;t help reactive apps.&amp;rdquo; You&amp;rsquo;ve heard this. Probably said it. The reasoning is sound: a fully reactive stack — &lt;code&gt;Mono&lt;/code&gt;/&lt;code&gt;Flux&lt;/code&gt; from HTTP client through R2DBC to Netty — never blocks a platform thread. Virtual threads solve blocking. No blocking, no benefit. QED.&lt;/p&gt;&#xA;&lt;p&gt;We ran the benchmark anyway. The conventional wisdom is correct about throughput and wrong about everything else.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-stack&#34;&gt;The Stack&lt;/h2&gt;&#xA;&lt;p&gt;Space Observatory: Micronaut 5.0.0, Netty, R2DBC PostgreSQL, Reactor Core, Micrometer. Reactive end-to-end — no &lt;code&gt;subscribeOn(Schedulers.boundedElastic())&lt;/code&gt;, no blocking JDBC, no thread-per-request anywhere. GraalVM CE 25, Java 25.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Every GC Failed at 256 MB (Except the Boring One)</title>
      <link>https://tsvinc.dev/posts/every-gc-failed-256mb/</link>
      <pubDate>Sun, 31 May 2026 00:00:00 +0000</pubDate>
      <guid>https://tsvinc.dev/posts/every-gc-failed-256mb/</guid>
      <description>&lt;p&gt;Monday: &amp;ldquo;The JVM at 256 MB should work. Just need the right GC.&amp;rdquo;&lt;/p&gt;&#xA;&lt;p&gt;Tuesday: G1GC. OOM at 150 concurrent users.&lt;/p&gt;&#xA;&lt;p&gt;Tuesday, later: Shenandoah. OOM at 50 concurrent users.&lt;/p&gt;&#xA;&lt;p&gt;Tuesday, even later: ZGC. OOM at zero concurrent users. Couldn&amp;rsquo;t serve a single request.&lt;/p&gt;&#xA;&lt;p&gt;Wednesday: Serial GC with 7 JVM flags and 3 Micronaut properties. Survives 60 seconds before the OOM killer shows up anyway.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-contenders&#34;&gt;The Contenders&lt;/h2&gt;&#xA;&lt;p&gt;Same app: Micronaut 5.0.0 Space Observatory, 16 REST endpoints, reactive R2DBC, 7-table PostgreSQL schema. Container: 256 MB hard limit, swap disabled. Heap capped at 64 MB (&lt;code&gt;-Xms64m -Xmx64m&lt;/code&gt;). Load: k6, ramping from 10 to 200 concurrent users over 8.5 minutes.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Native Image vs JVM at 128 MB: The Benchmark Nobody Warned Me About</title>
      <link>https://tsvinc.dev/posts/native-vs-jvm-128mb/</link>
      <pubDate>Tue, 26 May 2026 00:00:00 +0000</pubDate>
      <guid>https://tsvinc.dev/posts/native-vs-jvm-128mb/</guid>
      <description>&lt;p&gt;The mental model: native image uses less memory, therefore native image performs better under memory pressure.&lt;/p&gt;&#xA;&lt;p&gt;This is wrong.&lt;/p&gt;&#xA;&lt;h2 id=&#34;setup&#34;&gt;Setup&lt;/h2&gt;&#xA;&lt;p&gt;The app: Micronaut 5.0.0 Space Observatory. 4 HTTP clients, reactive R2DBC into PostgreSQL, 7-table schema, 16 REST endpoints. Real enough to matter. (&lt;a href=&#34;https://tsvinc.dev/posts/micronaut-5-native-image/&#34;&gt;Part 1&lt;/a&gt; covers the build.)&lt;/p&gt;&#xA;&lt;p&gt;Four scenarios, clean database each time (&lt;code&gt;docker compose down -v&lt;/code&gt;):&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;Native, unlimited memory&lt;/strong&gt; &amp;ndash; baseline&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Native, 128 MB hard limit&lt;/strong&gt; &amp;ndash; &lt;code&gt;mem_limit: 128m&lt;/code&gt;, swap disabled&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;JVM, unlimited memory&lt;/strong&gt; &amp;ndash; same app, &lt;code&gt;java -jar&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;JVM, 128 MB hard limit&lt;/strong&gt; &amp;ndash; same container constraint&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;Load: Apache Bench, 1,000 requests x 20 concurrent, container-to-container. DB endpoints only &amp;ndash; no external API calls polluting the numbers. Host: 16 cores, 32 GB RAM, Linux 7.0.9.&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
