<?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>Forem: Gaurav</title>
    <description>The latest articles on Forem by Gaurav (@dixitgurv).</description>
    <link>https://forem.com/dixitgurv</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2826900%2Fd77d1978-ca7c-4d2b-be80-67814439b072.png</url>
      <title>Forem: Gaurav</title>
      <link>https://forem.com/dixitgurv</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/dixitgurv"/>
    <language>en</language>
    <item>
      <title>Spring ShedLock</title>
      <dc:creator>Gaurav</dc:creator>
      <pubDate>Thu, 13 Nov 2025 15:53:24 +0000</pubDate>
      <link>https://forem.com/dixitgurv/spring-shedlock-53f1</link>
      <guid>https://forem.com/dixitgurv/spring-shedlock-53f1</guid>
      <description>&lt;h1&gt;
  
  
  Spring ShedLock – A Quick‑Start &amp;amp; Deep‑Dive Guide
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;ShedLock&lt;/strong&gt; is a tiny library that makes sure a scheduled job (or any piece of code) runs &lt;strong&gt;only once&lt;/strong&gt; in a &lt;strong&gt;cluster&lt;/strong&gt; of Spring Boot applications.&lt;br&gt;&lt;br&gt;
It does this by acquiring a distributed lock in a shared storage (DB, Redis, Mongo, …) before the job body is executed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Below is a &lt;strong&gt;complete, production‑ready cheat‑sheet&lt;/strong&gt; you can copy‑paste into a new Spring Boot project, plus the “why‑and‑how” you need to understand before you ship it.&lt;/p&gt;




&lt;h2&gt;
  
  
  1️⃣ Why you need ShedLock
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Problem&lt;/th&gt;
&lt;th&gt;What you see today&lt;/th&gt;
&lt;th&gt;What ShedLock does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Multiple instances of a Spring Boot app running the same &lt;code&gt;@Scheduled&lt;/code&gt; method&lt;/td&gt;
&lt;td&gt;The method fires &lt;strong&gt;N times&lt;/strong&gt; (once per instance) → duplicate email, double payment, race conditions&lt;/td&gt;
&lt;td&gt;Acquires a global lock → &lt;strong&gt;only the instance that wins the lock executes&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cron jobs need a &lt;strong&gt;guaranteed “once‑per‑minute”&lt;/strong&gt; semantics even after a pod restarts or a rolling deployment&lt;/td&gt;
&lt;td&gt;The scheduler restarts → job may run &lt;strong&gt;twice&lt;/strong&gt; (once before restart, once after)&lt;/td&gt;
&lt;td&gt;Lock persists across restarts; the second instance sees the lock and skips execution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;You already have a relational DB, Redis, or Mongo – don’t want to spin up Zookeeper / etcd just for locking&lt;/td&gt;
&lt;td&gt;You’d have to add new infra&lt;/td&gt;
&lt;td&gt;ShedLock works &lt;strong&gt;on top of any storage you already own&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Bottom line:&lt;/strong&gt; If you use Spring’s &lt;code&gt;@Scheduled&lt;/code&gt; (or any custom “run‑once” logic) in a clustered environment, ShedLock is the simplest, battle‑tested solution.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  2️⃣ Adding the dependency
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- Maven --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;net.javacrumbs.shedlock&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;shedlock-spring&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;5.11.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- Choose a lock provider (pick ONE) --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;net.javacrumbs.shedlock&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;shedlock-provider-jdbc-template&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;   &lt;span class="c"&gt;&amp;lt;!-- for any JDBC DB --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;5.11.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- OR --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;net.javacrumbs.shedlock&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;shedlock-provider-redis&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;5.11.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- OR --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;net.javacrumbs.shedlock&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;shedlock-provider-mongo&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;5.11.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Gradle equivalent:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s2"&gt;"net.javacrumbs.shedlock:shedlock-spring:5.11.0"&lt;/span&gt;
&lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s2"&gt;"net.javacrumbs.shedlock:shedlock-provider-jdbc-template:5.11.0"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; If you already have a &lt;code&gt;DataSource&lt;/code&gt; bean (most Spring Boot apps do), the &lt;em&gt;jdbc‑template&lt;/em&gt; provider is the zero‑config path.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  3️⃣ Configuring the lock provider
&lt;/h2&gt;

&lt;h3&gt;
  
  
  3.1 JDBC (PostgreSQL / MySQL / H2 / …)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.example.scheduling&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;net.javacrumbs.shedlock.core.LockProvider&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;net.javacrumbs.shedlock.provider.jdbctemplate.JdbcTemplateLockProvider&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.context.annotation.Bean&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.context.annotation.Configuration&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.jdbc.core.JdbcTemplate&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;javax.sql.DataSource&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@Configuration&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ShedLockConfig&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Bean&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;LockProvider&lt;/span&gt; &lt;span class="nf"&gt;lockProvider&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DataSource&lt;/span&gt; &lt;span class="n"&gt;dataSource&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ShedLock will create the table automatically on first lock acquisition&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;JdbcTemplateLockProvider&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                &lt;span class="nc"&gt;JdbcTemplateLockProvider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Configuration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withJdbcTemplate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;JdbcTemplate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dataSource&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
                        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;usingDbTime&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;          &lt;span class="c1"&gt;// &amp;lt;-- use DB clock, avoids clock‑drift issues&lt;/span&gt;
                        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What table does ShedLock create?&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;shedlock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;lock_until&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;locked_at&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;locked_by&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also create it manually (useful for migrations) – see the DDL in the &lt;a href="https://github.com/lukas-krecan/ShedLock/tree/master/shedlock-provider-jdbc-template#schema" rel="noopener noreferrer"&gt;official docs&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.2 Redis (single‑node or clustered)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;net.javacrumbs.shedlock.provider.redis.RedisLockProvider&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;net.javacrumbs.shedlock.core.LockProvider&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.redisson.api.RedissonClient&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.context.annotation.Bean&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.context.annotation.Configuration&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@Configuration&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ShedLockConfig&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Bean&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;LockProvider&lt;/span&gt; &lt;span class="nf"&gt;lockProvider&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RedissonClient&lt;/span&gt; &lt;span class="n"&gt;redisson&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RedisLockProvider&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;redisson&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why Redisson?&lt;/strong&gt; It’s the only Redis client that ships a ready‑made &lt;code&gt;RLock&lt;/code&gt; implementation with TTL handling, which is exactly what ShedLock expects.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  3.3 MongoDB
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;net.javacrumbs.shedlock.provider.mongo.MongoLockProvider&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.mongodb.client.MongoClient&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;net.javacrumbs.shedlock.core.LockProvider&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.context.annotation.Bean&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.context.annotation.Configuration&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@Configuration&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ShedLockConfig&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Bean&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;LockProvider&lt;/span&gt; &lt;span class="nf"&gt;lockProvider&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MongoClient&lt;/span&gt; &lt;span class="n"&gt;mongoClient&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;MongoLockProvider&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mongoClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getDatabase&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"mydb"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Collection name:&lt;/strong&gt; &lt;code&gt;shedlock&lt;/code&gt; (created automatically). Index on &lt;code&gt;name&lt;/code&gt; is added automatically.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  4️⃣ Using the annotation on a scheduled method
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.example.scheduling&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;net.javacrumbs.shedlock.spring.annotation.SchedulerLock&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.scheduling.annotation.Scheduled&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.stereotype.Component&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@Component&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BillingJob&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * Runs at the top of every hour, only once across the whole cluster.
     * lockAtMostFor = 30 minutes (fallback safety net)
     * lockAtLeastFor = 5 minutes (prevents rapid re‑execution if job finishes quickly)
     */&lt;/span&gt;
    &lt;span class="nd"&gt;@Scheduled&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cron&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0 0 * * * *"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// every hour, at minute 0&lt;/span&gt;
    &lt;span class="nd"&gt;@SchedulerLock&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"billingJob"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lockAtMostFor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"30m"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lockAtLeastFor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"5m"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;generateInvoices&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ... your business logic&lt;/span&gt;
        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Generating invoices at "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Instant&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;now&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What the lock parameters mean
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;th&gt;Typical values&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unique identifier of the lock (must be globally unique for the job)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;"billingJob"&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;lockAtMostFor&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Hard timeout&lt;/strong&gt; – if the lock is not released (e.g., crash) it will be auto‑released after this duration. Prevents dead‑locks.&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;30m&lt;/code&gt;, &lt;code&gt;1h&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;lockAtLeastFor&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Soft minimum&lt;/strong&gt; – even if the job finishes early, the lock will be kept for at least this long. Guarantees a “quiet period” and prevents “flapping” when the job is triggered too often (e.g., when the cron expression fires more frequently than the job can finish).&lt;/td&gt;
&lt;td&gt;&lt;code&gt;5m&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Best practice:&lt;/strong&gt; Keep &lt;code&gt;lockAtLeastFor&lt;/code&gt; ≤ your cron frequency. Keep &lt;code&gt;lockAtMostFor&lt;/code&gt; a little larger than the worst‑case execution time you expect.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  5️⃣ Advanced Topics
&lt;/h2&gt;

&lt;h3&gt;
  
  
  5.1 Custom Lock Provider (e.g., AWS DynamoDB, Cassandra)
&lt;/h3&gt;

&lt;p&gt;If none of the built‑in providers fits, you can implement the &lt;code&gt;LockProvider&lt;/code&gt; interface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DynamoDbLockProvider&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;LockProvider&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// inject DynamoDb client, table name, etc.&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SimpleLock&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;LockConfiguration&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// 1️⃣ Try to put an item with a ConditionExpression "attribute_not_exists(name)"&lt;/span&gt;
        &lt;span class="c1"&gt;// 2️⃣ If successful, return SimpleLock that will delete the item on unlock()&lt;/span&gt;
        &lt;span class="c1"&gt;// 3️⃣ Respect request.getLockAtMostFor() for TTL (use DynamoDB's TTL attribute)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Register it as a &lt;code&gt;@Bean&lt;/code&gt; just like the others.&lt;/p&gt;

&lt;h3&gt;
  
  
  5.2 Lock expiration and clock drift
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Issue&lt;/th&gt;
&lt;th&gt;Why it matters&lt;/th&gt;
&lt;th&gt;Recommended solution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Node clock is ahead&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;lockAtMostFor&lt;/code&gt; might expire earlier than expected → another node acquires the lock while the first job still runs&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;usingDbTime()&lt;/code&gt; for JDBC providers, or rely on the server‑side TTL (Redis, Mongo) which is independent of the client clock&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lock not released due to crash&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Subsequent runs stuck forever&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;lockAtMostFor&lt;/code&gt; acts as a safety net. Set it slightly larger than the maximum expected runtime.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Long-running jobs (&amp;gt; lockAtMostFor)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Job may be killed by the lock provider, causing partial work&lt;/td&gt;
&lt;td&gt;Either increase &lt;code&gt;lockAtMostFor&lt;/code&gt; or split the job into smaller chunks (each with its own lock).&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  5.3 Testing locks (unit &amp;amp; integration)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@SpringBootTest&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BillingJobTest&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Autowired&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;BillingJob&lt;/span&gt; &lt;span class="n"&gt;billingJob&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@MockBean&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;LockProvider&lt;/span&gt; &lt;span class="n"&gt;lockProvider&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// mock from ShedLock&lt;/span&gt;

    &lt;span class="nd"&gt;@Test&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;testLockAcquired&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Simulate lock acquisition success&lt;/span&gt;
        &lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lockProvider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="o"&gt;())).&lt;/span&gt;&lt;span class="na"&gt;thenReturn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{}));&lt;/span&gt;
        &lt;span class="n"&gt;billingJob&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;generateInvoices&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="c1"&gt;// verify that the real business logic was executed&lt;/span&gt;
        &lt;span class="c1"&gt;// (e.g., verify a service call)&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Test&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;testLockNotAcquired&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Simulate another node already holding the lock&lt;/span&gt;
        &lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lockProvider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="o"&gt;())).&lt;/span&gt;&lt;span class="na"&gt;thenReturn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;empty&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="n"&gt;billingJob&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;generateInvoices&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="c1"&gt;// make sure the business method is NOT called&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Integration test&lt;/strong&gt;: Spin up an in‑memory H2 DB (or Testcontainers Postgres) and let the real &lt;code&gt;JdbcTemplateLockProvider&lt;/code&gt; work. Verify that two beans scheduled &lt;em&gt;simultaneously&lt;/em&gt; only execute once.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  5.4 Combining with Spring Cloud Scheduler / Kubernetes CronJobs
&lt;/h3&gt;

&lt;p&gt;If you’re already using &lt;strong&gt;Kubernetes CronJobs&lt;/strong&gt; (which guarantee a single pod per schedule), you &lt;em&gt;don’t&lt;/em&gt; need ShedLock.&lt;br&gt;&lt;br&gt;
But if you run &lt;strong&gt;regular Pods with &lt;code&gt;@Scheduled&lt;/code&gt;&lt;/strong&gt; inside a Deployment (replicas &amp;gt; 1) &lt;em&gt;or&lt;/em&gt; you have an &lt;strong&gt;autoscaling group&lt;/strong&gt;, ShedLock is the way to go.&lt;/p&gt;


