<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="rss.xsl"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>ArchMan Blog</title>
        <link>https://archman.dev/blog</link>
        <description>ArchMan Blog</description>
        <lastBuildDate>Thu, 04 Sep 2025 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <item>
            <title><![CDATA[Mqtt connection management in Celery fork pool workers]]></title>
            <link>https://archman.dev/blog/solving-mqtt-connections-in-celery-fork-pool-workers</link>
            <guid>https://archman.dev/blog/solving-mqtt-connections-in-celery-fork-pool-workers</guid>
            <pubDate>Thu, 04 Sep 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[The Problem: When Module-Level MQTT Connections Go Wrong]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorWithStickyNavbar_jego" id="the-problem-when-module-level-mqtt-connections-go-wrong">The Problem: When Module-Level MQTT Connections Go Wrong<a href="https://archman.dev/blog/solving-mqtt-connections-in-celery-fork-pool-workers#the-problem-when-module-level-mqtt-connections-go-wrong" class="hash-link" aria-label="Direct link to The Problem: When Module-Level MQTT Connections Go Wrong" title="Direct link to The Problem: When Module-Level MQTT Connections Go Wrong">​</a></h2>
<p>Recently, I encountered a frustrating issue while working on a high-throughput data publishing system using Celery and MQTT. Our system was processing thousands of BLE (Bluetooth Low Energy) messages and publishing them to an MQTT broker. Everything worked fine initially, but after refactoring to improve code organization, messages stopped being delivered.</p>
<h3 class="anchor anchorWithStickyNavbar_jego" id="what-changed">What Changed?<a href="https://archman.dev/blog/solving-mqtt-connections-in-celery-fork-pool-workers#what-changed" class="hash-link" aria-label="Direct link to What Changed?" title="Direct link to What Changed?">​</a></h3>
<p>Originally, our MQTT client was created inside the Celery task:</p>
<div class="language-python codeBlockContainer_hVKb theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_p3fI"><pre tabindex="0" class="prism-code language-python codeBlock_N91u thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines__taO"><span class="token-line" style="color:#F8F8F2"><span class="token decorator annotation punctuation" style="color:rgb(248, 248, 242)">@shared_task</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">def</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">ble_message_publisher</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">self</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> data</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> devices</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> mqtt_topic</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> mqtt_qos</span><span class="token operator">=</span><span class="token number">2</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">try</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token comment" style="color:rgb(98, 114, 164)"># MQTT client created inside the task</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        mqtt </span><span class="token operator">=</span><span class="token plain"> MqttClient</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token comment" style="color:rgb(98, 114, 164)"># ... publish messages ...</span><br></span></code></pre></div></div>
<p>This worked but had a major drawback: it created a new MQTT connection for every task execution. With thousands of tasks, we were overwhelming the MQTT broker with connections.</p>
<p>To "optimize" this, I moved the MQTT client to module level:</p>
<div class="language-python codeBlockContainer_hVKb theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_p3fI"><pre tabindex="0" class="prism-code language-python codeBlock_N91u thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines__taO"><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)"># At the top of the file</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">mqtt </span><span class="token operator">=</span><span class="token plain"> MqttClient</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token decorator annotation punctuation" style="color:rgb(248, 248, 242)">@shared_task</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">def</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">ble_message_publisher</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">self</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> data</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> devices</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> mqtt_topic</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> mqtt_qos</span><span class="token operator">=</span><span class="token number">2</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">try</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token comment" style="color:rgb(98, 114, 164)"># Use the module-level mqtt client</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        mqtt</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">publish</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithStickyNavbar_jego" id="the-symptoms">The Symptoms<a href="https://archman.dev/blog/solving-mqtt-connections-in-celery-fork-pool-workers#the-symptoms" class="hash-link" aria-label="Direct link to The Symptoms" title="Direct link to The Symptoms">​</a></h2>
<p>After this change, strange things started happening:</p>
<ol>
<li>
<p><strong>No error messages</strong>, but messages weren't being delivered</p>
</li>
<li>
<p>The MQTT client appeared to be "connected" but couldn't send data</p>
</li>
<li>
<p>Logs showed timeout errors like:</p>
<div class="language-text codeBlockContainer_hVKb theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_p3fI"><pre tabindex="0" class="prism-code language-text codeBlock_N91u thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines__taO"><span class="token-line" style="color:#F8F8F2"><span class="token plain">[ERROR] Message not delivered within timeout for mid=502</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">[ERROR] Message not delivered within timeout for mid=503</span><br></span></code></pre></div></div>
</li>
</ol>
<h2 class="anchor anchorWithStickyNavbar_jego" id="understanding-the-root-cause">Understanding the Root Cause<a href="https://archman.dev/blog/solving-mqtt-connections-in-celery-fork-pool-workers#understanding-the-root-cause" class="hash-link" aria-label="Direct link to Understanding the Root Cause" title="Direct link to Understanding the Root Cause">​</a></h2>
<p>The issue stems from how Celery's fork pool workers operate:</p>
<h3 class="anchor anchorWithStickyNavbar_jego" id="how-fork-works">How Fork Works<a href="https://archman.dev/blog/solving-mqtt-connections-in-celery-fork-pool-workers#how-fork-works" class="hash-link" aria-label="Direct link to How Fork Works" title="Direct link to How Fork Works">​</a></h3>
<ol>
<li><strong>Parent Process</strong>: When Celery starts, it imports your modules and creates the MQTT connection</li>
<li><strong>Fork Happens</strong>: Celery creates child processes by forking the parent</li>
<li><strong>The Problem</strong>: File descriptors (like network sockets) can't be shared between processes after a fork</li>
</ol>
<p>Think of it like trying to share a phone call - you can't have multiple people talking on the same physical phone line at once!</p>
<h3 class="anchor anchorWithStickyNavbar_jego" id="visual-representation">Visual Representation<a href="https://archman.dev/blog/solving-mqtt-connections-in-celery-fork-pool-workers#visual-representation" class="hash-link" aria-label="Direct link to Visual Representation" title="Direct link to Visual Representation">​</a></h3>
<div class="language-text codeBlockContainer_hVKb theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_p3fI"><pre tabindex="0" class="prism-code language-text codeBlock_N91u thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines__taO"><span class="token-line" style="color:#F8F8F2"><span class="token plain">Before Fork:</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">Parent Process</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">└── mqtt = MqttClient() [Connected to broker]</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">After Fork:</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">Parent Process                    Child Process 1              Child Process 2</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">└── mqtt [Original socket]       └── mqtt [Broken socket]    └── mqtt [Broken socket]</span><br></span></code></pre></div></div>
<p>The child processes inherit a <em>copy</em> of the MQTT connection object, but the underlying socket is broken. The connection appears to exist but can't actually send data.</p>
<h2 class="anchor anchorWithStickyNavbar_jego" id="the-solution-thread-local-mqtt-clients">The Solution: Thread-Local MQTT Clients<a href="https://archman.dev/blog/solving-mqtt-connections-in-celery-fork-pool-workers#the-solution-thread-local-mqtt-clients" class="hash-link" aria-label="Direct link to The Solution: Thread-Local MQTT Clients" title="Direct link to The Solution: Thread-Local MQTT Clients">​</a></h2>
<p>To solve this, I implemented a thread-local pattern that creates separate MQTT clients for each thread/process combination:</p>
<div class="language-python codeBlockContainer_hVKb theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_p3fI"><pre tabindex="0" class="prism-code language-python codeBlock_N91u thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines__taO"><span class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> threading</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">from</span><span class="token plain"> typing </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> Dict</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># Instead of a single module-level client</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">_mqtt_clients</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> Dict</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token builtin" style="color:rgb(189, 147, 249)">int</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> MqttClient</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">_mqtt_lock </span><span class="token operator">=</span><span class="token plain"> threading</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">Lock</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">def</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">get_mqtt_client</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">-</span><span class="token operator">&gt;</span><span class="token plain"> MqttClient</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token triple-quoted-string string" style="color:rgb(255, 121, 198)">"""Get or create an MQTT client for current thread/process."""</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    thread_id </span><span class="token operator">=</span><span class="token plain"> threading</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">current_thread</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">ident</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">with</span><span class="token plain"> _mqtt_lock</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> thread_id </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">not</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">in</span><span class="token plain"> _mqtt_clients</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">            logger</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">info</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">f"Creating new MQTT client for thread </span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string-interpolation interpolation">thread_id</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">            _mqtt_clients</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">thread_id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> MqttClient</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        client </span><span class="token operator">=</span><span class="token plain"> _mqtt_clients</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">thread_id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token comment" style="color:rgb(98, 114, 164)"># Ensure connection is alive</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">not</span><span class="token plain"> client</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">is_connected</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">            logger</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">info</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">f"Reconnecting MQTT client for thread </span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string-interpolation interpolation">thread_id</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">            client</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">reconnect</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> client</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token decorator annotation punctuation" style="color:rgb(248, 248, 242)">@shared_task</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">def</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">ble_message_publisher</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">self</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> data</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> devices</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> mqtt_topic</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> mqtt_qos</span><span class="token operator">=</span><span class="token number">2</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">try</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token comment" style="color:rgb(98, 114, 164)"># Get thread-local client instead of using global</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        mqtt </span><span class="token operator">=</span><span class="token plain"> get_mqtt_client</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token comment" style="color:rgb(98, 114, 164)"># ... rest of the code ...</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithStickyNavbar_jego" id="why-this-works">Why This Works<a href="https://archman.dev/blog/solving-mqtt-connections-in-celery-fork-pool-workers#why-this-works" class="hash-link" aria-label="Direct link to Why This Works" title="Direct link to Why This Works">​</a></h2>
<h3 class="anchor anchorWithStickyNavbar_jego" id="1-process-isolation">1. Process Isolation<a href="https://archman.dev/blog/solving-mqtt-connections-in-celery-fork-pool-workers#1-process-isolation" class="hash-link" aria-label="Direct link to 1. Process Isolation" title="Direct link to 1. Process Isolation">​</a></h3>
<p>After forking, each child process has its own copy of the <code>_mqtt_clients</code> dictionary. When a task runs in a child process, it creates its own fresh MQTT connection instead of using the broken inherited one.</p>
<h3 class="anchor anchorWithStickyNavbar_jego" id="2-connection-reuse">2. Connection Reuse<a href="https://archman.dev/blog/solving-mqtt-connections-in-celery-fork-pool-workers#2-connection-reuse" class="hash-link" aria-label="Direct link to 2. Connection Reuse" title="Direct link to 2. Connection Reuse">​</a></h3>
<p>Within each process/thread, the MQTT client is reused across multiple task executions. This dramatically reduces the number of connections compared to creating a new one for each task.</p>
<h3 class="anchor anchorWithStickyNavbar_jego" id="3-thread-safety">3. Thread Safety<a href="https://archman.dev/blog/solving-mqtt-connections-in-celery-fork-pool-workers#3-thread-safety" class="hash-link" aria-label="Direct link to 3. Thread Safety" title="Direct link to 3. Thread Safety">​</a></h3>
<p>The <code>threading.Lock()</code> ensures that only one thread at a time can create or access clients in the dictionary, preventing race conditions.</p>
<h2 class="anchor anchorWithStickyNavbar_jego" id="the-results">The Results<a href="https://archman.dev/blog/solving-mqtt-connections-in-celery-fork-pool-workers#the-results" class="hash-link" aria-label="Direct link to The Results" title="Direct link to The Results">​</a></h2>
<h3 class="anchor anchorWithStickyNavbar_jego" id="before-module-level-client">Before (Module-level client)<a href="https://archman.dev/blog/solving-mqtt-connections-in-celery-fork-pool-workers#before-module-level-client" class="hash-link" aria-label="Direct link to Before (Module-level client)" title="Direct link to Before (Module-level client)">​</a></h3>
<ul>
<li>❌ 0 successful messages out of 200</li>
<li>❌ All messages timing out</li>
<li>❌ Broken socket connections</li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_jego" id="after-thread-local-pattern">After (Thread-local pattern)<a href="https://archman.dev/blog/solving-mqtt-connections-in-celery-fork-pool-workers#after-thread-local-pattern" class="hash-link" aria-label="Direct link to After (Thread-local pattern)" title="Direct link to After (Thread-local pattern)">​</a></h3>
<ul>
<li>✅ Connection reuse within threads</li>
<li>✅ Fresh connections per process</li>
<li>✅ Stable message delivery</li>
<li>✅ Reduced load on MQTT broker</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_jego" id="key-takeaways">Key Takeaways<a href="https://archman.dev/blog/solving-mqtt-connections-in-celery-fork-pool-workers#key-takeaways" class="hash-link" aria-label="Direct link to Key Takeaways" title="Direct link to Key Takeaways">​</a></h2>
<ol>
<li><strong>Be careful with module-level network connections</strong> in forking environments like Celery</li>
<li><strong>File descriptors don't survive fork()</strong> - this includes sockets, database connections, etc.</li>
<li><strong>Thread-local storage</strong> is a great pattern for managing per-worker resources</li>
<li><strong>Always verify connections</strong> before use, especially in long-running workers</li>
</ol>
<h2 class="anchor anchorWithStickyNavbar_jego" id="implementation-tips">Implementation Tips<a href="https://archman.dev/blog/solving-mqtt-connections-in-celery-fork-pool-workers#implementation-tips" class="hash-link" aria-label="Direct link to Implementation Tips" title="Direct link to Implementation Tips">​</a></h2>
<p>If you're facing similar issues:</p>
<ol>
<li>
<p><strong>Check your worker pool type</strong>: This issue specifically affects fork-based pools. Thread or gevent pools behave differently.</p>
</li>
<li>
<p><strong>Monitor connection counts</strong>: Keep an eye on your MQTT broker's connection count to ensure you're not creating too many.</p>
</li>
<li>
<p><strong>Add connection health checks</strong>: Always verify the connection is alive before using it:</p>
<div class="language-python codeBlockContainer_hVKb theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_p3fI"><pre tabindex="0" class="prism-code language-python codeBlock_N91u thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines__taO"><span class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">not</span><span class="token plain"> client</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">is_connected</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    client</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">reconnect</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><br></span></code></pre></div></div>
</li>
<li>
<p><strong>Consider connection pooling</strong>: For high-throughput systems, you might want to implement a proper connection pool with size limits.</p>
</li>
<li>
<p><strong>Log thread/process IDs</strong>: This helps debug which worker is creating which connection:</p>
<div class="language-python codeBlockContainer_hVKb theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_p3fI"><pre tabindex="0" class="prism-code language-python codeBlock_N91u thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines__taO"><span class="token-line" style="color:#F8F8F2"><span class="token plain">logger</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">info</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">f"Creating new MQTT client for thread </span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string-interpolation interpolation">thread_id</span><span class="token string-interpolation interpolation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token string-interpolation string" style="color:rgb(255, 121, 198)">"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><br></span></code></pre></div></div>
</li>
</ol>
<h2 class="anchor anchorWithStickyNavbar_jego" id="conclusion">Conclusion<a href="https://archman.dev/blog/solving-mqtt-connections-in-celery-fork-pool-workers#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion">​</a></h2>
<p>Moving from task-level to module-level resources seems like an obvious optimization, but in forking environments, it can lead to subtle and hard-to-debug issues. The thread-local pattern provides a nice middle ground - avoiding the overhead of creating connections for every task while ensuring each worker has a valid, working connection.</p>
<p>Remember: when working with Celery and network connections, always consider how your code will behave after a fork!</p>]]></content:encoded>
            <category>Software Development</category>
            <category>Architecture</category>
            <category>Celery</category>
            <category>MQTT</category>
            <category>RabbitMQ</category>
            <category>Threading</category>
            <category>Connections</category>
            <category>Python</category>
            <category>Multiprocessing</category>
            <category>Fork</category>
            <category>Sockets</category>
            <category>Network Programming</category>
            <category>Distributed Systems</category>
            <category>Message Broker</category>
            <category>Connection Pooling</category>
            <category>Thread-Local</category>
            <category>Concurrency</category>
            <category>Parallelism</category>
            <category>Process Isolation</category>
            <category>Task Queue</category>
            <category>Broker</category>
            <category>Bug Fix</category>
            <category>Troubleshooting</category>
            <category>Best Practices</category>
            <category>Performance</category>
            <category>Reliability</category>
            <category>High Throughput</category>
            <category>Resource Management</category>
            <category>File Descriptors</category>
            <category>Debugging</category>
            <category>Code Smells</category>
            <category>Anti-Patterns</category>
            <category>Python Tips</category>
            <category>Celery Tasks</category>
            <category>MQTT Client</category>
            <category>Connection Management</category>
            <category>Thread Safety</category>
            <category>Production</category>
            <category>Distributed Computing</category>
        </item>
    </channel>
</rss>