<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Devops on Tachera W Sasi</title><link>https://tacherasasi.github.io/tags/devops/</link><description>Recent content in Devops on Tachera W Sasi</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Tue, 28 Apr 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tacherasasi.github.io/tags/devops/index.xml" rel="self" type="application/rss+xml"/><item><title>The One-Word Bug That Broke Our Worker for 6 Days</title><link>https://tacherasasi.github.io/posts/the-one-word-bug-that-broke-our-worker-for-6-days-9cadd534ee77/</link><pubDate>Tue, 28 Apr 2026 00:00:00 +0000</pubDate><guid>https://tacherasasi.github.io/posts/the-one-word-bug-that-broke-our-worker-for-6-days-9cadd534ee77/</guid><description>&lt;p&gt;&lt;img src="https://cdn-images-1.medium.com/max/1024/1*CtVFn53iNGx3lQ2MEe3JKw.png" alt=""&gt;&lt;/p&gt;
&lt;h3 id="for-six-days-our-system-was-lying-to-us"&gt;For six days, our system was lying to us&lt;/h3&gt;
&lt;p&gt;Not crashing. Not failing loudly. Just quietly pretending everything was fine while doing absolutely nothing.&lt;/p&gt;
&lt;p&gt;And the cause?&lt;/p&gt;
&lt;p&gt;One wrong word in a shell script.&lt;/p&gt;
&lt;h3 id="everything-looked-fine"&gt;Everything Looked Fine&lt;/h3&gt;
&lt;p&gt;We run a pretty standard setup:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A Go API (core)&lt;/li&gt;
&lt;li&gt;A background worker (minion)&lt;/li&gt;
&lt;li&gt;Redis for queues&lt;/li&gt;
&lt;li&gt;Docker Compose holding it all together&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We recently moved bulk SMS processing into the worker using Asynq. Clean separation, better scalability, the usual win.&lt;/p&gt;
&lt;p&gt;We deployed.&lt;/p&gt;
&lt;p&gt;The API responded perfectly:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;200 OK
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;#34;bulk SMS queued successfully
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Except… no SMS was ever sent.&lt;/p&gt;
&lt;h3 id="the-worker-was-dead-silent"&gt;The Worker Was Dead Silent&lt;/h3&gt;
&lt;p&gt;First instinct: check the worker logs.&lt;/p&gt;
&lt;p&gt;Nothing.&lt;/p&gt;
&lt;p&gt;No errors. No panics. No trace of the task ever being picked up.&lt;/p&gt;
&lt;p&gt;Just one quiet warning after retries were exhausted.&lt;/p&gt;
&lt;p&gt;Meanwhile, everything else worked:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;login alerts ✅&lt;/li&gt;
&lt;li&gt;email notifications ✅&lt;/li&gt;
&lt;li&gt;scheduled jobs ✅&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Only bulk SMS failed.&lt;/p&gt;
&lt;p&gt;At this point, we did what engineers always do when the signal is weak:&lt;/p&gt;
&lt;p&gt;We guessed.&lt;/p&gt;
&lt;h3 id="we-investigated-the-wrong-things"&gt;We Investigated the Wrong Things&lt;/h3&gt;
&lt;p&gt;We went deep into the stack:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Suspected Redis corruption&lt;/li&gt;
&lt;li&gt;Questioned queue priorities&lt;/li&gt;
&lt;li&gt;Looked for serialization bugs&lt;/li&gt;
&lt;li&gt;Added panic recovery middleware&lt;/li&gt;
&lt;li&gt;Logged full payloads&lt;/li&gt;
&lt;li&gt;Added startup diagnostics&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We deployed again.&lt;/p&gt;
&lt;p&gt;Still nothing.&lt;/p&gt;
&lt;p&gt;And this is where most people waste days. We almost did.&lt;/p&gt;
&lt;p&gt;Because we were debugging behavior… not reality.&lt;/p&gt;
&lt;h3 id="the-subtle-clue"&gt;The Subtle Clue&lt;/h3&gt;
&lt;p&gt;There was one tiny inconsistency.&lt;/p&gt;
&lt;p&gt;Our worker used to log:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Worker started
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We had changed it to:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Worker started (server + scheduler running)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;After deploy, the logs still showed the old message.&lt;/p&gt;
&lt;p&gt;That should have been the end of the investigation right there.&lt;/p&gt;
&lt;p&gt;Our code wasn’t running.&lt;/p&gt;
&lt;h3 id="the-real-problem"&gt;The Real Problem&lt;/h3&gt;
&lt;p&gt;The issue wasn’t in Go.&lt;br&gt;
It wasn’t in Redis.&lt;br&gt;
It wasn’t in queues.&lt;/p&gt;
&lt;p&gt;It was in this script:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="l"&gt;docker compose build --no-cache api &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="l"&gt;docker compose up -d --remove-orphans&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And this config:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;services&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;core&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;minion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Look closely.&lt;/p&gt;
&lt;p&gt;We were building api.&lt;/p&gt;
&lt;p&gt;There is no api.&lt;/p&gt;
&lt;p&gt;Docker didn’t complain. It didn’t warn. It didn’t fail.&lt;/p&gt;
&lt;p&gt;It just did nothing.&lt;/p&gt;
&lt;p&gt;And then docker compose up happily started containers using old cached images.&lt;/p&gt;
&lt;p&gt;So for six days:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The API sometimes rebuilt&lt;/li&gt;
&lt;li&gt;The worker never rebuilt&lt;/li&gt;
&lt;li&gt;The system kept deploying successfully&lt;/li&gt;
&lt;li&gt;Our fixes never actually shipped&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We were debugging code that wasn’t even running.&lt;/p&gt;
&lt;h3 id="the-fix-was-embarrassing"&gt;The Fix Was Embarrassing&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- docker compose build --no-cache api
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;+ docker compose build --no-cache core minion
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That’s it.&lt;/p&gt;
&lt;p&gt;Two words instead of one.&lt;/p&gt;
&lt;p&gt;We deployed again.&lt;/p&gt;
&lt;p&gt;Immediately:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;New startup logs appeared&lt;/li&gt;
&lt;li&gt;Bulk SMS tasks were picked up&lt;/li&gt;
&lt;li&gt;Jobs executed in milliseconds&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;System fixed.&lt;/p&gt;
&lt;h3 id="what-this-actually-teaches"&gt;What This Actually Teaches&lt;/h3&gt;
&lt;h3 id="1-if-your-fix-doesnt-change-behavior-assume-it-never-deployed"&gt;1. If your fix doesn’t change behavior, assume it never deployed&lt;/h3&gt;
&lt;p&gt;Stop overthinking.&lt;/p&gt;
&lt;p&gt;Before touching Redis, queues, or concurrency… ask:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;“Is my new binary even running?”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you skip this, you deserve the hours you’re about to lose.&lt;/p&gt;
&lt;h3 id="2-silent-failures-are-worse-than-crashes"&gt;2. Silent failures are worse than crashes&lt;/h3&gt;
&lt;p&gt;Crashes force action.&lt;/p&gt;
&lt;p&gt;Silent no-ops pass CI, pass deploys, and waste days.&lt;/p&gt;
&lt;p&gt;docker compose build should not succeed when the service doesn’t exist.&lt;/p&gt;
&lt;p&gt;But it does.&lt;/p&gt;
&lt;p&gt;So you have to defend against that yourself.&lt;/p&gt;
&lt;h3 id="3-your-deployment-pipeline-is-part-of-your-codebase"&gt;3. Your deployment pipeline is part of your codebase&lt;/h3&gt;
&lt;p&gt;People treat scripts like they’re disposable.&lt;/p&gt;
&lt;p&gt;They’re not.&lt;/p&gt;
&lt;p&gt;That one-line deploy.sh caused more damage than any bug in the Go code.&lt;/p&gt;
&lt;p&gt;Review it. Test it. Break it intentionally.&lt;/p&gt;
&lt;h3 id="4-logging-is-not-optional-for-workers"&gt;4. Logging is not optional for workers&lt;/h3&gt;
&lt;p&gt;If your worker can fail quietly, it will.&lt;/p&gt;
&lt;p&gt;You need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;startup fingerprints (commit hash, build time)&lt;/li&gt;
&lt;li&gt;task-level logs&lt;/li&gt;
&lt;li&gt;explicit success/failure visibility&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Otherwise, you’re blind.&lt;/p&gt;
&lt;h3 id="5-rename-things-properly-or-dont-rename-them-at-all"&gt;5. Rename things properly or don’t rename them at all&lt;/h3&gt;
&lt;p&gt;api became core.&lt;/p&gt;
&lt;p&gt;The script didn’t.&lt;/p&gt;
&lt;p&gt;That mismatch cost six days.&lt;/p&gt;
&lt;p&gt;Refactors don’t end when the code compiles.&lt;/p&gt;
&lt;h3 id="the-real-lesson"&gt;The Real Lesson&lt;/h3&gt;
&lt;p&gt;This wasn’t a debugging failure.&lt;/p&gt;
&lt;p&gt;It was a discipline failure.&lt;/p&gt;
&lt;p&gt;We trusted the pipeline without verifying it.&lt;br&gt;
We chased complex explanations before ruling out simple ones.&lt;br&gt;
We debugged symptoms instead of checking fundamentals.&lt;/p&gt;
&lt;p&gt;And that’s how a one-word mistake beats experienced engineers.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://medium.com/_/stat?event=post.clientViewed&amp;amp;referrerSource=full_rss&amp;amp;postId=9cadd534ee77" alt=""&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://medium.com/@tacherasasi/the-one-word-bug-that-broke-our-worker-for-6-days-9cadd534ee77?source=rss-9a41d7ec29fb------2"&gt;Medium&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</description></item></channel></rss>