&lt;h2&gt;
  
  
  6️⃣ Common Pitfalls &amp;amp; How to Fix Them
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Root Cause&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Job runs twice after a quick pod restart&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;lockAtMostFor&lt;/code&gt; is too short; the lock expires while the job is still running.&lt;/td&gt;
&lt;td&gt;Increase &lt;code&gt;lockAtMostFor&lt;/code&gt; to comfortably exceed worst‑case runtime.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;LockAcquisitionException: LockProvider could not acquire lock&lt;/code&gt; even though DB is empty&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Spring’s transaction isolation (e.g., &lt;code&gt;READ_COMMITTED&lt;/code&gt;) may cause a race condition on the first insert.&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;JdbcTemplateLockProvider&lt;/code&gt; (which uses plain JDBC) &lt;strong&gt;or&lt;/strong&gt; set &lt;code&gt;@Transactional(propagation = Propagation.NOT_SUPPORTED)&lt;/code&gt; on the scheduled method.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;Lock table not found&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Auto‑creation disabled (e.g., you turned off &lt;code&gt;spring.jpa.hibernate.ddl-auto&lt;/code&gt;).&lt;/td&gt;
&lt;td&gt;Create the &lt;code&gt;shedlock&lt;/code&gt; table yourself (DDL above) or enable &lt;code&gt;spring.jpa.hibernate.ddl-auto=update&lt;/code&gt; just for dev.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lock never released after crash&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;lockAtMostFor&lt;/code&gt; set to &lt;code&gt;null&lt;/code&gt; (infinite) → lock stays forever.&lt;/td&gt;
&lt;td&gt;Always specify a finite &lt;code&gt;lockAtMostFor&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Duplicate executions in a multi‑DB‑replica setup&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Each replica uses its own &lt;em&gt;different&lt;/em&gt; DB (no shared storage).&lt;/td&gt;
&lt;td&gt;Choose a &lt;em&gt;single&lt;/em&gt; shared storage (Redis, external PostgreSQL, etc.) for the lock provider.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;h2&gt;
  
  
  7️⃣ Full Minimal Example (Spring Boot 3.x + PostgreSQL)
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;pom.xml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-data-jpa&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.postgresql&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;postgresql&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;runtime&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;net.javacrumbs.shedlock&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;shedlock-spring&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;5.11.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;net.javacrumbs.shedlock&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;shedlock-provider-jdbc-template&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;5.11.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;application.yml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;spring&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;datasource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jdbc:postgresql://localhost:5432/mydb&lt;/span&gt;
    &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app&lt;/span&gt;
    &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;secret&lt;/span&gt;
  &lt;span class="na"&gt;jpa&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;hibernate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;ddl-auto&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;update&lt;/span&gt;   &lt;span class="c1"&gt;# creates 'shedlock' table automatically&lt;/span&gt;
  &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;scheduling&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;pool&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;ShedLockConfig.java&lt;/code&gt; – same as in section 3.1&lt;/p&gt;

&lt;p&gt;&lt;code&gt;BillingJob.java&lt;/code&gt; – same as in section 4&lt;/p&gt;

&lt;p&gt;Run &lt;strong&gt;2&lt;/strong&gt; instances (&lt;code&gt;java -jar app.jar&lt;/code&gt; twice) → you’ll see ONLY ONE instance printing the “Generating invoices…” line each hour.&lt;/p&gt;




&lt;h2&gt;
  
  
  8️⃣ Alternatives (when not to use ShedLock)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Solution&lt;/th&gt;
&lt;th&gt;When it shines&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Kubernetes CronJob&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;You want &lt;em&gt;exactly one&lt;/em&gt; pod per schedule, no background &lt;code&gt;@Scheduled&lt;/code&gt; inside a long‑lived Deployment.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Quartz Scheduler&lt;/strong&gt; + &lt;strong&gt;Clustered JobStore&lt;/strong&gt; (JDBC)&lt;/td&gt;
&lt;td&gt;Need complex triggers (calendar intervals, misfire handling, job pause/resume) – Quartz already ships its own DB‑based lock.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Spring Cloud Task + Scheduler&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;One‑off, short‑lived tasks launched by a scheduler.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;External workflow engines&lt;/strong&gt; (Temporal, Camunda)&lt;/td&gt;
&lt;td&gt;You need full BPMN, saga patterns, or retries with compensation.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If you only need “run‑once‑per‑cron‑tick”, ShedLock remains the simplest answer.&lt;/p&gt;




&lt;h2&gt;
  
  
  9️⃣ TL;DR Checklist (Copy‑Paste)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;-&lt;/span&gt; [ ] Add &lt;span class="sb"&gt;`shedlock-spring`&lt;/span&gt; + a lock‑provider dependency.
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Create a &lt;span class="sb"&gt;`LockProvider`&lt;/span&gt; bean (JDBC, Redis, Mongo …).
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Annotate each &lt;span class="sb"&gt;`@Scheduled`&lt;/span&gt; method with &lt;span class="sb"&gt;`@SchedulerLock(name = "...")`&lt;/span&gt;.
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Pick sensible &lt;span class="sb"&gt;`lockAtMostFor`&lt;/span&gt; (&amp;gt;= max runtime) and &lt;span class="sb"&gt;`lockAtLeastFor`&lt;/span&gt; (&amp;lt;= cron period).
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Verify that the lock table/collection exists (or let ShedLock create it).
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Run multiple instances → only one should execute the job.
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Add unit/integration tests that mock the &lt;span class="sb"&gt;`LockProvider`&lt;/span&gt;.
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Monitor the &lt;span class="sb"&gt;`shedlock`&lt;/span&gt; table (optional: add a health check that the lock can be acquired).
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  10️⃣ Further Reading &amp;amp; Resources
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Resource&lt;/th&gt;
&lt;th&gt;Why read it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;GitHub – ShedLock&lt;/strong&gt; – &lt;a href="https://github.com/lukas-krecan/ShedLock" rel="noopener noreferrer"&gt;https://github.com/lukas-krecan/ShedLock&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Source code, all provider docs, migration guide&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Spring Boot Reference – Scheduling&lt;/strong&gt; – &lt;a href="https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-task-execution-and-scheduling" rel="noopener noreferrer"&gt;https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-task-execution-and-scheduling&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Baseline &lt;code&gt;@Scheduled&lt;/code&gt; mechanics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Redis Locking Best Practices&lt;/strong&gt; – Redisson docs&lt;/td&gt;
&lt;td&gt;If you pick Redis, understand TTL &amp;amp; renewal semantics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;“Handling Distributed Cron Jobs in Kubernetes”&lt;/strong&gt; (Medium)&lt;/td&gt;
&lt;td&gt;When you need to decide between CronJobs vs. ShedLock&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;ShedLock Spring Boot Starter (Starter Project)&lt;/strong&gt; – &lt;a href="https://start.spring.io" rel="noopener noreferrer"&gt;https://start.spring.io&lt;/a&gt; (add “ShedLock” as a dependency)&lt;/td&gt;
&lt;td&gt;Quick start for a new project&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;p&gt;&lt;strong&gt;Enjoy a clean, single‑execution guarantee for all your background jobs!&lt;/strong&gt; 🎉 If you run into a specific error or need a custom lock (e.g., DynamoDB, etcd), just drop a follow‑up and we’ll dive deeper. Happy coding!&lt;/p&gt;

</description>
      <category>java</category>
      <category>backend</category>
      <category>tutorial</category>
      <category>architecture</category>
    </item>
    <item>
      <title>How to Optimize SQL Queries in High-Traffic Applications</title>
      <dc:creator>Gaurav</dc:creator>
      <pubDate>Tue, 02 Sep 2025 16:12:23 +0000</pubDate>
      <link>https://forem.com/dixitgurv/how-to-optimize-sql-queries-in-high-traffic-applications-52d9</link>
      <guid>https://forem.com/dixitgurv/how-to-optimize-sql-queries-in-high-traffic-applications-52d9</guid>
      <description>&lt;h1&gt;
  
  
  Optimizing SQL Queries for High‑Traffic Applications
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;(A practical, step‑by‑step guide that works across MySQL, PostgreSQL, SQL Server, and Oracle)&lt;/em&gt;  &lt;/p&gt;




&lt;h2&gt;
  
  
  1. Why “SQL‑only” Optimisation Matters
&lt;/h2&gt;

&lt;p&gt;High‑traffic apps often hit the database thousands to millions of times per second. Even a 1 ms improvement per query can save seconds of CPU time, reduce latency, and keep your infrastructure costs down. The biggest gains come from &lt;strong&gt;making the database do less work&lt;/strong&gt;, not just from adding more hardware.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. The Optimization Lifecycle
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Phase&lt;/th&gt;
&lt;th&gt;Goal&lt;/th&gt;
&lt;th&gt;Typical Tools / Artefacts&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;A. Baseline&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Capture realistic workloads, identify hot queries.&lt;/td&gt;
&lt;td&gt;Slow‑query log, pg_stat_statements, Query Store (SQL Server), AWR (Oracle), application logs.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;B. Diagnose&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Understand &lt;em&gt;why&lt;/em&gt; a query is slow.&lt;/td&gt;
&lt;td&gt;EXPLAIN / EXPLAIN ANALYZE, visual explain plans, &lt;code&gt;SHOW PROFILE&lt;/code&gt;, DMVs.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;C. Refactor&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Rewrite / index / restructure to eliminate bottlenecks.&lt;/td&gt;
&lt;td&gt;DDL changes, query rewrite, materialized views, partitioning.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;D. Validate&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Verify that performance improved without regressions.&lt;/td&gt;
&lt;td&gt;Same metrics as baseline, regression test suite, load‑testing tools (JMeter, k6).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;E. Guard&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Prevent regressions in the future.&lt;/td&gt;
&lt;td&gt;CI checks, automated plan‑stability tests, query‑cost thresholds, monitoring alerts.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  3. Foundations – “First‑Principles” Checks
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Checklist&lt;/th&gt;
&lt;th&gt;Why it matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;1&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Use the right data types&lt;/strong&gt; – avoid &lt;code&gt;VARCHAR(255)&lt;/code&gt; for a 2‑digit code; prefer &lt;code&gt;CHAR(2)&lt;/code&gt; or &lt;code&gt;SMALLINT&lt;/code&gt;.&lt;/td&gt;
&lt;td&gt;Smaller rows → more fits in memory → fewer page reads.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;2&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Never SELECT * in production&lt;/strong&gt; – enumerate columns.&lt;/td&gt;
&lt;td&gt;Reduces I/O, avoids unnecessary row‑locking and cache pressure.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Keep transactions short&lt;/strong&gt; – commit ASAP.&lt;/td&gt;
&lt;td&gt;Reduces lock contention and dead‑lock probability.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;4&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Prefer set‑based operations&lt;/strong&gt; over row‑by‑row loops (cursor, WHILE).&lt;/td&gt;
&lt;td&gt;DB engines are built for set processing; loops cause massive overhead.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;5&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Avoid functions on indexed columns&lt;/strong&gt; in WHERE/JOIN (e.g., &lt;code&gt;WHERE DATE(col) = ...&lt;/code&gt;).&lt;/td&gt;
&lt;td&gt;Index cannot be used → full scan. Use computed/derived columns or range predicates instead.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;6&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Parameterise queries&lt;/strong&gt; (prepared statements) – not string concatenation.&lt;/td&gt;
&lt;td&gt;Enables plan reuse, prevents SQL injection, reduces parsing overhead.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;7&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Watch out for implicit conversions&lt;/strong&gt; – &lt;code&gt;WHERE int_col = '123'&lt;/code&gt; forces a scan.&lt;/td&gt;
&lt;td&gt;Force implicit cast on the column side kills the index.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  4. Indexing – The Most Powerful Lever
&lt;/h2&gt;

&lt;h3&gt;
  
  
  4.1. Types of Indexes (quick reference)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;DBMS&lt;/th&gt;
&lt;th&gt;B‑Tree&lt;/th&gt;
&lt;th&gt;Hash&lt;/th&gt;
&lt;th&gt;GiST / SP‑GiST&lt;/th&gt;
&lt;th&gt;BRIN&lt;/th&gt;
&lt;th&gt;Columnstore&lt;/th&gt;
&lt;th&gt;Full‑Text&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;MySQL&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅ (Memory)&lt;/td&gt;
&lt;td&gt;✗&lt;/td&gt;
&lt;td&gt;✅ (8.0+)&lt;/td&gt;
&lt;td&gt;✅ (InnoDB/ColumnStore)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PostgreSQL&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅ (hash)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅ (cstore_fdw)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SQL Server&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅ (Memory‑Optimized)&lt;/td&gt;
&lt;td&gt;✅ (spatial)&lt;/td&gt;
&lt;td&gt;✅ (SQL Server 2019+)&lt;/td&gt;
&lt;td&gt;✅ (Columnstore)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Oracle&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅ (in‑memory)&lt;/td&gt;
&lt;td&gt;✅ (Spatial)&lt;/td&gt;
&lt;td&gt;✅ (approx.)&lt;/td&gt;
&lt;td&gt;✅ (Hybrid)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  4.2. Index Design Process
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Identify the hot predicates&lt;/strong&gt; – the columns that appear in &lt;code&gt;WHERE&lt;/code&gt;, &lt;code&gt;JOIN ON&lt;/code&gt;, &lt;code&gt;ORDER BY&lt;/code&gt;, &lt;code&gt;GROUP BY&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check cardinality&lt;/strong&gt; – high cardinality (many distinct values) = good for B‑Tree; low cardinality may benefit from &lt;strong&gt;bitmap&lt;/strong&gt; (PostgreSQL &lt;code&gt;BRIN&lt;/code&gt;/&lt;code&gt;GIN&lt;/code&gt;, Oracle bitmap).
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Covering (Include) columns&lt;/strong&gt; – add non‑key columns to an index so the query can be satisfied entirely from the index (MySQL Server &lt;code&gt;INCLUDE&lt;/code&gt;, PostgreSQL &lt;code&gt;INCLUDE&lt;/code&gt;, MySQL &lt;code&gt;covering&lt;/code&gt; via &lt;code&gt;SELECT&lt;/code&gt; that matches the index).
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Composite index ordering&lt;/strong&gt; – order columns by &lt;strong&gt;filtering power first&lt;/strong&gt;, then by &lt;strong&gt;sorting/grouping&lt;/strong&gt;.

