<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Spring-Boot on tsvinc</title>
    <link>https://tsvinc.dev/tags/spring-boot/</link>
    <description>Recent content in Spring-Boot 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/spring-boot/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>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>Two ways to mistranslate a retry</title>
      <link>https://tsvinc.dev/posts/two-ways-mistranslate-retry/</link>
      <pubDate>Sat, 04 Jul 2026 00:00:00 +0000</pubDate>
      <guid>https://tsvinc.dev/posts/two-ways-mistranslate-retry/</guid>
      <description>&lt;p&gt;&lt;em&gt;Part 21 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/quarkus-two-stacked-bottlenecks/&#34;&gt;Part 20&lt;/a&gt;, where two stacked bottlenecks throttled Quarkus. Assumes familiarity with declarative HTTP clients and retry semantics.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;Porting 10 HTTP clients from Micronaut to Quarkus and Spring Boot produced two retry bugs. One framework over-retried by ~50%. The other silently never retried HTTP 5xx at all. Both ports passed every functional test. Both shipped code comments claiming parity with the original.&lt;/p&gt;&#xA;&lt;p&gt;The bugs were mirror images, caused by completely different mechanisms, and invisible without a benchmark that injects failures and counts upstream calls.&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>
  </channel>
</rss>
