{"id":5647,"date":"2025-10-06T12:46:36","date_gmt":"2025-10-06T12:46:36","guid":{"rendered":"https:\/\/codethataint.com\/blog\/?p=5647"},"modified":"2025-12-14T12:31:25","modified_gmt":"2025-12-14T12:31:25","slug":"shedlock-why-when-and-how","status":"publish","type":"post","link":"https:\/\/codethataint.com\/blog\/shedlock-why-when-and-how\/","title":{"rendered":"Shedlock &#8211; Why, When and How?"},"content":{"rendered":"<p><strong class=\"ctaHeader2\">Why Shedlock?<\/strong><br \/>\nShedlock prevents concurrent execution of scheduled tasks in distributed systems. In a server where multiple instance of same JAR running, shedlock prevents simultaneous execution of task by different instance at the same time. By this shedlock prevents <strong>race conditions<\/strong> and prevents multiple nodes from executing the same task simultaneously, which can lead to data corruption or duplication<\/p>\n<p>Before Shedlock<\/p>\n<blockquote><p>\nApplication Schedule Job<br \/>\n  \u2514\u2500\u25b6 PCF Inst A: tries to gets lock \u2192 runs job<br \/>\n  \u2514\u2500\u25b6 PCF Inst B: tries to gets lock \u2192 runs job<br \/>\n  \u2514\u2500\u25b6 PCF Inst C: tries to gets lock \u2192 runs job<\/p>\n<\/blockquote>\n<p>Post Shedlock<\/p>\n<blockquote><p>\nApplication Schedule Job<br \/>\n  \u2514\u2500\u25b6 PCF Inst A: tries to gets lock \u2192 runs job<br \/>\n  \u2514\u2500\u25b6 PCF Inst B: Waits for lock release \u2192 runs job<br \/>\n  \u2514\u2500\u25b6 PCF Inst C: Waits for lock release \u2192 runs job<\/p>\n<\/blockquote>\n<p><strong class=\"ctaHeader2\">When to use Shedlock?<\/strong><br \/>\nIf you have same scheduled job running in more than one instance.<\/p>\n<p><strong class=\"ctaHeader2\">How Shedlock Works?<\/strong><\/p>\n<ol>\n<li>When a scheduled task is triggered, ShedLock checks a shared lock (e.g., in a database table).<\/li>\n<li>If the lock is free, it acquires it and runs the task.<\/li>\n<li>If another instance already holds the lock, the task is skipped.<\/li>\n<li>The lock has an expiration time to handle crashes or failures gracefully.<\/li>\n<\/ol>\n<p><img decoding=\"async\" src=\"https:\/\/codethataint.com\/blog\/wp-content\/uploads\/2025\/10\/Shedlock1.png\" alt=\"\" \/><\/p>\n<p><strong class=\"ctaHeader2\">Use cases<\/strong><br \/>\nSending emails or notifications, Generating reports, Cleaning up expired sessions or data, Syncing data with external systems<\/p>\n<p><strong>FAQ<\/strong><br \/>\n<strong>atLeast and atMost Lock time in shedlock?<\/strong><br \/>\nIn ShedLock, atLeast and atMost are parameters used to control the duration of the lock for scheduled tasks. They help ensure that tasks are executed safely and efficiently in distributed environments.<\/p>\n<p>Example:<\/p>\n<p><em>atLeast = 5m<\/em> means the lock will stay held for at least 5 minutes, even if the task finishes in 1 minute. this prevents other instances from immediately picking up the task again, useful for throttling or spacing out executions.<\/p>\n<p><em>atMost = 10m<\/em> means the lock will automatically release after 10 minutes, even if the task hasn&#8217;t finished. atMost is needed incase the task fails and to prevent resource from holding for long time.<\/p>\n<p>The <strong>@SchedulerLock<\/strong> annotation in ShedLock is used to control distributed locking for scheduled tasks<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\n@Scheduled(cron = &quot;0 0 * * * *&quot;) \/\/ every hour\r\n@SchedulerLock(name = &quot;hourlyTask&quot;, atLeast = &quot;30m&quot;, atMost = &quot;1h&quot;)\r\npublic void hourlyTask() {\r\n    \/\/ task logic here\r\n}\r\n<\/pre>\n<ol>\n<li>The task runs only once per hour across all instances.<\/li>\n<li>The lock is held for at least 30 minutes, even if the task finishes early.<\/li>\n<li>The lock is released after 1 hour, even if the task hangs.<\/li>\n<\/ol>\n<p><strong>Can another instance Instance2 can execute the job, if Instance1 job is completed within 30 Minutes(atleast Time)?<\/strong><br \/>\nNo. The atLeast duration is a guaranteed lock time, not tied to actual job execution. Instance2 cannot start the job during this 30-minute window because the lock is still active. Even when the instance1 job is completed within 30 minutes instance2 job cannot be started unless the lock is released. <\/p>\n<p><strong>What happens when atLeastTime is more than Schedule Interval of Job<\/strong><br \/>\nLets say we have a job which runs every 4 minutes. We have a Shedlock which has minimum lock time of 5 Minutes and  max of 10 Minutes. Now in this scenario job would run in <em>8th, 16th, 28th, 36th<\/em> Minute the job would run<\/p>\n<pre>\r\n               |-------------|-------------|-------------|-------------|-------------|-------------|-------------|\r\nTimeLine       0-------------5-------------10------------15------------20------------25------------30------------35\r\nLock Time      <------L----->|<-----NL---->|<-----L----->|<-----NL---->|<-----L----->|<-----NL---->|<-----L----->|\r\nJob Exe Inter  0----------4----------8----------12----------16---------20---------24---------28---------32--------\r\n\r\nL -> Resource Locked\r\nNL -> Resource Unavailable\r\n<\/pre>\n<p>In the above Job Execution would work when the Lock Time is NL<\/p>\n<p><strong>What if there is a Negative Scenario as below<\/strong> <\/p>\n<ol>\n<li>Where the resource is locked and shedlock releases the resource<\/li>\n<li>The Time of Release of Resource and Job Execution Interval are same<\/li>\n<\/ol>\n<p>Lets have a scenario where the atmost Time is 5 Minutes and Job Execution Interval is also 5 Minutes. In this case the Job may or may not run as expected. <\/p>\n<p>The Job runs at 00:05:00 When Lock is released on 00:05:00<br \/>\nThe Job wont run at 00:05:00 When Lock is released on 00:05:01<\/p>\n<p>Its a good practice to have atmost time less than schedule interval time. I.E. 00:04:50 in the above case<\/p>\n<p><strong>Should I use Shedlock while updating db tables?<\/strong><br \/>\nIf updating database tables as part of a scheduled task in a distributed system using shedlock would be good option. this prevents Duplicate Execution and Data Integrity<\/p>\n<p><strong>Why we are defining lock timing at 2 places? <\/strong><\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\n@EnableSchedulerLock(defaultLockAtMostFor = &quot;PT4M&quot;) \r\n <\/pre>\n<p>vs<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\n  @SchedulerLock(name = &quot;fetchPosts&quot;, lockAtMostFor = &quot;PT2M&quot;, lockAtLeastFor = &quot;PT1M&quot;)\r\n <\/pre>\n<p><em>@EnableSchedulerLock<\/em> sets defaults for all tasks. A fallback setting for all tasks.<\/p>\n<p> <em>@SchedulerLock <\/em>gives per-task overrides with more precise control. Fine-grained control, overriding the default for specific jobs.<\/p>\n<p><strong>What is Race Condition?<\/strong><\/p>\n<p><code># Initial balance = 100<br \/>\nThread A: balance += 50  # Expected: 150<br \/>\nThread B: balance -= 30  # Expected: 70<\/code><\/p>\n<p>If both threads read the balance at the same time (100), and then write their results, the final balance could be either 120, 150, or 70, depending on timing <\/p>\n","protected":false},"excerpt":{"rendered":"<p>Why Shedlock? Shedlock prevents concurrent execution of scheduled tasks in distributed systems. In a server where multiple instance of same JAR running, shedlock prevents simultaneous execution of task by different instance at the same time. By this shedlock prevents race conditions and prevents multiple nodes from executing the same task simultaneously, which can lead to&hellip; <a href=\"https:\/\/codethataint.com\/blog\/shedlock-why-when-and-how\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[372,371],"tags":[],"class_list":["post-5647","post","type-post","status-publish","format-standard","hentry","category-java-tools","category-spring-batch"],"_links":{"self":[{"href":"https:\/\/codethataint.com\/blog\/wp-json\/wp\/v2\/posts\/5647","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/codethataint.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/codethataint.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/codethataint.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/codethataint.com\/blog\/wp-json\/wp\/v2\/comments?post=5647"}],"version-history":[{"count":5,"href":"https:\/\/codethataint.com\/blog\/wp-json\/wp\/v2\/posts\/5647\/revisions"}],"predecessor-version":[{"id":5698,"href":"https:\/\/codethataint.com\/blog\/wp-json\/wp\/v2\/posts\/5647\/revisions\/5698"}],"wp:attachment":[{"href":"https:\/\/codethataint.com\/blog\/wp-json\/wp\/v2\/media?parent=5647"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/codethataint.com\/blog\/wp-json\/wp\/v2\/categories?post=5647"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/codethataint.com\/blog\/wp-json\/wp\/v2\/tags?post=5647"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}