&lt;ul&gt;
&lt;li&gt;Example: &lt;code&gt;WHERE status = ? AND created_at &amp;gt; ? ORDER BY created_at DESC&lt;/code&gt; → &lt;code&gt;(status, created_at DESC)&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Avoid over‑indexing&lt;/strong&gt; – each extra index adds write overhead (INSERT/UPDATE/DELETE). Keep an &lt;strong&gt;index‑to‑write‑ratio&lt;/strong&gt; &amp;lt; 1 for OLTP workloads.
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  4.3. Practical Index Recipes
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Recommended Index (MySQL/PostgreSQL)&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Exact lookup&lt;/strong&gt; on &lt;code&gt;user_id&lt;/code&gt; (PK)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PRIMARY KEY (user_id)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Already optimal.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Range + filter&lt;/strong&gt;: &lt;code&gt;WHERE country = ? AND ts &amp;gt;= ?&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;(country, ts)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Country filters first, then range on ts.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Sorting&lt;/strong&gt;: &lt;code&gt;WHERE status = ? ORDER BY created_at DESC LIMIT 20&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;(status, created_at DESC)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Uses index for both filter and order, &lt;code&gt;LIMIT&lt;/code&gt; stops early.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Join&lt;/strong&gt;: &lt;code&gt;ON o.customer_id = c.id&lt;/code&gt; and &lt;code&gt;c.region = ?&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;customer (region, id)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Region filter first, then join column.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Full‑text search&lt;/strong&gt; on &lt;code&gt;title&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;FULLTEXT(title)&lt;/code&gt; (MySQL) / &lt;code&gt;GIN(to_tsvector('english', title))&lt;/code&gt; (PostgreSQL)&lt;/td&gt;
&lt;td&gt;Enables inverted index.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Very large table&lt;/strong&gt; &amp;gt; 100 M rows, queries on recent data&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;BRIN&lt;/strong&gt; on timestamp column (&lt;code&gt;ts&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;BRIN stores min/max per block → fast for “newest N rows”.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Frequent aggregates&lt;/strong&gt;: &lt;code&gt;GROUP BY product_id&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;HASH&lt;/code&gt; index (PostgreSQL) or &lt;strong&gt;clustered&lt;/strong&gt; index (SQL Server) on &lt;code&gt;product_id&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Hash index speeds up equality grouping.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  4.4. Maintaining Index Health
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;How to do it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Detect unused indexes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;MySQL &lt;code&gt;performance_schema&lt;/code&gt;.&lt;code&gt;events_statements_summary_by_digest&lt;/code&gt;; PostgreSQL &lt;code&gt;pg_stat_user_indexes&lt;/code&gt;; SQL Server &lt;code&gt;sys.dm_db_index_usage_stats&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rebuild / reorganise&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;MySQL &lt;code&gt;OPTIMIZE TABLE&lt;/code&gt;; PostgreSQL &lt;code&gt;REINDEX&lt;/code&gt; or &lt;code&gt;VACUUM (FULL, ANALYZE)&lt;/code&gt;; SQL Server &lt;code&gt;ALTER INDEX REBUILD&lt;/code&gt;; Oracle &lt;code&gt;ALTER INDEX REBUILD&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Update statistics&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;MySQL &lt;code&gt;ANALYZE TABLE&lt;/code&gt;; PostgreSQL &lt;code&gt;ANALYZE&lt;/code&gt;; SQL Server &lt;code&gt;UPDATE STATISTICS&lt;/code&gt;; Oracle &lt;code&gt;DBMS_STATS.GATHER_TABLE_STATS&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Monitor index bloat&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;PostgreSQL &lt;code&gt;pgstattuple&lt;/code&gt;, MySQL &lt;code&gt;SHOW TABLE STATUS&lt;/code&gt;; SQL Server &lt;code&gt;sys.dm_db_index_physical_stats&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  5. Query‑Plan Analysis – Reading EXPLAIN
&lt;/h2&gt;

&lt;h3&gt;
  
  
  5.1. Common Plan Nodes &amp;amp; Their Cost
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Node&lt;/th&gt;
&lt;th&gt;Typical Meaning&lt;/th&gt;
&lt;th&gt;Red Flag&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Seq Scan / Table Scan&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Full table read&lt;/td&gt;
&lt;td&gt;Expect an index.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Index Scan&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Reads index entries (may still fetch table rows).&lt;/td&gt;
&lt;td&gt;OK if selective.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Index Only Scan&lt;/strong&gt; (PostgreSQL)&lt;/td&gt;
&lt;td&gt;Index satisfies query completely.&lt;/td&gt;
&lt;td&gt;Ideal!&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Bitmap Index Scan → Bitmap Heap Scan&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Combines many indexes; may be slower than a single index.&lt;/td&gt;
&lt;td&gt;Consider a composite index.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Nested Loop Join&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;For each row from outer, probe inner.&lt;/td&gt;
&lt;td&gt;Good for small outer side, otherwise consider Hash or Merge join.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Hash Join&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Builds hash table on inner side.&lt;/td&gt;
&lt;td&gt;Works well for large, unsorted data.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Merge Join&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Both sides must be sorted (often via index).&lt;/td&gt;
&lt;td&gt;Efficient if both inputs already ordered.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Sort&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Explicit sort operation.&lt;/td&gt;
&lt;td&gt;Try to push ordering into index.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Aggregate&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Grouping/aggregation.&lt;/td&gt;
&lt;td&gt;If &lt;code&gt;GROUP BY&lt;/code&gt; matches index order → “GroupAggregate” (faster).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Subquery/CTE Materialisation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Temporary result set.&lt;/td&gt;
&lt;td&gt;May be avoidable with rewrite.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  5.2. Example: MySQL EXPLAIN
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;EXPLAIN&lt;/span&gt; &lt;span class="n"&gt;FORMAT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;JSON&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;  &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;    &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt;    &lt;span class="n"&gt;customers&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;   &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'paid'&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt;   &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="s1"&gt;'2025-01-01'&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result (simplified):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"query_block"&lt;/span&gt;&lt;span class="p"&gt;:&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="nl"&gt;"select_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"cost_info"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"query_cost"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"319.50"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"table"&lt;/span&gt;&lt;span class="p"&gt;:&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="nl"&gt;"table_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"orders"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"access_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"range"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"possible_keys"&lt;/span&gt;&lt;span class="p"&gt;:[&lt;/span&gt;&lt;span class="s2"&gt;"idx_status_created"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"idx_status_created"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"used_key_parts"&lt;/span&gt;&lt;span class="p"&gt;:[&lt;/span&gt;&lt;span class="s2"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"rows_examined_per_scan"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"filtered"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;85.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"attached_condition"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"(`orders`.`status` = 'paid')"&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="nl"&gt;"inner join"&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="p"&gt;}&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Interpretation&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;range&lt;/code&gt; on &lt;code&gt;idx_status_created&lt;/code&gt; → good (uses composite index).
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;rows_examined_per_scan&lt;/code&gt; low → index selective.
&lt;/li&gt;
&lt;li&gt;No separate &lt;code&gt;sort&lt;/code&gt; node → ordering satisfied by index (&lt;code&gt;created_at DESC&lt;/code&gt;).
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you see &lt;code&gt;type: ALL&lt;/code&gt; (full scan) or a separate &lt;code&gt;sort&lt;/code&gt; step, you likely need a better index or query rewrite.&lt;/p&gt;

&lt;h3&gt;
  
  
  5.3. PostgreSQL EXPLAIN ANALYZE
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;EXPLAIN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;ANALYZE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BUFFERS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FORMAT&lt;/span&gt; &lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;   &lt;span class="n"&gt;orders&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;  &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'paid'&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt;  &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="k"&gt;CURRENT_DATE&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt;  &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt;  &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Typical output snippet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"Plan"&lt;/span&gt;&lt;span class="p"&gt;:&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="nl"&gt;"Node Type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Index Scan Backward"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Relation Name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"orders"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Index Name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"idx_status_created"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Index Cond"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"(status = 'paid'::text)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Filter"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"(created_at &amp;gt;= now())"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Rows Removed by Filter"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Buffers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"Shared Hit Blocks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"Shared Read Blocks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Actual Total Time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.78&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Actual Loops"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&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="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key take‑aways&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Index Scan Backward&lt;/code&gt; = using index for descending order → no sort needed.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Buffers&lt;/code&gt; shows almost all data served from cache → good.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you see &lt;code&gt;Seq Scan&lt;/code&gt; instead, add a composite index or rewrite the predicate.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Advanced Physical Design
&lt;/h2&gt;

&lt;h3&gt;
  
  
  6.1. Partitioning (Horizontal Sharding)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;DBMS&lt;/th&gt;
&lt;th&gt;Partitioning Styles&lt;/th&gt;
&lt;th&gt;When to use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;MySQL 8+&lt;/td&gt;
&lt;td&gt;Range, List, Hash, Key, Composite&lt;/td&gt;
&lt;td&gt;10 M+ rows, time‑series, multi‑tenant data.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PostgreSQL 13+&lt;/td&gt;
&lt;td&gt;Declarative range/list/hash partitions, sub‑partitioning.&lt;/td&gt;
&lt;td&gt;Same as MySQL + very large tables (&amp;gt; 500 M).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SQL Server&lt;/td&gt;
&lt;td&gt;Partitioned tables &amp;amp; indexes (range); can be on any column.&lt;/td&gt;
&lt;td&gt;OLTP with massive data, need to prune partitions.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Oracle&lt;/td&gt;
&lt;td&gt;Range/List/Hash partitions; interval partitioning.&lt;/td&gt;
&lt;td&gt;Enterprise data warehouses, multi‑year history.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Best practice&lt;/strong&gt;: Partition on a column that appears in most range predicates (e.g., &lt;code&gt;created_at&lt;/code&gt;). Keep the partition key &lt;strong&gt;immutable&lt;/strong&gt; (no updates).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- MySQL example&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;BIGINT&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="nb"&gt;ENUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'new'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'paid'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;'canceled'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="nb"&gt;DATETIME&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt; 
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;PARTITION&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="k"&gt;RANGE&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;YEAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;PARTITION&lt;/span&gt; &lt;span class="n"&gt;p2023&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="k"&gt;LESS&lt;/span&gt; &lt;span class="k"&gt;THAN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2024&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="k"&gt;PARTITION&lt;/span&gt; &lt;span class="n"&gt;p2024&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="k"&gt;LESS&lt;/span&gt; &lt;span class="k"&gt;THAN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2025&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="k"&gt;PARTITION&lt;/span&gt; &lt;span class="n"&gt;pmax&lt;/span&gt;  &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="k"&gt;LESS&lt;/span&gt; &lt;span class="k"&gt;THAN&lt;/span&gt; &lt;span class="k"&gt;MAXVALUE&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  6.2. Sharding (Application‑Level Horizontal Scaling)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Technique&lt;/th&gt;
&lt;th&gt;When to consider&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Key‑based sharding&lt;/strong&gt; (e.g., &lt;code&gt;user_id % N&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Very high write rates, &amp;gt; 10 M writes/sec.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Directory‑based sharding&lt;/strong&gt; (lookup table)&lt;/td&gt;
&lt;td&gt;Uneven data distribution, need flexibility.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Consistent hashing&lt;/strong&gt; (e.g., using Vitess, Citus, CockroachDB)&lt;/td&gt;
&lt;td&gt;Need to add/remove shards with minimal rebalancing.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Hybrid&lt;/strong&gt; (partition + shard)&lt;/td&gt;
&lt;td&gt;Multi‑tenant SaaS with per‑tenant isolation.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Implementation tip&lt;/strong&gt;: Keep the &lt;strong&gt;shard key in every table&lt;/strong&gt; that needs to be joined (e.g., &lt;code&gt;tenant_id&lt;/code&gt;). This allows joins to stay within a single shard.&lt;/p&gt;

&lt;h3&gt;
  
  
  6.3. Materialised Views &amp;amp; Summary Tables
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;When&lt;/strong&gt;: Pre‑aggregate heavy reports (&lt;code&gt;GROUP BY&lt;/code&gt; on large fact tables).
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How&lt;/strong&gt;: Use DB‑native materialised view (PostgreSQL &lt;code&gt;CREATE MATERIALIZED VIEW&lt;/code&gt;, Oracle &lt;code&gt;MATERIALIZED VIEW&lt;/code&gt;, SQL Server &lt;code&gt;Indexed View&lt;/code&gt;).
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Refresh&lt;/strong&gt;: &lt;code&gt;REFRESH FAST&lt;/code&gt; (incremental) or schedule nightly &lt;code&gt;REFRESH COMPLETE&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;  &lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;MATERIALIZED&lt;/span&gt; &lt;span class="k"&gt;VIEW&lt;/span&gt; &lt;span class="n"&gt;daily_sales&lt;/span&gt;
  &lt;span class="n"&gt;BUILD&lt;/span&gt; &lt;span class="k"&gt;IMMEDIATE&lt;/span&gt;
  &lt;span class="n"&gt;REFRESH&lt;/span&gt; &lt;span class="n"&gt;FAST&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="k"&gt;COMMIT&lt;/span&gt;
  &lt;span class="k"&gt;AS&lt;/span&gt;
  &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;product_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_ts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;day&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;total_sales&lt;/span&gt;
  &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;
  &lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;product_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_ts&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  6.4. Columnstore / OLAP Optimisations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SQL Server&lt;/strong&gt;: &lt;code&gt;CREATE CLUSTERED COLUMNSTORE INDEX&lt;/code&gt; on large fact tables → massive compression + vectorised scans.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PostgreSQL&lt;/strong&gt;: Use &lt;code&gt;cstore_fdw&lt;/code&gt; or &lt;code&gt;timescaledb&lt;/code&gt; hypertables for time‑series.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MySQL&lt;/strong&gt;: &lt;code&gt;COLUMNSTORE&lt;/code&gt; engine (MariaDB) or &lt;code&gt;InnoDB&lt;/code&gt; with &lt;code&gt;ROW_FORMAT=COMPRESSED&lt;/code&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Rule of thumb&lt;/strong&gt;: Use row‑store for OLTP (high‑frequency point reads/writes). Use column‑store for analytical queries that scan many rows but only a few columns.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Application‑Side Tactics
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tactic&lt;/th&gt;
&lt;th&gt;Why it matters&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Connection Pooling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Reduces handshake overhead, keeps sockets warm.&lt;/td&gt;
&lt;td&gt;HikariCP (Java), pgBouncer (Postgres), ProxySQL (MySQL).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Prepared‑statement caching&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Avoids re‑parsing, re‑planning.&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;PDO::ATTR_EMULATE_PREPARES = false&lt;/code&gt; (PHP).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Batch inserts/updates&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fewer round‑trips, better transaction granularity.&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;INSERT INTO t (a,b) VALUES (?,?),(?,?),(?,?)&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Read‑through / Write‑behind cache&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Offloads hot reads to Redis / Memcached.&lt;/td&gt;
&lt;td&gt;Cache product catalog for 5 min, invalidate on write.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Avoid N+1 queries&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Reduce number of round‑trips.&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;JOIN&lt;/code&gt; or &lt;code&gt;IN (...)&lt;/code&gt; instead of fetching children per parent.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pagination strategy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;OFFSET&lt;/code&gt; gets slower with deep pages.&lt;/td&gt;
&lt;td&gt;Use &lt;strong&gt;keyset pagination&lt;/strong&gt; (&lt;code&gt;WHERE id &amp;gt; last_seen_id ORDER BY id&lt;/code&gt;).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Retry with exponential back‑off&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Handles transient dead‑locks or connection spikes gracefully.&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;retry(attempt =&amp;gt; 1..5, backoff =&amp;gt; 2^attempt * 100ms)&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Circuit breaker&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Prevents cascading failures when DB is overloaded.&lt;/td&gt;
&lt;td&gt;Hystrix / &lt;code&gt;resilience4j&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  8. Monitoring &amp;amp; Alerting
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Typical Threshold (OLTP)&lt;/th&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Avg query latency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&amp;lt; 5 ms (core reads)&lt;/td&gt;
&lt;td&gt;Prometheus + Grafana (&lt;code&gt;pg_stat_statements.mean_time&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;95th‑percentile latency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&amp;lt; 20 ms&lt;/td&gt;
&lt;td&gt;DataDog, New Relic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Cache hit ratio&lt;/strong&gt; (buffer pool)&lt;/td&gt;
&lt;td&gt;&amp;gt; 95 %&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;SHOW ENGINE INNODB STATUS&lt;/code&gt; (MySQL), &lt;code&gt;pg_buffercache&lt;/code&gt; (Postgres)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lock wait time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&amp;lt; 10 ms&lt;/td&gt;
&lt;td&gt;&lt;code&gt;performance_schema.events_waits_summary_by_thread_by_event_name&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dead‑lock count&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;0 (or &amp;lt; 1 per hour)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SHOW ENGINE INNODB STATUS&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Replication lag&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&amp;lt; 1 s&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;SHOW SLAVE STATUS&lt;/code&gt; (MySQL), &lt;code&gt;pg_stat_replication&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Disk I/O&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&amp;lt; 80 % of provisioned bandwidth&lt;/td&gt;
&lt;td&gt;iostat, CloudWatch&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Alert example (Prometheus)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;alert&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HighQueryLatency&lt;/span&gt;
  &lt;span class="na"&gt;expr&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;avg_over_time(pg_stat_statements_mean_time[5m]) &amp;gt; &lt;/span&gt;&lt;span class="m"&gt;0.02&lt;/span&gt;
  &lt;span class="na"&gt;for&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2m&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;severity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;critical&lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Average&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;PostgreSQL&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;query&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;latency&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;20 ms"&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Investigate&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;slow&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;queries;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;check&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;execution&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;plans."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  9. End‑to‑End Optimization Checklist
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;✅&lt;/th&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;How to Verify&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;All hot queries have &lt;strong&gt;covering indexes&lt;/strong&gt; (or at least an index on filter columns).&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;EXPLAIN&lt;/code&gt; shows &lt;code&gt;Index Scan&lt;/code&gt; / &lt;code&gt;Index Only Scan&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;No &lt;strong&gt;SELECT *&lt;/strong&gt; in production code.&lt;/td&gt;
&lt;td&gt;Code review / static analysis.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Statistics are &lt;strong&gt;up‑to‑date&lt;/strong&gt; (ANALYZE run after bulk loads).&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;SELECT relname, n_live_tup FROM pg_stat_user_tables;&lt;/code&gt; compare with `pg_class&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
    </item>
    <item>
      <title>Microservices Design Patterns in Java</title>
      <dc:creator>Gaurav</dc:creator>
      <pubDate>Mon, 01 Sep 2025 15:52:33 +0000</pubDate>
      <link>https://forem.com/dixitgurv/microservices-design-patterns-in-java-3pfk</link>
      <guid>https://forem.com/dixitgurv/microservices-design-patterns-in-java-3pfk</guid>
      <description>&lt;h1&gt;
  
  
  Microservices Design Patterns in Java
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;(A practical guide for architects, senior engineers, and teams building cloud‑native systems with Spring Boot, Spring Cloud, Micronaut, Quarkus, and related libraries.)&lt;/em&gt;  &lt;/p&gt;




&lt;h2&gt;
  
  
  1️⃣ Why Patterns Matter in a Java Microservices Landscape
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Challenge&lt;/th&gt;
&lt;th&gt;Pattern Category&lt;/th&gt;
&lt;th&gt;Typical Java Tooling&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Service granularity &amp;amp; bounded contexts&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Decomposition&lt;/td&gt;
&lt;td&gt;Spring Boot, Micronaut, Quarkus, JPA/Hibernate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Service discovery &amp;amp; routing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Resilience to failures&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fault‑tolerance&lt;/td&gt;
&lt;td&gt;Resilience4j, Spring Cloud CircuitBreaker, Hystrix (legacy)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data consistency across services&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Transactional&lt;/td&gt;
&lt;td&gt;Saga, Event‑sourcing, Outbox&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Observability (metrics, logs, traces)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Monitoring&lt;/td&gt;
&lt;td&gt;Micrometer, Spring Cloud Sleuth, OpenTelemetry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Secure communication&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Security&lt;/td&gt;
&lt;td&gt;Spring Security, OAuth2 Resource Server, JWT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CI/CD &amp;amp; deployment automation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;DevOps&lt;/td&gt;
&lt;td&gt;Docker, Kubernetes, Helm, Skaffold, Tekton&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Design patterns give you a &lt;strong&gt;vocabulary&lt;/strong&gt; (e.g., “circuit‑breaker”) and a &lt;strong&gt;prescribed solution&lt;/strong&gt; that can be directly mapped to Java libraries, configuration, and code. Below is a curated catalogue of the most widely‑used patterns, why you’d pick them, and concrete Java snippets.&lt;/p&gt;




&lt;h2&gt;
  
  
  2️⃣ Decomposition Patterns (How to break the monolith)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;Goal&lt;/th&gt;
&lt;th&gt;When to Use&lt;/th&gt;
&lt;th&gt;Java Implementation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Domain‑Driven Design (DDD) Bounded Context&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Separate business capabilities into autonomous services.&lt;/td&gt;
&lt;td&gt;Complex domain with clear sub‑domains; need independent evolution.&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Spring Boot + JPA&lt;/strong&gt; (separate schema per service), &lt;strong&gt;Micronaut Data&lt;/strong&gt;, &lt;strong&gt;Quarkus Panache&lt;/strong&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;API‑First / Contract‑First&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Define service contracts (OpenAPI/Swagger) before implementation.&lt;/td&gt;
&lt;td&gt;Teams need clear contracts, automated client generation.&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;springdoc‑openapi&lt;/strong&gt;, &lt;strong&gt;OpenAPI Generator&lt;/strong&gt;, &lt;strong&gt;Micronaut OpenAPI&lt;/strong&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Strangler Fig&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Incrementally replace a monolith with micro‑services.&lt;/td&gt;
&lt;td&gt;Legacy monolith, low‑risk migration.&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Spring Cloud Gateway&lt;/strong&gt; for routing, &lt;strong&gt;Feature Flags&lt;/strong&gt; (Togglz).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Self‑Contained System (SCS)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;End‑to‑end ownership (UI + backend) per service.&lt;/td&gt;
&lt;td&gt;Teams own full stack; UI composition needed (e.g., micro‑frontends).&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Spring Boot&lt;/strong&gt; + &lt;strong&gt;Thymeleaf&lt;/strong&gt; or &lt;strong&gt;React&lt;/strong&gt; built into the same Docker image.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; In Java, the &lt;em&gt;bounded context&lt;/em&gt; often maps to a &lt;strong&gt;module&lt;/strong&gt; (Maven/Gradle sub‑project) with its own &lt;code&gt;application.yml&lt;/code&gt; profile and isolated database schema.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  3️⃣ Integration Patterns (How services talk)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Typical Java Stack&lt;/th&gt;
&lt;th&gt;Code Sketch&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;API Gateway&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Single entry point, request routing, cross‑cutting concerns (auth, throttling).&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Spring Cloud Gateway&lt;/strong&gt;, &lt;strong&gt;Netflix Zuul&lt;/strong&gt; (legacy), &lt;strong&gt;Kong&lt;/strong&gt; (external).&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
 &lt;code&gt;java @Bean public RouteLocator routes(RouteLocatorBuilder b) { return b.routes() .route("users", r -&amp;gt; r.path("/users/**") .uri("lb://USER-SERVICE")) .build(); }&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
 |&lt;br&gt;
| &lt;strong&gt;Service Discovery&lt;/strong&gt; | Dynamic lookup of service instances. | &lt;strong&gt;Eureka&lt;/strong&gt;, &lt;strong&gt;Consul&lt;/strong&gt;, &lt;strong&gt;Zookeeper&lt;/strong&gt;, &lt;strong&gt;Kubernetes DNS&lt;/strong&gt;. |&lt;br&gt;
&lt;br&gt;
 &lt;code&gt;yaml spring: application: name: order-service cloud: discovery: client: simple: enabled: true&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
 |&lt;br&gt;
| &lt;strong&gt;Client‑Side Load Balancing&lt;/strong&gt; | Distribute calls across multiple instances. | &lt;strong&gt;Spring Cloud LoadBalancer&lt;/strong&gt;, &lt;strong&gt;Ribbon&lt;/strong&gt; (legacy), &lt;strong&gt;Resilience4j&lt;/strong&gt;'s &lt;code&gt;Bulkhead&lt;/code&gt;. |&lt;br&gt;
&lt;br&gt;
 &lt;code&gt;java @LoadBalanced RestTemplate restTemplate() { return new RestTemplate(); }&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
 |&lt;br&gt;
| &lt;strong&gt;Synchronous Request‑Response (REST/HTTP)&lt;/strong&gt; | Simple, low‑latency calls. | &lt;strong&gt;Spring WebFlux&lt;/strong&gt; (reactive) or &lt;strong&gt;Spring MVC&lt;/strong&gt; (blocking). |&lt;br&gt;
&lt;br&gt;
 &lt;code&gt;java @RestController class OrderController { @GetMapping("/orders/{id}") public OrderDto get(@PathVariable Long id) { return orderService.find(id); } }&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
 |&lt;br&gt;
| &lt;strong&gt;Asynchronous Messaging&lt;/strong&gt; | Decouple producers/consumers, enable eventual consistency. | &lt;strong&gt;Spring Cloud Stream&lt;/strong&gt;, &lt;strong&gt;Kafka&lt;/strong&gt;, &lt;strong&gt;RabbitMQ&lt;/strong&gt;, &lt;strong&gt;Pulsar&lt;/strong&gt;, &lt;strong&gt;ActiveMQ&lt;/strong&gt;. |&lt;br&gt;
&lt;br&gt;
 &lt;code&gt;java @EnableBinding(Source.class) class OrderProducer { @Autowired Source source; void publish(OrderEvent e) { source.output().send(MessageBuilder.withPayload(e).build()); } }&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
 |&lt;br&gt;
| &lt;strong&gt;Event‑Driven Architecture (EDA)&lt;/strong&gt; | React to domain events, enable saga orchestration. | &lt;strong&gt;Axon Framework&lt;/strong&gt;, &lt;strong&gt;Eventuate&lt;/strong&gt;, &lt;strong&gt;Kafka Streams&lt;/strong&gt;, &lt;strong&gt;Debezium&lt;/strong&gt;. |&lt;br&gt;
&lt;br&gt;
 &lt;code&gt;java @EventHandler public void on(OrderCreated e) { // update read model }&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
 |&lt;br&gt;
| &lt;strong&gt;API Composition (Aggregator)&lt;/strong&gt; | Combine multiple service calls into a single response. | &lt;strong&gt;Spring Cloud Function&lt;/strong&gt;, &lt;strong&gt;GraphQL&lt;/strong&gt;, &lt;strong&gt;BFF pattern&lt;/strong&gt;. |&lt;br&gt;
&lt;br&gt;
 &lt;code&gt;java @RestController class CustomerBff { @GetMapping("/customer/{id}") public CustomerDto get(@PathVariable Long id) { CustomerDto c = customerClient.get(id); List&amp;lt;OrderDto&amp;gt; o = orderClient.getByCustomer(id); c.setOrders(o); return c; } }&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
 |&lt;/p&gt;


&lt;h2&gt;
  
  
  4️⃣ Data Management Patterns (How to keep data consistent)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;Goal&lt;/th&gt;
&lt;th&gt;When to Use&lt;/th&gt;
&lt;th&gt;Java‑centric Implementation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Database per Service&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Strong isolation, independent schema evolution.&lt;/td&gt;
&lt;td&gt;All services own their data.&lt;/td&gt;
&lt;td&gt;JPA/Hibernate with separate &lt;code&gt;DataSource&lt;/code&gt; bean per service.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Shared Database (Anti‑Pattern)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Quick start but couples services.&lt;/td&gt;
&lt;td&gt;Legacy migration phase only.&lt;/td&gt;
&lt;td&gt;Avoid; instead use &lt;strong&gt;Read‑Only Views&lt;/strong&gt; if needed.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Saga (Transaction Orchestration)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Distributed transaction across services without 2PC.&lt;/td&gt;
&lt;td&gt;Multi‑step business process (e.g., order → payment → inventory).&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Axon&lt;/strong&gt; (orchestrated), &lt;strong&gt;Eventuate Tram&lt;/strong&gt;, &lt;strong&gt;Camunda BPM&lt;/strong&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Saga (Choreography)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Services publish events; others react autonomously.&lt;/td&gt;
&lt;td&gt;Simple flows, want loose coupling.&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Kafka&lt;/strong&gt; + &lt;strong&gt;Spring Cloud Stream&lt;/strong&gt;; each service subscribes to its own topic.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Outbox Pattern&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Guarantees atomic write + message publish.&lt;/td&gt;
&lt;td&gt;Need reliable event publishing from a DB transaction.&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Debezium&lt;/strong&gt; + &lt;code&gt;outbox&lt;/code&gt; table; or &lt;strong&gt;Spring Transactional outbox&lt;/strong&gt; library.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CQRS (Command‑Query Responsibility Segregation)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Separate write model from read model for scalability.&lt;/td&gt;
&lt;td&gt;High read‑write imbalance.&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Axon&lt;/strong&gt;, &lt;strong&gt;Spring Data JDBC&lt;/strong&gt; for command side, &lt;strong&gt;Spring Data JPA&lt;/strong&gt; + &lt;strong&gt;Elasticsearch&lt;/strong&gt; for query side.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Event Sourcing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Persist state as a series of events.&lt;/td&gt;
&lt;td&gt;Auditable, immutable history required.&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Axon Framework&lt;/strong&gt;, &lt;strong&gt;EventStoreDB&lt;/strong&gt;, &lt;strong&gt;JPA + EventEntity&lt;/strong&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cache‑Aside / Read‑Through&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Reduce latency for hot data.&lt;/td&gt;
&lt;td&gt;Frequently read data, tolerant of slight staleness.&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Spring Cache&lt;/strong&gt; (&lt;code&gt;@Cacheable&lt;/code&gt;) + &lt;strong&gt;Redis&lt;/strong&gt; or &lt;strong&gt;Caffeine&lt;/strong&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Sample Saga (Orchestration) using Spring Cloud &amp;amp; Resilience4j&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Service&lt;/span&gt;
&lt;span class="nd"&gt;@RequiredArgsConstructor&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderSaga&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;PaymentClient&lt;/span&gt; &lt;span class="n"&gt;paymentClient&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;InventoryClient&lt;/span&gt; &lt;span class="n"&gt;inventoryClient&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;OrderRepository&lt;/span&gt; &lt;span class="n"&gt;orderRepo&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Transactional&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;OrderDto&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;OrderEntity&lt;/span&gt; &lt;span class="n"&gt;ent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;orderRepo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;OrderMapper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toEntity&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

        &lt;span class="c1"&gt;// 1️⃣ Reserve inventory (synchronous)&lt;/span&gt;
        &lt;span class="n"&gt;inventoryClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;reserve&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getItems&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onFailure&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;throwable&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;compensateInventory&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ent&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;retry&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;subscribe&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// 2️⃣ Charge payment (async with circuit‑breaker)&lt;/span&gt;
        &lt;span class="n"&gt;paymentClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;charge&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getPaymentInfo&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;transform&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;CircuitBreaker&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofDefaults&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"paymentCB"&lt;/span&gt;&lt;span class="o"&gt;)::&lt;/span&gt;&lt;span class="n"&gt;decorateFunction&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onSuccess&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;completeOrder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ent&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onFailure&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;compensatePayment&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ent&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;compensateInventory&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;OrderEntity&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;inventoryClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;release&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="n"&gt;orderRepo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;delete&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;compensatePayment&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;OrderEntity&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;paymentClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;refund&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getPaymentId&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="c1"&gt;// maybe also release inventory&lt;/span&gt;
        &lt;span class="n"&gt;compensateInventory&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;completeOrder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;OrderEntity&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setStatus&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Status&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;COMPLETED&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;orderRepo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Key points:&lt;/em&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Resilience4j&lt;/strong&gt; provides the circuit‑breaker (&lt;code&gt;CircuitBreaker.ofDefaults&lt;/code&gt;).
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reactive&lt;/strong&gt; (&lt;code&gt;Mono&lt;/code&gt;/&lt;code&gt;Flux&lt;/code&gt;) ensures non‑blocking**.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compensation&lt;/strong&gt; methods implement the saga’s rollback logic.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  5️⃣ Resilience &amp;amp; Fault‑Tolerance Patterns
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;Synopsis&lt;/th&gt;
&lt;th&gt;Java Tools&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Circuit Breaker&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Short‑circuit failing calls; open → close after cooldown.&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Resilience4j&lt;/strong&gt;, &lt;strong&gt;Spring Cloud CircuitBreaker&lt;/strong&gt;, &lt;strong&gt;Hystrix&lt;/strong&gt; (legacy).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Retry&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Re‑attempt transient failures with back‑off.&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Resilience4j Retry&lt;/strong&gt;, &lt;strong&gt;Spring Retry&lt;/strong&gt; (&lt;code&gt;@Retryable&lt;/code&gt;).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Bulkhead&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Limit concurrent calls per service to prevent cascading failures.&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Resilience4j Bulkhead&lt;/strong&gt;, &lt;strong&gt;ThreadPoolBulkhead&lt;/strong&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Timeout&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fail fast if a remote call exceeds SLA.&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Resilience4j TimeLimiter&lt;/strong&gt;, &lt;strong&gt;WebClient.timeout&lt;/strong&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Fallback&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Provide a default response when a service is unavailable.&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;fallbackMethod&lt;/code&gt; in &lt;strong&gt;Feign&lt;/strong&gt;, &lt;code&gt;@CircuitBreaker(fallbackMethod="...")&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Rate Limiting&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Throttle incoming traffic per client or per endpoint.&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Bucket4j&lt;/strong&gt;, &lt;strong&gt;Resilience4j RateLimiter&lt;/strong&gt;, &lt;strong&gt;Spring Cloud Gateway RateLimiter&lt;/strong&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Idempotent Consumer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Ensure message processing is safe against duplicates.&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Spring Integration IdempotentReceiver&lt;/strong&gt;, &lt;strong&gt;Kafka exactly‑once&lt;/strong&gt; (&lt;code&gt;enable.idempotence&lt;/code&gt;).&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Example: Circuit Breaker + Retry with Spring Cloud OpenFeign
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@FeignClient&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"inventory-service"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fallback&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;InventoryFallback&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
             &lt;span class="n"&gt;configuration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;InventoryClientConfig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;InventoryClient&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@PostMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/inventory/reserve"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@CircuitBreaker&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"inventoryCB"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fallbackMethod&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"reserveFallback"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@Retry&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"inventoryRetry"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;maxAttempts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nc"&gt;ReservationResponse&lt;/span&gt; &lt;span class="nf"&gt;reserve&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@RequestBody&lt;/span&gt; &lt;span class="nc"&gt;ReservationRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Fallback bean&lt;/span&gt;
&lt;span class="nd"&gt;@Component&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;InventoryFallback&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;InventoryClient&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ReservationResponse&lt;/span&gt; &lt;span class="nf"&gt;reserve&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ReservationRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Return a safe “no‑stock” response&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ReservationResponse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Service unavailable – using cached data"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Feign config (Resilience4j integration)&lt;/span&gt;
&lt;span class="nd"&gt;@Configuration&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;InventoryClientConfig&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Bean&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;RetryConfig&lt;/span&gt; &lt;span class="nf"&gt;inventoryRetryConfig&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;RetryConfig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;custom&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;maxAttempts&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;waitDuration&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofMillis&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;retryExceptions&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;IOException&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;TimeoutException&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Bean&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;CircuitBreakerConfig&lt;/span&gt; &lt;span class="nf"&gt;inventoryCircuitBreakerConfig&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;CircuitBreakerConfig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;custom&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;failureRateThreshold&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;waitDurationInOpenState&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofSeconds&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;permittedNumberOfCallsInHalfOpenState&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Result:&lt;/em&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Calls to &lt;strong&gt;inventory-service&lt;/strong&gt; are automatically wrapped in a &lt;strong&gt;circuit‑breaker&lt;/strong&gt; and &lt;strong&gt;retry&lt;/strong&gt; policy.
&lt;/li&gt;
&lt;li&gt;If the circuit opens, Feign delegates to &lt;code&gt;InventoryFallback&lt;/code&gt;, guaranteeing a graceful degradation.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  6️⃣ Observability Patterns (Logging, Metrics, Tracing)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;What It Solves&lt;/th&gt;
&lt;th&gt;Java Stack&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Centralized Logging&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Correlate logs across services.&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Logback&lt;/strong&gt; + &lt;strong&gt;logstash‑logback‑encoder&lt;/strong&gt;, &lt;strong&gt;ELK&lt;/strong&gt;, &lt;strong&gt;Grafana Loki&lt;/strong&gt;, &lt;strong&gt;Spring Cloud Sleuth&lt;/strong&gt; (adds trace IDs).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Distributed Tracing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Follow a request across service boundaries.&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Spring Cloud Sleuth&lt;/strong&gt; (Zipkin), &lt;strong&gt;OpenTelemetry Java&lt;/strong&gt;, &lt;strong&gt;Jaeger&lt;/strong&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Metrics &amp;amp; Health Checks&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Export service‑level KP&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Micrometer&lt;/strong&gt; (Prometheus, Datadog, CloudWatch), &lt;strong&gt;Spring Boot Actuator&lt;/strong&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Structured Log Context&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Include &lt;code&gt;traceId&lt;/code&gt;, &lt;code&gt;spanId&lt;/code&gt;, &lt;code&gt;tenantId&lt;/code&gt; in every log line.&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;MDC.put("traceId", ...)&lt;/code&gt; automatically by Sleuth.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Alerting on SLAs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Detect latency spikes, error rates.&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Prometheus Alertmanager&lt;/strong&gt;, &lt;strong&gt;Grafana&lt;/strong&gt;, &lt;strong&gt;Opsgenie&lt;/strong&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Sample: OpenTelemetry + Micrometer (Spring Boot 3)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@SpringBootApplication&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PaymentService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;SpringApplication&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PaymentService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Bean&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;OpenTelemetry&lt;/span&gt; &lt;span class="nf"&gt;openTelemetry&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;OpenTelemetrySdk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setTracerProvider&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SdkTracerProvider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addSpanProcessor&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BatchSpanProcessor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                                &lt;span class="nc"&gt;OtlpGrpcSpanExporter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                                        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setEndpoint&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"http://otel-collector:4317"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                                        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
                                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
                        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setMeterProvider&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SdkMeterProvider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;registerMetricReader&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                                &lt;span class="nc"&gt;PeriodicMetricReader&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                                        &lt;span class="nc"&gt;OtlpGrpcMetricExporter&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// e.g., Prometheus exporter&lt;/span&gt;
                                        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setInterval&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofSeconds&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
                                        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
                        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Bean&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;MeterRegistryCustomizer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;MeterRegistry&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;metricsCustomizer&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;registry&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;commonTags&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"service"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"payment-service"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Result:&lt;/em&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every HTTP request gets a trace (&lt;code&gt;traceId&lt;/code&gt;) automatically via &lt;strong&gt;Spring Cloud Sleuth&lt;/strong&gt; (or &lt;code&gt;otel-spring-boot-starter&lt;/code&gt;).
&lt;/li&gt;
&lt;li&gt;Custom &lt;strong&gt;Micrometer&lt;/strong&gt; metrics (&lt;code&gt;payment_success_total&lt;/code&gt;, &lt;code&gt;payment_failure_total&lt;/code&gt;) are exported to Prometheus.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  7️⃣ Security Patterns
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;Use‑Case&lt;/th&gt;
&lt;th&gt;Java Implementation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Zero‑Trust API Gateway&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Enforce authentication/authorization centrally.&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Spring Cloud Gateway&lt;/strong&gt; + &lt;strong&gt;Spring Security OAuth2 Resource Server&lt;/strong&gt; (&lt;code&gt;JwtAuthenticationConverter&lt;/code&gt;).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Token‑Based Authentication (JWT)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Stateless auth across services.&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;spring-boot-starter-oauth2-resource-server&lt;/strong&gt;, &lt;strong&gt;jjwt&lt;/strong&gt;, &lt;strong&gt;Nimbus JOSE&lt;/strong&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Service‑to‑Service Mutual TLS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Secure inter‑service traffic.&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Spring Cloud Consul&lt;/strong&gt; (or &lt;strong&gt;Istio&lt;/strong&gt;), &lt;strong&gt;Spring Security&lt;/strong&gt; &lt;code&gt;SslContext&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Fine‑Grained Authorization (ABAC)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Policy‑driven access (attributes, scopes).&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;OPA (Open Policy Agent)&lt;/strong&gt; side‑car, &lt;strong&gt;Spring Security ACL&lt;/strong&gt;, &lt;strong&gt;Keycloak Authorization Services&lt;/strong&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Secret Management&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Centralized credentials, rotation.&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Spring Cloud Config&lt;/strong&gt; + &lt;strong&gt;Vault&lt;/strong&gt;, &lt;strong&gt;AWS Secrets Manager&lt;/strong&gt;, &lt;strong&gt;Azure Key Vault&lt;/strong&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Security Headers &amp;amp; CSP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Harden HTTP responses.&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Spring Security&lt;/strong&gt; &lt;code&gt;http.headers().contentSecurityPolicy(...)&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Example: JWT Resource Server with Spring Security
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@EnableWebSecurity&lt;/span&gt;
&lt;span class="nd"&gt;@Configuration&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SecurityConfig&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;WebSecurityConfigurerAdapter&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;protected&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;configure&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpSecurity&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;http&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;csrf&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;disable&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;authorizeRequests&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;antMatchers&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/public/**"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;permitAll&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;anyRequest&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;authenticated&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;and&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;oauth2ResourceServer&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;jwt&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;jwtAuthenticationConverter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jwtAuthConverter&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;JwtAuthenticationConverter&lt;/span&gt; &lt;span class="nf"&gt;jwtAuthConverter&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;JwtAuthenticationConverter&lt;/span&gt; &lt;span class="n"&gt;conv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;JwtAuthenticationConverter&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;conv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setJwtGrantedAuthoritiesConverter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jwt&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;Collection&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;scopes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jwt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getClaimAsStringList&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"scope"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;scopes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SimpleGrantedAuthority&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SCOPE_"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;collect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Collectors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toList&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="o"&gt;});&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;conv&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;All microservices that include this configuration will automatically validate incoming JWTs signed by the Authorization Server (Keycloak, Auth0, Okta, etc.).&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  8️⃣ DevOps &amp;amp; Deployment Patterns
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Java‑centric Tooling&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Docker‑Ready Build&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Produce an OCI image with minimal layers.&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Spring Boot 3&lt;/strong&gt; → &lt;code&gt;java -jar&lt;/code&gt; + &lt;code&gt;jlink&lt;/code&gt; for native images, &lt;strong&gt;Micronaut GraalVM Native&lt;/strong&gt;, &lt;strong&gt;Quarkus&lt;/strong&gt; native mode.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Kubernetes Sidecar&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Add capabilities (e.g., Envoy proxy, Prometheus exporter).&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Spring Cloud Kubernetes&lt;/strong&gt;, &lt;strong&gt;Istio&lt;/strong&gt;, &lt;strong&gt;Envoy&lt;/strong&gt;, &lt;strong&gt;OpenTelemetry Collector&lt;/strong&gt; sidecar.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Canary / Blue‑Green Deployments&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Gradual rollout, rollback safety.&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Argo Rollouts&lt;/strong&gt;, &lt;strong&gt;Spinnaker&lt;/strong&gt;, &lt;strong&gt;Helm&lt;/strong&gt; &lt;code&gt;--set&lt;/code&gt; values.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Infrastructure as Code (IaC)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Version control of deployment descriptors.&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Terraform&lt;/strong&gt;, &lt;strong&gt;Pulumi&lt;/strong&gt;, &lt;strong&gt;Kustomize&lt;/strong&gt;, &lt;strong&gt;Helm Charts&lt;/strong&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CI/CD Pipelines&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Automated build, test, release.&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;GitHub Actions&lt;/strong&gt;, &lt;strong&gt;GitLab CI&lt;/strong&gt;, &lt;strong&gt;Jenkins&lt;/strong&gt;, &lt;strong&gt;Tekton&lt;/strong&gt;, &lt;strong&gt;Skaffold&lt;/strong&gt; for dev loops.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Feature Flags&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Toggle functionality at runtime.&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Togglz&lt;/strong&gt;, &lt;strong&gt;LaunchDarkly&lt;/strong&gt;, &lt;strong&gt;FF4J&lt;/strong&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Sample Dockerfile (Spring Boot + JDK 21)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
dockerfile
# ---- Build Stage ----
FROM eclipse-temurin:21-jdk-alpine AS build
WORKDIR /app
COPY mvnw pom.xml ./
COPY .mvn .mvn
RUN ./mvnw dependency:go-offline -B
COPY src src
RUN ./mvnw package -DskipTests

# ----
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
    </item>
    <item>
      <title>AI-Assisted Code Review: Opportunities and Pitfalls</title>
      <dc:creator>Gaurav</dc:creator>
      <pubDate>Sun, 31 Aug 2025 17:37:16 +0000</pubDate>
      <link>https://forem.com/dixitgurv/ai-assisted-code-review-opportunities-and-pitfalls-llp</link>
      <guid>https://forem.com/dixitgurv/ai-assisted-code-review-opportunities-and-pitfalls-llp</guid>
      <description>&lt;h1&gt;
  
  
  AI‑Assisted Code Review: Opportunities and Pitfalls
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;An in‑depth guide for engineering leaders, architects, and developers who want to harness the power of large‑language‑model (LLM)‑based code‑review tools without falling into the common traps.&lt;/em&gt;  &lt;/p&gt;




&lt;h2&gt;
  
  
  1. Why AI‑assisted code review matters now
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Driver&lt;/th&gt;
&lt;th&gt;What it means for teams&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Scale of code&lt;/strong&gt; – Modern services often exceed millions of lines of code (LoC).&lt;/td&gt;
&lt;td&gt;Human reviewers can’t keep up with every PR.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Speed of delivery&lt;/strong&gt; – Continuous‑delivery pipelines push changes many times a day.&lt;/td&gt;
&lt;td&gt;Review latency becomes a bottleneck.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Security &amp;amp; compliance pressure&lt;/strong&gt; – Regulations (PCI‑DSS, GDPR, ISO‑27001) demand early detection of vulnerabilities.&lt;/td&gt;
&lt;td&gt;Automated static analysis + LLM reasoning can surface hidden risks.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Talent shortage&lt;/strong&gt; – Senior engineers are scarce; junior hires need mentorship.&lt;/td&gt;
&lt;td&gt;AI can provide instant, consistent feedback, accelerating onboarding.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Tool‑driven ecosystems&lt;/strong&gt; – IDEs, CI/CD, and version‑control platforms already expose hooks for AI.&lt;/td&gt;
&lt;td&gt;Integration is technically feasible with low friction.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The confluence of these forces makes AI‑assisted review a “must‑try” rather than a “nice‑to‑have” experiment.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Opportunities – What AI brings to the table‑top
&lt;/h2&gt;

&lt;h3&gt;
  
  
  2.1 Speed &amp;amp; Throughput
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Instant linting &amp;amp; style enforcement&lt;/strong&gt; – LLM‑backed linters can suggest refactors the moment a file is saved, reducing the number of style‑only comments later.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pre‑review triage&lt;/strong&gt; – An AI filter can auto‑approve “low‑risk” changes (e.g., documentation updates, simple getters/setters) and flag only the “high‑risk” PRs for human eyes.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Batch‑review assistance&lt;/strong&gt; – On a PR with dozens of files, the model can surface the &lt;em&gt;most&lt;/em&gt; suspicious snippets, letting reviewers focus on the 5–10% that matters most.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2.2 Consistency &amp;amp; Knowledge Codification
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Uniform rule‑set&lt;/strong&gt; – Human reviewers differ in style, naming conventions, and security heuristics. An LLM trained on your internal style guide can enforce the same standards across the whole codebase.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Codified tribal knowledge&lt;/strong&gt; – Patterns that live only in senior engineers’ heads (e.g., “always use &lt;code&gt;await&lt;/code&gt; with &lt;code&gt;fetch&lt;/code&gt; in this repo”) become explicit in the model’s prompt library.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2.3 Security &amp;amp; Reliability
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Vulnerability surfacing&lt;/strong&gt; – Modern LLMs can combine static‑analysis signatures (SQLi, XSS, insecure deserialization) with reasoning about data flow, surfacing bugs that traditional linters miss.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compliance checks&lt;/strong&gt; – By embedding regulatory check‑lists (e.g., “no hard‑coded passwords”, “PII must be encrypted”), the AI can flag violations before they merge.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reliability patterns&lt;/strong&gt; – The model can suggest idempotent retry logic, circuit‑breaker usage, or proper use of &lt;code&gt;finally&lt;/code&gt; blocks, improving resilience.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2.4 Learning &amp;amp; On‑boarding
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Instant mentorship&lt;/strong&gt; – When a junior opens a PR, the AI can attach a short “explanation” comment: “You’re using &lt;code&gt;Array.map&lt;/code&gt; but ignoring the return value, which has no side‑effects. Consider using &lt;code&gt;forEach&lt;/code&gt; instead.”
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Knowledge transfer&lt;/strong&gt; – If a senior leaves, the prompts and fine‑tuned model retain part of their expertise, reducing the “bus factor”.
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2.5 Documentation &amp;amp; Traceability
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Auto‑generated change‑log snippets&lt;/strong&gt; – The model can summarize a PR’s intent in a single sentence, helping release notes.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;In‑code comments&lt;/strong&gt; – When a function is complex, the AI can propose a doc‑string that explains the algorithmic steps, improving future maintainability.
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2.6 Cost Efficiency
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reduced review cycles&lt;/strong&gt; – Faster merges mean less idle time for reviewers, translating to measurable engineering‑hour savings.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lower defect‑fix cost&lt;/strong&gt; – Early detection (especially security bugs) cuts the “cost‑of‑fix” curve dramatically (often &amp;gt;10× cheaper than post‑release patches).&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. Pitfalls – The hidden costs and failure modes
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Typical symptom&lt;/th&gt;
&lt;th&gt;Why it happens&lt;/th&gt;
&lt;th&gt;Real‑world impact&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;False positives / negatives&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AI flags harmless code or misses a critical bug.&lt;/td&gt;
&lt;td&gt;LLMs lack precise static‑analysis semantics; they hallucinate or over‑generalize.&lt;/td&gt;
&lt;td&gt;Review fatigue → “alert fatigue” → reviewers start ignoring AI suggestions.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Context blindness&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Model suggests a change that breaks a domain‑specific contract.&lt;/td&gt;
&lt;td&gt;Prompt only contains the diff, not the full call‑graph, runtime configuration, or business rules.&lt;/td&gt;
&lt;td&gt;Introduces regressions; reduces confidence in the tool.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Bias &amp;amp; style lock‑in&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AI enforces a style that diverges from the team’s evolving conventions.&lt;/td&gt;
&lt;td&gt;Model inherits biases from its training data or from a stale prompt set.&lt;/td&gt;
&lt;td&gt;Stifles innovation; may cause friction with existing code.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Security &amp;amp; privacy leakage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Sensitive code (e.g., API keys) is sent to a hosted LLM.&lt;/td&gt;
&lt;td&gt;SaaS providers may log prompts for model improvement.&lt;/td&gt;
&lt;td&gt;Violates compliance (GDPR, HIPAA) and can cause data breaches.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Over‑reliance &amp;amp; skill erosion&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Junior developers stop learning to read compiler errors.&lt;/td&gt;
&lt;td&gt;AI becomes a crutch.&lt;/td&gt;
&lt;td&gt;Long‑term reduction in code‑reading competence.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Integration friction&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AI tool slows CI pipeline or fails on merge conflicts.&lt;/td&gt;
&lt;td&gt;Poor caching, large model latency, or lack of incremental analysis.&lt;/td&gt;
&lt;td&gt;Pipeline bottlenecks; developers disable the tool.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Legal / licensing entanglements&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Model trained on proprietary code generates similar snippets.&lt;/td&gt;
&lt;td&gt;Potential IP contamination.&lt;/td&gt;
&lt;td&gt;Legal exposure for the organization.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  4. Mitigation Strategies – Turning pitfalls into manageable risks
&lt;/h2&gt;

&lt;h3&gt;
  
  
  4.1 Human‑in‑the‑Loop (HITL|) Architecture
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;AI‑first, reviewer‑second&lt;/strong&gt; – AI produces a &lt;em&gt;draft&lt;/em&gt; review comment set.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reviewer “accept/reject/modify”&lt;/strong&gt; – The UI must make it trivial to approve, edit, or discard each suggestion.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Feedback loop&lt;/strong&gt; – Capture reviewer actions (e.g., “dismissed as false‑positive”) and feed them back as reinforcement signals for the model.
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  4.2 Prompt Engineering &amp;amp; Guardrails
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Guardrail&lt;/th&gt;
&lt;th&gt;Implementation tip&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scope restriction&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Include the full file header, import list, and a short “module purpose” description in the prompt.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Risk weighting&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Prepend a “risk level” token (e.g., &lt;code&gt;HIGH_RISK&lt;/code&gt;) for files matching a pattern (&lt;code&gt;*.sql&lt;/code&gt;, &lt;code&gt;crypto/*&lt;/code&gt;). The model then applies stricter heuristics.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Compliance checklist&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Append a bullet list of mandatory checks (e.g., “No &lt;code&gt;eval&lt;/code&gt; usage”, “All secrets must be read from Vault”).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Output format&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Force JSON with fields &lt;code&gt;{line, suggestion, confidence, category}&lt;/code&gt; – easier to parse and filter in CI.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  4.3 Model Choice &amp;amp; Deployment
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Need&lt;/th&gt;
&lt;th&gt;Recommended approach&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Low latency, on‑prem&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Deploy a distilled LLaMA‑2 or Mistral model (7‑13 B) behind a GPU inference server.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Best‑in‑class security&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Use an open‑source model you can audit; avoid sending code to a public API unless you have a data‑processing agreement.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Domain‑specific reasoning&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fine‑tune on your own repository (e.g., 10 k PRs) using LoRA adapters – improves context awareness without full retraining.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Hybrid static‑analysis + LLM&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Run a traditional SAST engine first, then feed its diagnostics into the LLM for “explanation + remediation suggestion”.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  4.4 Metrics &amp;amp; Governance
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Why it matters&lt;/th&gt;
&lt;th&gt;Target (example)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Precision @ 5&lt;/strong&gt; (fraction of top‑5 AI suggestions that are accepted)&lt;/td&gt;
&lt;td&gt;Indicates usefulness without overwhelming reviewers.&lt;/td&gt;
&lt;td&gt;≥ 80 %&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Review‑time reduction&lt;/strong&gt; (average minutes saved per PR)&lt;/td&gt;
&lt;td&gt;Business impact.&lt;/td&gt;
&lt;td&gt;30 % reduction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;False‑positive rate&lt;/strong&gt; (suggestions dismissed)&lt;/td&gt;
&lt;td&gt;Alert fatigue.&lt;/td&gt;
&lt;td&gt;≤ 10 %&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Security‑bug detection lift&lt;/strong&gt; (new bugs found vs. baseline)&lt;/td&gt;
&lt;td&gt;Core ROI for security teams.&lt;/td&gt;
&lt;td&gt;≥ 2×&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data‑exfiltration incidents&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Compliance health.&lt;/td&gt;
&lt;td&gt;Zero&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Governance should include a &lt;strong&gt;review board&lt;/strong&gt; (security, legal, engineering) that signs off on model updates and on any change to the data‑handling policy.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.5 Developer Culture
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;“AI is a teammate, not a manager.”&lt;/strong&gt; – Emphasize that the model’s suggestions are &lt;em&gt;proposals&lt;/em&gt; subject to human judgment.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Encourage “explain‑why”&lt;/strong&gt; – When a reviewer accepts a suggestion, ask the AI to generate a short rationale that can be stored as part of the review comment.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Continuous learning&lt;/strong&gt; – Run a monthly “AI‑review post‑mortem” to surface patterns of missed bugs or over‑reactions, and update prompts accordingly.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  5. Case Studies – How teams are doing it today
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Organization&lt;/th&gt;
&lt;th&gt;Stack &amp;amp; Scale&lt;/th&gt;
&lt;th&gt;AI Tooling&lt;/th&gt;
&lt;th&gt;Outcome&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;FinTech startup (10 M LOC, 120 engineers)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Node.js, TypeScript, AWS Lambda&lt;/td&gt;
&lt;td&gt;Self‑hosted Mistral‑7B + custom “risk‑token” prompts; integrated into GitHub Actions.&lt;/td&gt;
&lt;td&gt;40 % reduction in PR turnaround; security findings rose from 2 → 9 per month (all fixed before prod).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Enterprise SaaS (50 M LOC, 600 engineers)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Java, Kotlin, Spring Boot&lt;/td&gt;
&lt;td&gt;GitHub Advanced Security + OpenAI GPT‑4 “explain‑vuln” plugin.&lt;/td&gt;
&lt;td&gt;False‑positive rate dropped to 6 % after a 2‑week fine‑tuning sprint; compliance audit passed with zero critical findings.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Open‑source library (C++)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;500 k LOC, volunteers globally&lt;/td&gt;
&lt;td&gt;Tabnine + CodeQL + ChatGPT‑4 “review‑assistant” in PR comments.&lt;/td&gt;
&lt;td&gt;Contributor onboarding time fell from 2 weeks to &amp;lt; 3 days; 30 % more PRs merged per week.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Healthcare platform (HIPAA‑bound)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Python, Django, PostgreSQL&lt;/td&gt;
&lt;td&gt;On‑prem LLaMA‑2 1‑B with strict data‑scrubbing; all API calls logged for audit.&lt;/td&gt;
&lt;td&gt;Zero data‑leak incidents; AI only suggested style changes (precision 94 %).&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Key take‑aways:&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fine‑tuning on internal data&lt;/strong&gt; dramatically improves relevance.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hybrid approaches&lt;/strong&gt; (static analysis + LLM) yield the best security signal.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strict data‑handling policies&lt;/strong&gt; are non‑negotiable in regulated domains.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  6. Future Directions – What’s on the horizon?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Trend&lt;/th&gt;
&lt;th&gt;Implication for code review&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Retrieval‑augmented generation (RAG)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;LLM can pull in &lt;em&gt;relevant&lt;/em&gt; files from the repository on‑the‑fly, providing true whole‑project context without loading everything into the prompt.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Program synthesis + verification&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Models will not only suggest &lt;em&gt;what&lt;/em&gt; to change but also auto‑generate &lt;em&gt;correct&lt;/em&gt; implementations with formal proofs (e.g., ensure &lt;code&gt;null&lt;/code&gt; safety).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Continuous‑learning pipelines&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Feedback from reviewer actions can be streamed back to the model daily, enabling “online” adaptation without full retraining.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Explainable AI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;New UI that surfaces the &lt;em&gt;reasoning chain&lt;/em&gt; (e.g., “found &lt;code&gt;strcpy&lt;/code&gt; → looked up CWE‑120 → suggests &lt;code&gt;strncpy&lt;/code&gt;”) improves trust.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Policy‑as‑code integration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;LLM suggestions can be automatically gated by OPA (Open Policy Agent) policies, ensuring compliance before merge.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Investing now in a modular, extensible architecture (e.g., “LLM‑review microservice” + “policy engine”) will make it easier to adopt these upcoming capabilities.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Practical Checklist – Deploying AI‑Assisted Review in Your Org
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;✅&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;Owner&lt;/th&gt;
&lt;th&gt;Deadline&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;1&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Define scope&lt;/strong&gt; – Which repos, file types, and risk levels will be auto‑reviewed?&lt;/td&gt;
&lt;td&gt;Architecture Lead&lt;/td&gt;
&lt;td&gt;Week 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;2&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Select model&lt;/strong&gt; – On‑prem (Mistral, LLaMA) vs. SaaS (OpenAI, Anthropic). Include a data‑privacy impact assessment.&lt;/td&gt;
&lt;td&gt;Security &amp;amp; Legal&lt;/td&gt;
&lt;td&gt;Week 2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Create prompt library&lt;/strong&gt; – Include style guide, compliance checklist, and “risk token” taxonomy.&lt;/td&gt;
&lt;td&gt;Senior Engineer&lt;/td&gt;
&lt;td&gt;Week 3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;4&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Implement CI integration&lt;/strong&gt; – Add a step that runs the model on the PR diff, returns JSON suggestions, and posts them as review comments.&lt;/td&gt;
&lt;td&gt;DevOps&lt;/td&gt;
&lt;td&gt;Week 4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;5&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Build reviewer UI&lt;/strong&gt; – Simple “Accept / Dismiss / Edit” buttons in GitHub/GitLab UI (via bots).&lt;/td&gt;
&lt;td&gt;Front‑end Engineer&lt;/td&gt;
&lt;td&gt;Week 5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;6&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Pilot&lt;/strong&gt; – Run on a low‑risk repo (e.g., docs or internal tooling) for 2 weeks. Capture metrics.&lt;/td&gt;
&lt;td&gt;Team Lead&lt;/td&gt;
&lt;td&gt;Week 7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;7&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Iterate&lt;/strong&gt; – Refine prompts based on false‑positive/negative logs; optionally fine‑tune on pilot PRs.&lt;/td&gt;
&lt;td&gt;Data‑Science Engineer&lt;/td&gt;
&lt;td&gt;Week 9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;8&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Roll‑out&lt;/strong&gt; – Expand to high‑risk services with “high‑risk” token enabled.&lt;/td&gt;
&lt;td&gt;Engineering Manager&lt;/td&gt;
&lt;td&gt;Week 12&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;9&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Governance&lt;/strong&gt; – Quarterly audit of data handling, model drift, and compliance reports.&lt;/td&gt;
&lt;td&gt;Governance Board&lt;/td&gt;
&lt;td&gt;Ongoing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Culture&lt;/strong&gt; – Host a brown‑bag session on “AI is a teammate” and publish a “best‑practice guide” for reviewers.&lt;/td&gt;
&lt;td&gt;People Ops&lt;/td&gt;
&lt;td&gt;Month 4&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  8. Conclusion
&lt;/h2&gt;

&lt;p&gt;AI‑assisted code review is &lt;strong&gt;no longer a futuristic experiment&lt;/strong&gt;—it is a practical lever for scaling quality, security, and knowledge sharing in today’s fast‑moving software organizations.  &lt;/p&gt;

&lt;p&gt;&lt;em&gt;The upside&lt;/em&gt; (speed, consistency, early security detection, onboarding acceleration) can translate into measurable cost savings and risk reduction.  &lt;/p&gt;

&lt;p&gt;&lt;em&gt;The downside&lt;/em&gt; (false alerts, context loss, privacy concerns, skill erosion) must be deliberately mitigated through a &lt;strong&gt;human‑in‑the‑loop design, robust prompt engineering, careful model selection, and strong governance&lt;/strong&gt;.  &lt;/p&gt;

&lt;p&gt;When you treat the LLM as a &lt;em&gt;collaborative teammate&lt;/em&gt;—one that proposes, explains, and learns from you—rather than a &lt;em&gt;replacement&lt;/em&gt; for human judgment, you capture the best of both worlds.  &lt;/p&gt;

&lt;p&gt;Start small, measure relentlessly, and evolve the system as your codebase, regulations, and AI technology mature. The result will be a review process that is faster, safer, and more inclusive—exactly what modern engineering teams need to stay competitive. Happy reviewing!  &lt;/p&gt;




</description>
    </item>
    <item>
      <title>Spring Boot Scheduling Best Practices</title>
      <dc:creator>Gaurav</dc:creator>
      <pubDate>Sat, 30 Aug 2025 19:26:33 +0000</pubDate>
      <link>https://forem.com/dixitgurv/spring-boot-scheduling-best-practices-503h</link>
      <guid>https://forem.com/dixitgurv/spring-boot-scheduling-best-practices-503h</guid>
      <description>&lt;h1&gt;
  
  
  Spring Boot Scheduling – Best‑Practice Guide
&lt;/h1&gt;

&lt;p&gt;Whether you need to run a nightly batch, poll an external API every few seconds, or trigger a complex workflow on a cron schedule, Spring Boot gives you a rich set of tools.  This guide gathers the most‑effective patterns, pitfalls to avoid, and production‑ready recommendations that have emerged from the community and from real‑world deployments.&lt;/p&gt;




&lt;h2&gt;
  
  
  1️⃣ Choose the Right Scheduler for the Job
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Use‑case&lt;/th&gt;
&lt;th&gt;Recommended tool&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Simple, local, non‑critical jobs&lt;/strong&gt; (e.g. cache warm‑up, health‑check pings, short‑lived tasks)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;@Scheduled&lt;/code&gt; + &lt;code&gt;TaskScheduler&lt;/code&gt; (default &lt;code&gt;ThreadPoolTaskScheduler&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Zero‑config, lightweight, runs in the same JVM.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Jobs with retry, mis‑fire handling, state persistence&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Quartz&lt;/strong&gt; (Spring Boot starter &lt;code&gt;spring-boot-starter-quartz&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Persistent job store, sophisticated calendar &amp;amp; mis‑fire policies, clustering support out‑of‑the‑box.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Distributed / multi‑instance environments&lt;/strong&gt; (e.g. microservices, Kubernetes)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Spring Cloud Scheduler&lt;/strong&gt; (Cloud‑foundry), &lt;strong&gt;Quartz with JDBC/Redis lock&lt;/strong&gt;, or &lt;strong&gt;external orchestrators&lt;/strong&gt; (Kubernetes CronJobs, AWS EventBridge)&lt;/td&gt;
&lt;td&gt;Guarantees “run‑once” semantics across nodes.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Very long‑running or resource‑intensive jobs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Separate worker process&lt;/strong&gt; (Spring Batch, Spring Cloud Task) + message queue (Kafka, RabbitMQ)&lt;/td&gt;
&lt;td&gt;Decouples scheduling from execution, enables scaling &amp;amp; back‑pressure.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dynamic job creation at runtime&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Quartz&lt;/strong&gt; or &lt;strong&gt;Spring Batch JobLauncher&lt;/strong&gt; + a DB‑backed job definition table&lt;/td&gt;
&lt;td&gt;Jobs can be added/removed without redeploy.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Rule‑of‑thumb&lt;/strong&gt;: Start with &lt;code&gt;@Scheduled&lt;/code&gt;. Only move to Quartz or an external scheduler when you need persistence, clustering, or advanced mis‑fire handling.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  2️⃣ Core &lt;code&gt;@Scheduled&lt;/code&gt; Best Practices
&lt;/h2&gt;

&lt;h3&gt;
  
  
  2.1 Enable Scheduling Once, Early
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@SpringBootApplication&lt;/span&gt;
&lt;span class="nd"&gt;@EnableScheduling&lt;/span&gt;   &lt;span class="c1"&gt;// enable once, preferably on the main class&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyApp&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2.2 Externalise Cron Expressions &amp;amp; Delays
&lt;/h3&gt;

&lt;p&gt;Never hard‑code them. Use &lt;code&gt;application.yml&lt;/code&gt; (or &lt;code&gt;application.properties&lt;/code&gt;) and bind them to a &lt;code&gt;@ConfigurationProperties&lt;/code&gt; bean.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# application.yml&lt;/span&gt;
&lt;span class="na"&gt;myapp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;scheduler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;cleanup-cron&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;30&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;2&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;?"&lt;/span&gt;   &lt;span class="c1"&gt;# 02:30 AM every day&lt;/span&gt;
    &lt;span class="na"&gt;poll-rate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;15s&lt;/span&gt;                &lt;span class="c1"&gt;# ISO‑8601 duration&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Component&lt;/span&gt;
&lt;span class="nd"&gt;@ConfigurationProperties&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prefix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"myapp.scheduler"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SchedulerProperties&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;cleanupCron&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Duration&lt;/span&gt; &lt;span class="n"&gt;pollRate&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// getters/setters&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Component&lt;/span&gt;
&lt;span class="nd"&gt;@RequiredArgsConstructor&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CleanupTask&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;SchedulerProperties&lt;/span&gt; &lt;span class="n"&gt;props&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Scheduled&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cron&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"#{@schedulerProperties.cleanupCron}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;cleanOldRecords&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// …&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Scheduled&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fixedDelayString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"#{@schedulerProperties.pollRate.toMillis()}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;pollExternalApi&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// …&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2.3 Use a Dedicated Thread‑Pool
&lt;/h3&gt;

&lt;p&gt;The default &lt;code&gt;ThreadPoolTaskScheduler&lt;/code&gt; uses a pool size of &lt;strong&gt;1&lt;/strong&gt;. For any realistic workload you need more threads and you must configure the pool &lt;strong&gt;once&lt;/strong&gt;, preferably as a bean.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Configuration&lt;/span&gt;
&lt;span class="nd"&gt;@EnableScheduling&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SchedulingConfig&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Bean&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;TaskScheduler&lt;/span&gt; &lt;span class="nf"&gt;taskScheduler&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@Value&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"${myapp.scheduler.pool-size:5}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;poolSize&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;ThreadPoolTaskScheduler&lt;/span&gt; &lt;span class="n"&gt;scheduler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ThreadPoolTaskScheduler&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;scheduler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setPoolSize&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;poolSize&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;scheduler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setThreadNamePrefix&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"myapp-scheduler-"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;scheduler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setAwaitTerminationSeconds&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;scheduler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setWaitForTasksToCompleteOnShutdown&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;scheduler&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why:&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prevents one slow job from starving the others.
&lt;/li&gt;
&lt;li&gt;Guarantees graceful shutdown (&lt;code&gt;waitForTasksToCompleteOnShutdown&lt;/code&gt;).
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2.4 Avoid Long‑Running Work Inside a &lt;code&gt;@Scheduled&lt;/code&gt; Method
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Rule:&lt;/strong&gt; &lt;em&gt;Never block  &amp;gt; 30 s&lt;/em&gt; (or whatever your SLA is).  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pattern:&lt;/strong&gt; Off‑load the heavy work to an asynchronous executor or a message queue.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Component&lt;/span&gt;
&lt;span class="nd"&gt;@RequiredArgsConstructor&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ReportScheduler&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;ApplicationEventPublisher&lt;/span&gt; &lt;span class="n"&gt;publisher&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Scheduled&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cron&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0 0 3 * * ?"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;// 03:00 AM daily&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;scheduleReportGeneration&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;publisher&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;publishEvent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GenerateReportEvent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;LocalDate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;now&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Component&lt;/span&gt;
&lt;span class="nd"&gt;@RequiredArgsConstructor&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ReportGenerator&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Async&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"reportExecutor"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;// custom executor bean&lt;/span&gt;
    &lt;span class="nd"&gt;@EventListener&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onGenerateReport&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;GenerateReportEvent&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// heavy processing, DB writes, file I/O …&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2.5 Exception Handling – Never Let Exceptions Escape
&lt;/h3&gt;

&lt;p&gt;If a &lt;code&gt;@Scheduled&lt;/code&gt; method throws an uncaught exception, the scheduler &lt;strong&gt;silently cancels&lt;/strong&gt; the task.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Approach:&lt;/strong&gt; Wrap the body in a try/catch and log + optionally send to a monitoring system.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Scheduled&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fixedRateString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"PT10S"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;poll&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// … business logic&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Polling failed"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="c1"&gt;// optional: alerting, metrics increment&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alternatively, define a &lt;strong&gt;global &lt;code&gt;TaskScheduler&lt;/code&gt; error handler&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Bean&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ThreadPoolTaskScheduler&lt;/span&gt; &lt;span class="nf"&gt;taskScheduler&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;ThreadPoolTaskScheduler&lt;/span&gt; &lt;span class="n"&gt;scheduler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ThreadPoolTaskScheduler&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;scheduler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setErrorHandler&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Scheduled task error"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="c1"&gt;// …&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;scheduler&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2.6 Time‑Zone Awareness
&lt;/h3&gt;

&lt;p&gt;Cron expressions are evaluated in the &lt;strong&gt;system default time‑zone&lt;/strong&gt; unless you specify &lt;code&gt;zone&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Scheduled&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cron&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0 0 2 * * ?"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;zone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"America/New_York"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;nightlyJob&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="err"&gt;…&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Never&lt;/em&gt; rely on the OS time‑zone in a containerized environment—explicitly set &lt;code&gt;zone&lt;/code&gt; or use &lt;code&gt;ZonedDateTime.now(ZoneId.of(...))&lt;/code&gt; inside the job.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.7 Fixed‑Rate vs Fixed‑Delay vs Cron
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;When to use&lt;/th&gt;
&lt;th&gt;Behaviour&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fixedRate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Periodic heartbeat, polling where you &lt;em&gt;care&lt;/em&gt; about the &lt;em&gt;rate&lt;/em&gt; (e.g., “run every 5 s regardless of execution time”)&lt;/td&gt;
&lt;td&gt;Starts the next execution &lt;strong&gt;N ms after the previous start&lt;/strong&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fixedDelay&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;“Run &lt;strong&gt;N&lt;/strong&gt; ms &lt;strong&gt;after&lt;/strong&gt; the previous execution &lt;strong&gt;finishes&lt;/strong&gt;” – ideal for back‑pressure‑friendly jobs.&lt;/td&gt;
&lt;td&gt;Starts the next execution &lt;strong&gt;N ms after the previous *completion&lt;/strong&gt;*.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cron&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Human‑readable schedules (daily, weekly, business‑hour windows).&lt;/td&gt;
&lt;td&gt;Uses a cron expression; supports &lt;code&gt;zone&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; For most batch‑style work, &lt;code&gt;fixedDelay&lt;/code&gt; is safer because it naturally throttles if a run takes longer than expected.&lt;/p&gt;




&lt;h2&gt;
  
  
  3️⃣ Advanced Scheduler – Quartz Integration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  3.1 When to Introduce Quartz
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Jobs must survive application restarts.
&lt;/li&gt;
&lt;li&gt;You need &lt;strong&gt;mis‑fire handling&lt;/strong&gt; (e.g., “run immediately if missed”).
&lt;/li&gt;
&lt;li&gt;You require &lt;strong&gt;clustering&lt;/strong&gt; (multiple nodes, “run‑once” guarantee).
&lt;/li&gt;
&lt;li&gt;You need &lt;strong&gt;job parameters&lt;/strong&gt; that can be changed at runtime.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3.2 Minimal Quartz Configuration
&lt;/h3&gt;

&lt;p&gt;Add the starter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-quartz&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;application.yml&lt;/strong&gt; (JDBC store – works with any Spring‑Boot datasource):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;spring&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;quartz&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;job-store-type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jdbc&lt;/span&gt;
    &lt;span class="na"&gt;jdbc&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;initialize-schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;   &lt;span class="c1"&gt;# creates tables on startup (dev)&lt;/span&gt;
    &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;org.quartz.threadPool.threadCount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
      &lt;span class="na"&gt;org.quartz.scheduler.instanceId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AUTO&lt;/span&gt;
      &lt;span class="na"&gt;org.quartz.scheduler.instanceName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;MyAppScheduler&lt;/span&gt;
      &lt;span class="na"&gt;org.quartz.jobStore.isClustered&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.3 Defining a Quartz Job
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@PersistJobDataAfterExecution&lt;/span&gt;
&lt;span class="nd"&gt;@DisallowConcurrentExecution&lt;/span&gt;   &lt;span class="c1"&gt;// prevents overlapping runs for the same job key&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EmailDigestJob&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;Job&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Autowired&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;EmailService&lt;/span&gt; &lt;span class="n"&gt;emailService&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// injected lazily via SpringBeanJobFactory&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;JobExecutionContext&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;JobDataMap&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMergedJobDataMap&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;tenantId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"tenantId"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;emailService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sendDigest&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tenantId&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.4 Scheduling a Job Programmatically
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Service&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;or&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="nd"&gt;@Component&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;
&lt;span class="nd"&gt;@RequiredArgsConstructor&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;QuartzScheduler&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Scheduler&lt;/span&gt; &lt;span class="n"&gt;scheduler&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;scheduleDailyDigest&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;tenantId&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;SchedulerException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;JobDetail&lt;/span&gt; &lt;span class="n"&gt;job&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;JobBuilder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newJob&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;EmailDigestJob&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withIdentity&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"digest-"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;tenantId&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"digest"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;usingJobData&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"tenantId"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tenantId&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="nc"&gt;Trigger&lt;/span&gt; &lt;span class="n"&gt;trigger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TriggerBuilder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newTrigger&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;forJob&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withIdentity&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"trigger-"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;tenantId&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"digest"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withSchedule&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;CronScheduleBuilder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;cronSchedule&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0 0 8 ? * MON-FRI"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;inTimeZone&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TimeZone&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getTimeZone&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"UTC"&lt;/span&gt;&lt;span class="o"&gt;)))&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;scheduler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;scheduleJob&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;trigger&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.5 Mis‑fire Policies (the most common pitfalls)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Policy&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;th&gt;When to use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;withMisfireHandlingInstructionDoNothing()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Skip the missed execution, continue with the next schedule.&lt;/td&gt;
&lt;td&gt;For idempotent jobs where “catch‑up” would cause duplication.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;withMisfireHandlingInstructionFireNow()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Execute immediately, then resume normal schedule.&lt;/td&gt;
&lt;td&gt;For critical job that must not be missed (e.g., security sweep).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;withMisfireHandlingInstructionIgnoreMisfires()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run as if nothing happened (may cause bursts).&lt;/td&gt;
&lt;td&gt;Rarely recommended; only when you &lt;em&gt;trust&lt;/em&gt; the job to be cheap.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withSchedule&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;CronScheduleBuilder&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;cronSchedule&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"0 0/5 * * * ?"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withMisfireHandlingInstructionDoNothing&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.6 Preventing Overlap Across Nodes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;@DisallowConcurrentExecution&lt;/code&gt; on the job class.
&lt;/li&gt;
&lt;li&gt;For &lt;strong&gt;stateful&lt;/strong&gt; jobs (e.g., update same DB rows), also add a &lt;strong&gt;pessimistic lock&lt;/strong&gt; (SELECT … FOR UPDATE) or use the Quartz built‑in check (the annotation already guarantees that a job with the same &lt;code&gt;JobKey&lt;/code&gt; won’t be executed concurrently even in a cluster).&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  4️⃣ Distributed Scheduling – “Run‑Once” Guarantees
&lt;/h2&gt;

&lt;h3&gt;
  
  
  4.1 Simple DB‑Lock (Spring Integration)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Component&lt;/span&gt;
&lt;span class="nd"&gt;@RequiredArgsConstructor&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DistributedTask&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;JdbcTemplate&lt;/span&gt; &lt;span class="n"&gt;jdbc&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;LockProvider&lt;/span&gt; &lt;span class="n"&gt;lockProvider&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// from spring‑integration‑jdbc&lt;/span&gt;

    &lt;span class="nd"&gt;@Scheduled&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cronMisfire&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;???)&lt;/span&gt; &lt;span class="c1"&gt;// not needed; lock decides execution&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;runIfLeader&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Lock&lt;/span&gt; &lt;span class="n"&gt;lock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lockProvider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;obtainLock&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;LockConfiguration&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                &lt;span class="nc"&gt;Instant&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;now&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;plusSeconds&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// lock timeout&lt;/span&gt;
                &lt;span class="s"&gt;"myapp-distributed-task"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lock&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// critical work&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;unlock&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Works with any relational DB, no extra infrastructure.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4.2 Redis‑Based Lock (Spring‑Boot‑Starter‑Data‑Redis)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Bean&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;LockProvider&lt;/span&gt; &lt;span class="nf"&gt;redisLockProvider&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RedisConnectionFactory&lt;/span&gt; &lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RedisLockProvider&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;factory&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same usage as DB lock; Redis gives sub‑millisecond latency and works well in Kubernetes.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.3 Using Quartz’s Built‑In Clustering
&lt;/h3&gt;

&lt;p&gt;If you already have Quartz, just set:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;spring.quartz.properties.org.quartz.jobStore.isClustered&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Quartz will automatically elect a single node to fire each trigger.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.4 External Orchestrators (Kubernetes CronJob, Cloud Scheduler)
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Pros:&lt;/em&gt; No code, native HA, built‑in history, easy to scale‑out.&lt;br&gt;&lt;br&gt;
&lt;em&gt;Cons:&lt;/em&gt; You lose the ability to pass in application‑specific parameters directly.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pattern:&lt;/strong&gt; Keep the job logic in a &lt;strong&gt;Spring Boot “worker”&lt;/strong&gt; (plain &lt;code&gt;CommandLineRunner&lt;/code&gt; or Spring Cloud Task) and let the orchestrator start a new pod/container for each run.&lt;/p&gt;


&lt;h2&gt;
  
  
  5️⃣ Monitoring, Metrics &amp;amp; Observability
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concern&lt;/th&gt;
&lt;th&gt;Tool / Technique&lt;/th&gt;
&lt;th&gt;Sample Code&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Execution duration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Micrometer Timer (&lt;code&gt;@Timed&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;@Timed(value = "myapp.scheduled.cleanup", description = "Cleanup job duration")&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Success / failure counts&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Micrometer Counter (&lt;code&gt;@Counted&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;@Counted("myapp.scheduled.cleanup.errors")&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Thread‑pool health&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ThreadPoolTaskScheduler&lt;/code&gt; exposes &lt;code&gt;activeCount&lt;/code&gt;, &lt;code&gt;queueSize&lt;/code&gt; via &lt;code&gt;Gauge&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Gauge.builder("myapp.scheduler.pool.active", scheduler, s -&amp;gt; s.getThreadPoolExecutor().getActiveCount()).register(meterRegistry);&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Job history&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Store start/end timestamps in DB; or use Quartz’s &lt;code&gt;QRTZ_FIRED_TRIGGERS&lt;/code&gt; tables.&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Alert on‑failure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Spring Boot Actuator + Alertmanager (Prometheus)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;management.metrics.export.prometheus.enabled=true&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Graceful shutdown&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;waitForTasksToCompleteOnShutdown=true&lt;/code&gt; + &lt;code&gt;awaitTerminationSeconds&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Already shown in &lt;code&gt;TaskScheduler&lt;/code&gt; bean.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Logging context&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Add &lt;code&gt;MDC&lt;/code&gt; with job name &amp;amp; run ID&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;MDC.put("job", "cleanup");&lt;/code&gt; – clear after job finishes.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Example – Micrometer Timer with custom tags:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Component&lt;/span&gt;
&lt;span class="nd"&gt;@RequiredArgsConstructor&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CleanupTask&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;MeterRegistry&lt;/span&gt; &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Scheduled&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cron&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"${myapp.scheduler.cleanup-cron}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Timer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Sample&lt;/span&gt; &lt;span class="n"&gt;sample&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Timer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// … actual work&lt;/span&gt;
            &lt;span class="n"&gt;sample&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stop&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;timer&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"myapp.scheduled.cleanup"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"status"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"success"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;sample&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stop&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;timer&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"myapp.scheduled.cleanup"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"status"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"error"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// or swallow as per 2.5&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  6️⃣ Testing Scheduled Jobs
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Goal&lt;/th&gt;
&lt;th&gt;Technique&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Unit test logic&lt;/strong&gt; (without waiting)&lt;/td&gt;
&lt;td&gt;Call the method directly, or use &lt;code&gt;@SpringBootTest&lt;/code&gt; with &lt;code&gt;@Import(SchedulingConfig.class)&lt;/code&gt; and inject the bean.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Verify scheduling metadata&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;ApplicationContext&lt;/code&gt; to fetch &lt;code&gt;ScheduledAnnotationBeanPostProcessor&lt;/code&gt; and inspect &lt;code&gt;ScheduledTask&lt;/code&gt; objects.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Integration test full schedule&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Use &lt;strong&gt;&lt;code&gt;@TestConfiguration&lt;/code&gt;&lt;/strong&gt; to replace the real &lt;code&gt;TaskScheduler&lt;/code&gt; with a &lt;strong&gt;&lt;code&gt;ThreadPoolTaskScheduler&lt;/code&gt;&lt;/strong&gt; that has a &lt;strong&gt;short &lt;code&gt;awaitTerminationSeconds&lt;/code&gt;&lt;/strong&gt; and &lt;code&gt;setWaitForTasksToCompleteOnShutdown(false)&lt;/code&gt;. Then use &lt;code&gt;await().until(...)&lt;/code&gt; (Awaitility) to assert side‑effects.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Mock time‑zones / clock&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Inject &lt;code&gt;Clock&lt;/code&gt; bean (&lt;code&gt;java.time.Clock&lt;/code&gt;) wherever you compute &lt;code&gt;ZonedDateTime.now(clock)&lt;/code&gt;. In tests, provide &lt;code&gt;Clock.fixed(...)&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Avoid real Quartz DB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Use an &lt;strong&gt;in‑memory H2&lt;/strong&gt; database with &lt;code&gt;spring.quartz.jdbc.initialize-schema=always&lt;/code&gt; for fast integration tests.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Sample unit test:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@SpringBootTest&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CleanupTaskTest&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Autowired&lt;/span&gt; &lt;span class="nc"&gt;CleanupTask&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nd"&gt;@MockBean&lt;/span&gt; &lt;span class="nc"&gt;EmailService&lt;/span&gt; &lt;span class="n"&gt;emailService&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// dependency&lt;/span&gt;

    &lt;span class="nd"&gt;@Test&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;runDeletesOldRecords&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// given: pre‑populate repository with test data …&lt;/span&gt;

        &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// call directly, no scheduling delay&lt;/span&gt;

        &lt;span class="c1"&gt;// then: verify repository state&lt;/span&gt;
        &lt;span class="n"&gt;verify&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;emailService&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;sendDigest&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;anyString&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  7️⃣ Common Pitfalls &amp;amp; How to Avoid Them
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pitfall&lt;/th&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Single‑threaded scheduler&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Subsequent jobs never start, or “stuck” tasks block the whole system.&lt;/td&gt;
&lt;td&gt;Configure a pool size &amp;gt; 1 (&lt;code&gt;ThreadPoolTaskScheduler#setPoolSize&lt;/code&gt;).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Uncaught exception kills the task&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cron job runs once, then disappears from logs.&lt;/td&gt;
&lt;td&gt;Wrap body in try/catch or set a global &lt;code&gt;ErrorHandler&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Time‑zone drift in containers&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Nightly job runs at wrong local hour after DST change.&lt;/td&gt;
&lt;td&gt;Always set &lt;code&gt;zone&lt;/code&gt; attribute on &lt;code&gt;@Scheduled&lt;/code&gt; or compute times with &lt;code&gt;ZonedDateTime&lt;/code&gt; using a fixed &lt;code&gt;ZoneId&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Job overlap across instances&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Duplicate emails, double‑charged payments.&lt;/td&gt;
&lt;td&gt;Use Quartz clustering, DB/Redis lock, or &lt;code&gt;@DisallowConcurrentExecution&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Long‑running job blocks scheduler shutdown&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Application hangs for minutes on SIGTERM.&lt;/td&gt;
&lt;td&gt;Set &lt;code&gt;waitForTasksToCompleteOnShutdown&lt;/code&gt; &lt;strong&gt;and&lt;/strong&gt; &lt;code&gt;awaitTerminationSeconds&lt;/code&gt; to a reasonable value; off‑load heavy work to a separate worker pool.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Hard‑coded cron strings&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Need to change schedule -&amp;gt; redeploy.&lt;/td&gt;
&lt;td&gt;Externalise to &lt;code&gt;application.yml&lt;/code&gt; (see 2.2).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Memory leak via &lt;code&gt;ThreadLocal&lt;/code&gt; or &lt;code&gt;MDC&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Thread pool reuses threads, stale context leaks.&lt;/td&gt;
&lt;td&gt;Clear MDC (&lt;code&gt;MDC.clear()&lt;/code&gt;) at end of each job; avoid storing state in static &lt;code&gt;ThreadLocal&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Missing metrics&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No visibility on job health.&lt;/td&gt;
&lt;td&gt;Add Micrometer timers/counters; expose via Actuator.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




</description>
    </item>
  </channel>
</rss>
