<?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>DEV Community: swaroop kolasani</title>
    <description>The latest articles on DEV Community by swaroop kolasani (@swaroop_kolasani_).</description>
    <link>https://dev.to/swaroop_kolasani_</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3836075%2F47542818-da30-45f9-acec-54426aa1df4c.jpg</url>
      <title>DEV Community: swaroop kolasani</title>
      <link>https://dev.to/swaroop_kolasani_</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/swaroop_kolasani_"/>
    <language>en</language>
    <item>
      <title>Debugging an Idempotency Bug in Temporal Worker Deployment Version Deletion</title>
      <dc:creator>swaroop kolasani</dc:creator>
      <pubDate>Fri, 28 Aug 2026 16:08:05 +0000</pubDate>
      <link>https://dev.to/swaroop_kolasani_/debugging-an-idempotency-bug-in-temporal-worker-deployment-version-deletion-4an</link>
      <guid>https://dev.to/swaroop_kolasani_/debugging-an-idempotency-bug-in-temporal-worker-deployment-version-deletion-4an</guid>
      <description>&lt;p&gt;I recently worked through &lt;a href="https://github.com/temporalio/temporal/issues/11539" rel="noopener noreferrer"&gt;Temporal issue #11539&lt;/a&gt;, which involved deleting Worker Deployment Versions when the parent deployment and version workflow had drifted out of sync.&lt;/p&gt;

&lt;p&gt;The code fix was small. The interesting part was reproducing the inconsistent state and finding the correct place to make deletion idempotent.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;A Worker Deployment stores version summaries in the parent deployment workflow.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Worker Deployment
    |
    | DeleteWorkerDeploymentVersion
    v
Worker Deployment Version workflow
    |
    | delete succeeds
    v
Parent removes version from State.Versions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The bug appears when the parent still lists a version even though the version workflow is already completed or missing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DescribeWorkerDeployment
    -&amp;gt; version still listed

DescribeWorkerDeploymentVersion
    -&amp;gt; version not found

Version workflow
    -&amp;gt; completed or missing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When &lt;code&gt;DeleteWorkerDeploymentVersion&lt;/code&gt; runs, Temporal tries to send a &lt;code&gt;delete-version&lt;/code&gt; update to that workflow.&lt;/p&gt;

&lt;p&gt;Because the workflow can no longer accept the update, the server returns errors such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;workflow execution already completed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;workflow not found for ID: ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That error propagates back through the activity.&lt;/p&gt;

&lt;p&gt;The parent only removes the version summary after the activity succeeds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="nb"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Versions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Version&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the stale entry remains, and every later delete fails for the same reason.&lt;/p&gt;

&lt;p&gt;This matters because the stale version can still count toward deployment limits and can prevent the entire Worker Deployment from being deleted.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reproducing the bug
&lt;/h2&gt;

&lt;p&gt;I reproduced the issue locally on Temporal &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Initial state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;parent deployment workflow: RUNNING
version workflow:           RUNNING
parent versionSummaries:    contains v1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I then tested two scenarios.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Version workflow completed
&lt;/h3&gt;

&lt;p&gt;I terminated only the version workflow while keeping the parent running.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;parent deployment: still lists v1
version workflow:  TERMINATED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deleting the version failed, with the server logs showing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;workflow execution already completed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The parent summary remained.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Version workflow missing
&lt;/h3&gt;

&lt;p&gt;I then removed the version workflow completely.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DescribeWorkerDeployment
    -&amp;gt; v1 still listed

DescribeWorkerDeploymentVersion
    -&amp;gt; not found

workflow describe
    -&amp;gt; workflow not found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deleting the version failed again, this time with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;workflow not found for ID: ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important result was that retention was not required to reproduce the bug.&lt;/p&gt;

&lt;p&gt;The failure exists whenever the parent version summary outlives the version workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing the fix
&lt;/h2&gt;

&lt;p&gt;There were several possible places to handle the problem.&lt;/p&gt;

&lt;p&gt;The frontend could swallow the &lt;code&gt;NotFound&lt;/code&gt; error, but that would only hide the failure. The parent workflow would still keep the stale entry.&lt;/p&gt;

&lt;p&gt;The parent could periodically reconcile missing version workflows, but that would introduce a broader cleanup mechanism for something deletion can handle directly.&lt;/p&gt;

