<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Graalvm on tsvinc</title>
    <link>https://tsvinc.dev/tags/graalvm/</link>
    <description>Recent content in Graalvm 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/graalvm/index.xml" rel="self" type="application/rss+xml" />
    <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>Four Bugs the Tests Couldn&#39;t See</title>
      <link>https://tsvinc.dev/posts/four-bugs-tests-couldnt-see/</link>
      <pubDate>Sun, 05 Jul 2026 00:00:00 +0000</pubDate>
      <guid>https://tsvinc.dev/posts/four-bugs-tests-couldnt-see/</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;. Follows &lt;a href=&#34;https://tsvinc.dev/posts/six-ooms-one-http-client/&#34;&gt;Part 23&lt;/a&gt;, where six HTTP client swaps each OOM&amp;rsquo;d differently. Assumes familiarity with R2DBC, Hibernate Reactive, and connection pooling.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;The senior code review on the first reactive port &amp;ndash; Spring Boot 4.1 WebFlux and Quarkus 3.33 Hibernate Reactive, same app, same tests &amp;ndash; came back clean. Field mapping: correct. Fan-out concurrency: correct. Scoring logic: correct. Native image hints: correct. Retry semantics: fixed (that was &lt;a href=&#34;https://tsvinc.dev/posts/two-ways-mistranslate-retry/&#34;&gt;Part 21&lt;/a&gt;). All tests green.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Six OOMs and a Connection Pool</title>
      <link>https://tsvinc.dev/posts/six-ooms-one-http-client/</link>
      <pubDate>Sun, 05 Jul 2026 00:00:00 +0000</pubDate>
      <guid>https://tsvinc.dev/posts/six-ooms-one-http-client/</guid>
      <description>&lt;p&gt;&lt;em&gt;Part 23 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/three-front-doors-one-engine/&#34;&gt;Part 22&lt;/a&gt;, where the reactive ports reached throughput parity across three frameworks. Assumes familiarity with Spring&amp;rsquo;s RestClient and GraalVM native image.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;The same 28-API fan-out app runs comfortably at 256 MB on Quarkus. Runs comfortably on Micronaut. On the imperative Spring Boot 4.1 port &amp;ndash; Spring MVC, virtual threads, JdbcClient &amp;ndash; it OOM&amp;rsquo;d. Then I fixed it and it OOM&amp;rsquo;d differently. Six benchmark runs. Six different failure modes. Each one peeling back a layer of what &amp;ldquo;choosing an HTTP client&amp;rdquo; actually means in a memory-constrained native image.&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>-Xmx Won&#39;t Save Your Native Image</title>
      <link>https://tsvinc.dev/posts/xmx-wont-save-native-image/</link>
      <pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate>
      <guid>https://tsvinc.dev/posts/xmx-wont-save-native-image/</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/counter-reset-benchmark-lied/&#34;&gt;Part 18&lt;/a&gt;, where a reset Prometheus counter made the benchmark lie. Assumes familiarity with GraalVM native image and Docker memory limits.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-confident-wrong-fix&#34;&gt;The Confident Wrong Fix&lt;/h2&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-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c&#34;&gt;# docker-compose.yml — the JVM reflex&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;environment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;JAVA_TOOL_OPTIONS&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;-Xmx128m&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Standard. Reliable. Every JVM deployment in every Kubernetes cluster in the world does this. It has never not worked.&lt;/p&gt;&#xA;&lt;p&gt;It is a no-op on a native binary.&lt;/p&gt;&#xA;&lt;p&gt;GraalVM native images are not JVMs. They don&amp;rsquo;t read &lt;code&gt;JAVA_TOOL_OPTIONS&lt;/code&gt;. They don&amp;rsquo;t read &lt;code&gt;-Xmx&lt;/code&gt; from environment variables. &lt;a href=&#34;https://github.com/oracle/graal/issues/4650&#34;&gt;GraalVM issue #4650&lt;/a&gt; requested this capability. &lt;a href=&#34;https://github.com/oracle/graal/issues/9504&#34;&gt;Issue #9504&lt;/a&gt; confirmed it&amp;rsquo;s still not supported. Your heap cap does nothing. No warning. No error. The binary starts, ignores the variable, sizes its heap however it wants.&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>Error Is Not Exception: Micrometer&#39;s Silent CPU Metric on Native Image</title>
      <link>https://tsvinc.dev/posts/error-is-not-exception/</link>
      <pubDate>Mon, 22 Jun 2026 00:00:00 +0000</pubDate>
      <guid>https://tsvinc.dev/posts/error-is-not-exception/</guid>
      <description>&lt;p&gt;&lt;em&gt;Part 13 in the &lt;a href=&#34;https://tsvinc.dev/series/micronaut-256mb/&#34;&gt;Memory Benchmark series&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-catch-block-that-catches-nothing&#34;&gt;The catch block that catches nothing&lt;/h2&gt;&#xA;&lt;p&gt;This code has a bug. Not a typo, not a logic error. A type hierarchy bug.&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-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;private&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;double&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;invoke&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@Nullable&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Method&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;method&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;try&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;method&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;?&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;toDouble&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Number&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;method&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;invoke&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;operatingSystemBean&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Double&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;NaN&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;catch&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;IllegalAccessException&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;IllegalArgumentException&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;           &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;InvocationTargetException&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;e&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Double&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;NaN&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s micrometer&amp;rsquo;s &lt;code&gt;ProcessorMetrics.invoke()&lt;/code&gt; (v1.16.5). It calls JMX methods via reflection, wraps the call in three catch clauses. Standard defensive coding.&lt;/p&gt;</description>
    </item>
    <item>
      <title>We Removed Reactor. Nothing Got Slower.</title>
      <link>https://tsvinc.dev/posts/reactor-removal-benchmark/</link>
      <pubDate>Sat, 20 Jun 2026 00:00:00 +0000</pubDate>
      <guid>https://tsvinc.dev/posts/reactor-removal-benchmark/</guid>
      <description>&lt;p&gt;We took the entire reactive stack out. R2DBC, Mono, Flux, Reactor Core, loom-carrier, &lt;code&gt;flatMap&lt;/code&gt; with its carefully-tuned concurrency arguments. Replaced it with JDBC, HikariCP, and blocking method calls. Same endpoints. Same Netty HTTP server. Same PostgreSQL. Same k6 load profile: 10 to 200 VUs, 500 req/s constant arrival rate, seven scenarios each.&lt;/p&gt;&#xA;&lt;p&gt;Throughput didn&amp;rsquo;t move. Latency dropped. Memory dropped. GC pauses dropped 63% at 256 MB.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-scoreboard&#34;&gt;The Scoreboard&lt;/h2&gt;&#xA;&lt;p&gt;This is &lt;a href=&#34;https://tsvinc.dev/series/micronaut-256mb/&#34;&gt;part of the series&lt;/a&gt; benchmarking a Micronaut 5.0.0 app under memory constraints. The reactive version is the one from &lt;a href=&#34;https://tsvinc.dev/posts/flatmap-backpressure/&#34;&gt;Part 10&lt;/a&gt; and &lt;a href=&#34;https://tsvinc.dev/posts/loom-carrier-reactive/&#34;&gt;Part 11&lt;/a&gt; — fully tuned, &lt;code&gt;flatMap&lt;/code&gt; concurrency bounded, R2DBC pool sized to match, loom-carrier enabled for virtual threads. The imperative version: JDBC/HikariCP, &lt;code&gt;THREAD_PER_TASK&lt;/code&gt; executor for virtual threads, same pool size.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Three Bugs in a Trenchcoat Pretending to Be One Hang</title>
      <link>https://tsvinc.dev/posts/aot-three-bugs-trenchcoat/</link>
      <pubDate>Sun, 07 Jun 2026 00:00:00 +0000</pubDate>
      <guid>https://tsvinc.dev/posts/aot-three-bugs-trenchcoat/</guid>
      <description>&lt;p&gt;The CLAUDE.md for this project had a one-liner that persisted for a week: &amp;ldquo;Micronaut AOT hangs — keep disabled.&amp;rdquo;&lt;/p&gt;&#xA;&lt;p&gt;Every developer who touched the project read that line, nodded, and moved on. AOT disabled. Property set to false. Life continued. Nobody questioned it because it was technically true — AOT &lt;em&gt;had&lt;/em&gt; hung — and the project worked without it.&lt;/p&gt;&#xA;&lt;p&gt;Then the benchmark numbers arrived. Non-heap at 119 MB. Container limit at 256 MB. JVM OOM-killing at 150 concurrent users. And a footnote in the Micronaut AOT docs: &amp;ldquo;build-time service loading reduces runtime metaspace by eliminating classpath scanning.&amp;rdquo;&lt;/p&gt;</description>
    </item>
    <item>
      <title>Your JVM Reports 151 MB. Your Container Uses 248.</title>
      <link>https://tsvinc.dev/posts/jvm-memory-anatomy/</link>
      <pubDate>Fri, 05 Jun 2026 00:00:00 +0000</pubDate>
      <guid>https://tsvinc.dev/posts/jvm-memory-anatomy/</guid>
      <description>&lt;p&gt;151 MB. That&amp;rsquo;s what micrometer-prometheus reports for the JVM at 256 MB after serving 1.66 million requests. Heap 35 MB, metaspace 72 MB, compressed class space 13 MB, code cache 16 MB, direct buffers 0.75 MB. Add it up. 137 MB. Rounded generously: 151 MB with some measurement timing jitter.&lt;/p&gt;&#xA;&lt;p&gt;Docker stats says 244.2 MiB. Out of 256 MiB. 95.38%.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-visible-budget&#34;&gt;The Visible Budget&lt;/h2&gt;&#xA;&lt;p&gt;After 1.66 million requests, Serial GC, &lt;code&gt;-Xms64m -Xmx64m -Xss256k&lt;/code&gt;, here&amp;rsquo;s what the JVM admits to owning:&lt;/p&gt;</description>
    </item>
    <item>
      <title>107 MB of Invisible JVM RAM</title>
      <link>https://tsvinc.dev/posts/107mb-invisible-jvm-ram/</link>
      <pubDate>Tue, 02 Jun 2026 00:00:00 +0000</pubDate>
      <guid>https://tsvinc.dev/posts/107mb-invisible-jvm-ram/</guid>
      <description>&lt;p&gt;Prometheus says 137 MB. Docker says 244 MiB. &lt;code&gt;/proc/PID/smaps&lt;/code&gt; is the only witness that doesn&amp;rsquo;t lie.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-discrepancy&#34;&gt;The Discrepancy&lt;/h2&gt;&#xA;&lt;p&gt;JVM at 256 MB, Serial GC, after serving 1.66 million requests. Measured at idle:&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;Docker stats:         244.2 MiB / 256 MiB  (95.38%)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Prometheus reports:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Heap used:           35 MB  (Eden 4.4 + Survivor 0.1 + Tenured 30.5)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Non-heap:           101 MB  (Metaspace 71.9 + CompClass 12.7 + CodeCache 16.3)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Direct buffers:     0.75 MB&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  ────────────────────────────&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  Total visible:      ~137 MB&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Gap:                  107 MB&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;107 MB that no JMX MBean exposes. No micrometer gauge records it. No Grafana dashboard shows it. To know it exists, you &lt;code&gt;exec&lt;/code&gt; into the container and parse kernel memory maps.&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>The Observability Tax: When Metrics Cost More Than Your App</title>
      <link>https://tsvinc.dev/posts/observability-tax-micrometer/</link>
      <pubDate>Thu, 28 May 2026 00:00:00 +0000</pubDate>
      <guid>https://tsvinc.dev/posts/observability-tax-micrometer/</guid>
      <description>&lt;p&gt;Add observability to understand your app&amp;rsquo;s memory usage. Watch observability consume all the memory.&lt;/p&gt;&#xA;&lt;p&gt;The JVM at 128 MB worked in Part 2. It no longer works.&lt;/p&gt;&#xA;&lt;h2 id=&#34;what-changed&#34;&gt;What Changed&lt;/h2&gt;&#xA;&lt;p&gt;One dependency:&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-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;io.micronaut.micrometer&lt;span class=&#34;nt&#34;&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;micronaut-micrometer-registry-prometheus&lt;span class=&#34;nt&#34;&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;runtime&lt;span class=&#34;nt&#34;&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Three lines of YAML:&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-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;micrometer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;export&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;prometheus&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;enabled&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Prometheus endpoint exposed, micrometer collecting JVM metrics — heap, non-heap, GC pauses, threads, CPU, R2DBC pool stats. The standard production observability setup.&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>
    <item>
      <title>Micronaut 5 Native Image: 14 Entries vs 1,913</title>
      <link>https://tsvinc.dev/posts/micronaut-5-native-image/</link>
      <pubDate>Sat, 23 May 2026 00:00:00 +0000</pubDate>
      <guid>https://tsvinc.dev/posts/micronaut-5-native-image/</guid>
      <description>&lt;p&gt;Micronaut 5.0.0 dropped on May 20, 2026. The &lt;a href=&#34;https://micronaut.io/2026/05/20/micronaut-framework-5-0-0-released/&#34;&gt;launch blog post&lt;/a&gt; promises first-class GraalVM native image support. The documentation is correct. It is also incomplete in ways that cost hours.&lt;/p&gt;&#xA;&lt;p&gt;This is what building a non-trivial native image app looks like on day one: 4 HTTP clients hitting real APIs, reactive R2DBC pipeline into PostgreSQL, Liquibase migrations, &lt;code&gt;@Scheduled&lt;/code&gt; cron jobs, Swagger UI. Everything that should Just Work and mostly does — except when it doesn&amp;rsquo;t, and the failure modes are silent.&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