&lt;p&gt;The better question was:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Where does &lt;code&gt;NotFound&lt;/code&gt; mean the delete operation has already reached its desired state?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The answer was &lt;code&gt;Activities.DeleteWorkerDeploymentVersion&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If the version workflow is already gone, the version-side goal of deletion is already satisfied.&lt;/p&gt;

&lt;p&gt;So deletion can safely treat that specific condition as success:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;outcome&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;updateWorkflow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;notFound&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;serviceerror&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NotFound&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;As&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;notFound&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I intentionally left the generic &lt;code&gt;updateWorkflow&lt;/code&gt; helper unchanged.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;NotFound&lt;/code&gt; does not mean success for every Temporal update. It only has that meaning here because this is a delete operation.&lt;/p&gt;

&lt;p&gt;Once the activity succeeds, the existing parent workflow logic can remove the stale version summary normally.&lt;/p&gt;

&lt;h2&gt;
  
  
  Regression testing
&lt;/h2&gt;

&lt;p&gt;Before changing the production code, I added tests for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version workflow completed  -&amp;gt; delete should succeed
version workflow missing    -&amp;gt; delete should succeed
unrelated service error     -&amp;gt; delete should still fail
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first two failed before the fix, while the unrelated error still behaved normally.&lt;/p&gt;

&lt;p&gt;I also added a functional regression test that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Creates a deployment and version.&lt;/li&gt;
&lt;li&gt;Closes the version workflow.&lt;/li&gt;
&lt;li&gt;Keeps the parent deployment running.&lt;/li&gt;
&lt;li&gt;Calls the public delete API.&lt;/li&gt;
&lt;li&gt;Verifies that the version disappears from the parent's summaries.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After adding the &lt;code&gt;NotFound&lt;/code&gt; handling, the regression tests passed without changing unrelated deletion behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  What happened upstream
&lt;/h2&gt;

&lt;p&gt;I opened &lt;a href="https://github.com/temporalio/temporal/pull/11638" rel="noopener noreferrer"&gt;PR #11638&lt;/a&gt; with the idempotent deletion change and regression coverage.&lt;/p&gt;

&lt;p&gt;While it was under review, a Temporal maintainer merged &lt;a href="https://github.com/temporalio/temporal/pull/11696" rel="noopener noreferrer"&gt;PR #11696&lt;/a&gt;, which applies the same core behavior: treat a missing Worker Deployment Version as already deleted.&lt;/p&gt;

&lt;p&gt;My PR was therefore not the commit that landed upstream, but the issue was ultimately resolved using the same underlying approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;The fix itself was only a few lines.&lt;/p&gt;

&lt;p&gt;The real work was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;reproduce inconsistent state
        ↓
trace the failed state transition
        ↓
find the correct idempotency boundary
        ↓
write a failing regression test
        ↓
make the smallest semantic change
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The bug was not really about a missing workflow.&lt;/p&gt;

&lt;p&gt;It was about two durable pieces of state completing the same delete operation at different times.&lt;/p&gt;

&lt;p&gt;Once the child-side state is already gone, retrying deletion should converge toward the desired final state instead of requiring the deleted object to still exist.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/temporalio/temporal/issues/11539" rel="noopener noreferrer"&gt;Temporal issue #11539&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/temporalio/temporal/pull/11638" rel="noopener noreferrer"&gt;My PR #11638 — Make Worker Deployment Version deletion idempotent&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/temporalio/temporal/pull/11696" rel="noopener noreferrer"&gt;Merged PR #11696 — Treat missing Worker Deployment Version as deleted&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>backend</category>
      <category>debugging</category>
      <category>github</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Before You Upgrade Hardware, Fix the Software</title>
      <dc:creator>swaroop kolasani</dc:creator>
      <pubDate>Wed, 01 Apr 2026 06:27:39 +0000</pubDate>
      <link>https://dev.to/swaroop_kolasani_/before-you-upgrade-hardware-fix-the-software-292k</link>
      <guid>https://dev.to/swaroop_kolasani_/before-you-upgrade-hardware-fix-the-software-292k</guid>
      <description>&lt;p&gt;&lt;em&gt;Better software algorithms can significantly improve effective memory efficiency, but only until the workload reaches a real hardware bottleneck.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Misconception
&lt;/h2&gt;

&lt;p&gt;Recent work such as Google's TurboQuant shows that software can significantly reduce memory pressure for specific workloads like LLM inference. At the same time, companies across the AI stack are investing in physical infrastructure such as power and chips to sustain growing compute demand. Meta has expanded its energy strategy, including major nuclear power agreements for AI-related infrastructure, while NVIDIA remains tied to the semiconductor path through advanced chip production and packaging. Together, these trends raise a broader question: if software can make systems more efficient, how often are we upgrading hardware before we have truly exhausted software optimization?&lt;/p&gt;

&lt;h2&gt;
  
  
  The Impact
&lt;/h2&gt;

&lt;p&gt;The decision to optimize software or upgrade infrastructure is not only a technical choice. It affects cost, scalability, engineering time, and system reliability. When teams upgrade hardware too early, they often spend more without understanding the real bottleneck. Poor algorithms, inefficient memory use, weak caching, unnecessary data movement, or bad execution placement remain hidden behind larger machines. The system appears faster, but the underlying inefficiency remains unresolved.&lt;/p&gt;

&lt;p&gt;The opposite mistake is also costly. If teams continue forcing software optimization after the workload has already reached a true hardware limit, they waste time chasing marginal gains. At that point, the system becomes harder to maintain, more fragile, and often less predictable under real load. What begins as optimization turns into complexity without meaningful return.&lt;/p&gt;

&lt;p&gt;The real impact, then, is strategic: knowing when software can still recover efficiency, and when infrastructure upgrades are the only rational next step. Reaching that decision requires evaluating a wide range of technical scenarios, because the right answer depends on the workload, the bottleneck, and the tradeoffs the system can tolerate.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bottleneck
&lt;/h2&gt;

&lt;p&gt;Before upgrading infrastructure, the first question is whether the workload is truly hardware constrained or simply inefficient. In many cases, software can recover substantial performance by reducing active memory pressure, improving execution strategy, or restructuring the workload itself. Compression and quantization can reduce memory use, better caching and locality can reduce wasted movement and repeated work, and stronger algorithms or data structures can change the resource profile of the system more than a hardware upgrade would.&lt;/p&gt;

&lt;p&gt;Some of the most meaningful gains, however, come from architecture rather than low-level optimization. Software efficiency is not only about making a single process use less memory; it is also about deciding where work should run. Systems often become more efficient by separating latency-sensitive tasks from heavy background computation, reducing local resource pressure, and moving burstable workloads into environments better suited to them. Ephemeral cloud burst is one example of this approach: instead of permanently upgrading local hardware, a system can offload short-lived, compute-intensive, or memory-heavy work to temporary remote machines, using software to place the workload where the right resources already exist only when they are needed.&lt;/p&gt;

&lt;p&gt;But software optimization has limits. Every efficiency technique introduces a tradeoff: compression adds processing overhead, caching consumes memory, recomputation trades storage for compute, and offloading introduces latency and synchronization cost. These strategies remain effective only while the system can tolerate those tradeoffs. In demanding or real-time workloads, that tolerance is often narrow. Interactive systems, games, rendering pipelines, and latency-sensitive applications cannot absorb unlimited overhead in exchange for lower local resource usage.&lt;/p&gt;

&lt;p&gt;This is the real bottleneck, once a workload consistently hits limits in memory capacity, bandwidth, compute throughput, or latency tolerance, the issue is no longer inefficiency alone. It is a resource ceiling. At that point, the question is no longer how to force more efficiency out of the same hardware, but whether the workload now requires a different machine, a different execution environment, or a different system architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Last Option: Hardware
&lt;/h2&gt;

&lt;p&gt;Hardware should be the last option, not the first reaction. Once software inefficiencies have been removed, architecture has been improved, and workload placement has been made efficient, the remaining limit is no longer design waste but physical constraint. That is the point where more RAM, more bandwidth, more compute, or a different class of machine becomes necessary.&lt;/p&gt;

&lt;p&gt;Upgrading hardware at this stage is not an admission that software failed. It is an acknowledgment that software has already delivered its meaningful gains. &lt;strong&gt;The real mistake is upgrading before reaching this point. Hardware is not the enemy of efficiency; premature hardware dependency is.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Even this final option has constraints. Upgrading infrastructure or moving workloads into the cloud does not remove bottlenecks entirely; it often replaces local capacity limits with distributed systems limits such as latency, bandwidth, synchronization overhead, and data locality. &lt;strong&gt;The real problem is not whether to optimize software or upgrade hardware but knowing exactly where the bottleneck has moved next.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>infrastructure</category>
      <category>latency</category>
      <category>software</category>
    </item>
  </channel>
</rss>
