<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/">
    <channel>
        <title><![CDATA[Gavide's Blog]]></title>
        <description><![CDATA[Technical notes and essays from Davide Galilei about software, security, systems, and programming.]]></description>
        <link>https://blog.gavide.dev</link>
        <image>
            <url>https://blog.gavide.dev/web-app-manifest-512x512.png</url>
            <title>Gavide&apos;s Blog</title>
            <link>https://blog.gavide.dev</link>
        </image>
        <generator>RSS for Node</generator>
        <lastBuildDate>Thu, 07 May 2026 21:03:25 GMT</lastBuildDate>
        <atom:link href="https://blog.gavide.dev/feed.xml" rel="self" type="application/rss+xml"/>
        <pubDate>Thu, 09 Oct 2025 00:00:00 GMT</pubDate>
        <copyright><![CDATA[2026 Davide Galilei]]></copyright>
        <language><![CDATA[en]]></language>
        <managingEditor><![CDATA[Davide Galilei]]></managingEditor>
        <webMaster><![CDATA[Davide Galilei]]></webMaster>
        <ttl>60</ttl>
        <atom:link href="https://blog.gavide.dev/sitemap.xml" rel="sitemap" type="application/xml"/>
        <item>
            <title><![CDATA[ESP32 and Termux]]></title>
            <description><![CDATA[Documenting my journey in trying to use my ESP32 on my phone through Termux]]></description>
            <link>https://blog.gavide.dev/blog/esp32-and-termux</link>
            <guid isPermaLink="false">https://blog.gavide.dev/blog/esp32-and-termux</guid>
            <category><![CDATA[blog]]></category>
            <category><![CDATA[embedded]]></category>
            <category><![CDATA[termux]]></category>
            <category><![CDATA[esp32]]></category>
            <category><![CDATA[micropython]]></category>
            <category><![CDATA[fun]]></category>
            <dc:creator><![CDATA[Davide Galilei]]></dc:creator>
            <pubDate>Thu, 09 Oct 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://blog.gavide.dev/_app/immutable/assets/thumbnail.DXPcb2Gg.jpg" length="0" type="image/jpeg"/>
            <content:encoded><![CDATA[<article><header><h1>ESP32 and Termux</h1><p><time datetime="2025-10-09T00:00:00.000Z">Thu, 09 Oct 2025 00:00:00 GMT</time></p></header><figure><picture><source srcset="https://blog.gavide.dev/_app/immutable/assets/thumbnail.Pe-FQ3kr.avif 1x, https://blog.gavide.dev/_app/immutable/assets/thumbnail.H-L3DVmd.avif 2x" type="image/avif"/><source srcset="https://blog.gavide.dev/_app/immutable/assets/thumbnail.M-jwy3Fp.webp 1x, https://blog.gavide.dev/_app/immutable/assets/thumbnail.cqh0RY9c.webp 2x" type="image/webp"/><source srcset="https://blog.gavide.dev/_app/immutable/assets/thumbnail.DlEhUxEm.jpg 1x, https://blog.gavide.dev/_app/immutable/assets/thumbnail.BvZFKiW1.jpg 2x" type="image/jpeg"/><img src="https://blog.gavide.dev/_app/immutable/assets/thumbnail.BvZFKiW1.jpg" alt="Screenshot of Termux showing the flash command" style="width:100%; max-width:500px; height:auto; display:block;" width="1280" height="1223"/></picture></figure> <p>If you’re like me, you might enjoy being able to do things on your phone that you might otherwise do from your computer.</p> <p>I wanted to play around with my <code>ESP32-WROOM-32</code> development board, but apparently there is no online guide specifically for Termux, so I want to document the steps that worked for me as a future reference for myself and others.</p> <blockquote><p>⚠️ <strong>DISCLAIMER</strong></p> <p><strong>I am not responsible for any damage</strong> that could occurr by following this guide. This is written for educational purposes.</p></blockquote> <h2 id="requirements"><a href="#requirements" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>Requirements</h2> <ul><li>any ESP32 development board will do, but in my case I will use a <code>ESP32-WROOM-32</code></li> <li>an <a href="https://en.wikipedia.org/wiki/USB_On-The-Go" rel="nofollow">OTG adapter</a></li> <li>a USB-A cable (in my case micro-USB, but it depends by your board)</li> <li>a phone with Termux installed, <a href="https://f-droid.org/packages/com.termux/" rel="nofollow">ideally from F-Droid</a></li></ul> <blockquote><p>❗️ <strong>NOTE</strong></p> <p>Make sure that your USB-A cable supports data transfer. This is crucial.</p> <p>Many cables I tried either did not support data transfer or were not delivering the power correctly, <a href="https://en.wikipedia.org/wiki/Brownout_(electricity)" rel="nofollow">making the board brownout</a>.</p></blockquote> <h2 id="getting-started"><a href="#getting-started" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>Getting started</h2> <p>The first thing you need to do is installing <a href="https://play.google.com/store/apps/details?id=com.hardcodedjoy.tcpuart" rel="nofollow"><code>TCPUART transparent Bridge</code></a>. This application will act as a bridge between the android Serial USB API and Termux. It will expose a local two-way TCP server that will forward the data to and from <code>UART</code>.</p> <blockquote><p>Installing a third party application is not ideal. An alternative could have been using <code>termux-usb</code> through <a href="https://f-droid.org/packages/com.termux.api/" rel="nofollow"><code>Termux-API</code></a>, but I was facing constant disconnections and setup issues, so I settled for this app.</p></blockquote> <figure><div style="display:flex; gap:1rem; flex-wrap:wrap; align-items:flex-start; margin:1em 0;"><figure style="flex:1 1 200px; max-width:300px; margin:0;"><picture><source srcset="https://blog.gavide.dev/_app/immutable/assets/tcpuart1.CKCCPmPB.avif 1x, https://blog.gavide.dev/_app/immutable/assets/tcpuart1.ZmfZUdTb.avif 2x" type="image/avif"/><source srcset="https://blog.gavide.dev/_app/immutable/assets/tcpuart1.1pQSgd6F.webp 1x, https://blog.gavide.dev/_app/immutable/assets/tcpuart1.Bmx1Mc16.webp 2x" type="image/webp"/><source srcset="https://blog.gavide.dev/_app/immutable/assets/tcpuart1.Cq5J0Uuq.jpg 1x, https://blog.gavide.dev/_app/immutable/assets/tcpuart1.DEXX441a.jpg 2x" type="image/jpeg"/><img src="https://blog.gavide.dev/_app/immutable/assets/tcpuart1.DEXX441a.jpg" alt="tcpuart screenshot 1" style="width:100%; height:auto; display:block;" width="616" height="1280"/></picture> <figcaption style="font-size:0.9em; margin-top:0.4rem">TCPUART main screen</figcaption></figure> <figure style="flex:1 1 200px; max-width:300px; margin:0;"><picture><source srcset="https://blog.gavide.dev/_app/immutable/assets/tcpuart2.DMPhvvCx.avif 1x, https://blog.gavide.dev/_app/immutable/assets/tcpuart2.BZ_oBRDI.avif 2x" type="image/avif"/><source srcset="https://blog.gavide.dev/_app/immutable/assets/tcpuart2.Cu9Ljbze.webp 1x, https://blog.gavide.dev/_app/immutable/assets/tcpuart2.B1NT9BdP.webp 2x" type="image/webp"/><source srcset="https://blog.gavide.dev/_app/immutable/assets/tcpuart2.CaQ6INC7.jpg 1x, https://blog.gavide.dev/_app/immutable/assets/tcpuart2.BAlb5hD5.jpg 2x" type="image/jpeg"/><img src="https://blog.gavide.dev/_app/immutable/assets/tcpuart2.BAlb5hD5.jpg" alt="tcpuart screenshot 2" style="width:100%; height:auto; display:block;" width="618" height="1280"/></picture> <figcaption style="font-size:0.9em; margin-top:0.4rem">After connecting the ESP32</figcaption></figure> <figure style="flex:1 1 200px; max-width:300px; margin:0;"><picture><source srcset="https://blog.gavide.dev/_app/immutable/assets/tcpuart3.BG67RKJh.avif 1x, https://blog.gavide.dev/_app/immutable/assets/tcpuart3.BSGInNja.avif 1.9967845659163987x" type="image/avif"/><source srcset="https://blog.gavide.dev/_app/immutable/assets/tcpuart3.RPBJP1aO.webp 1x, https://blog.gavide.dev/_app/immutable/assets/tcpuart3.DKOXtDBA.webp 1.9967845659163987x" type="image/webp"/><source srcset="https://blog.gavide.dev/_app/immutable/assets/tcpuart3.xyQP_Irx.jpg 1x, https://blog.gavide.dev/_app/immutable/assets/tcpuart3.BjKBOiYg.jpg 1.9967845659163987x" type="image/jpeg"/><img src="https://blog.gavide.dev/_app/immutable/assets/tcpuart3.BjKBOiYg.jpg" alt="tcpuart screenshot 3" style="width:100%; height:auto; display:block;" width="621" height="1280"/></picture> <figcaption style="font-size:0.9em; margin-top:0.4rem">After starting the local TCP server</figcaption></figure></div> <figcaption>TCPUART app screenshots</figcaption></figure> <h2 id="tcpuart-setup"><a href="#tcpuart-setup" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>TCPUART Setup</h2> <ul><li>Set Baud Rate to <code>115200</code></li> <li>Press the <code>Connect</code> button</li> <li>A prompt should appear (see the second screenshot). Click <code>OK</code></li> <li>Between <code>client</code> and <code>server</code>, choose <code>server</code></li> <li>Use <code>8080</code> as the port</li> <li>Click the <code>Start</code> button</li></ul> <h2 id="termux-setup"><a href="#termux-setup" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>Termux setup</h2> <p>Make sure you have the following termux packages installed. Run this command:</p> <pre class="language-shell"><code class="language-shell">pkg <span class="token function">install</span> <span class="token parameter variable">-y</span> python esptool mpremote socat</code></pre> <p>We will then setup a TCP bridge virtual device file:</p> <pre class="language-shell"><code class="language-shell">socat pty,link<span class="token operator">=</span><span class="token environment constant">$HOME</span>/esp32,raw,echo<span class="token operator">=</span><span class="token number">0</span> tcp:127.0.0.1:8080 <span class="token operator">&amp;</span></code></pre> <p>If it was executed successfully, the command should not print any output and <code>socat</code> will run in background. A file named <code>esp32</code> will be created in the Termux home folder.</p> <h2 id="resetting-the-esp32"><a href="#resetting-the-esp32" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>Resetting the ESP32</h2> <p>We need to reset the <code>ESP32</code> memory, so we need to reboot it into <strong>download mode</strong>.</p> <figure><picture><source srcset="https://blog.gavide.dev/_app/immutable/assets/ESP32-pinout-diagram.jv8q-ixJ.avif 1x, https://blog.gavide.dev/_app/immutable/assets/ESP32-pinout-diagram.CmAWNf4T.avif 2x" type="image/avif"/><source srcset="https://blog.gavide.dev/_app/immutable/assets/ESP32-pinout-diagram.DNsYGDye.webp 1x, https://blog.gavide.dev/_app/immutable/assets/ESP32-pinout-diagram.DpiCj4tj.webp 2x" type="image/webp"/><source srcset="https://blog.gavide.dev/_app/immutable/assets/ESP32-pinout-diagram.DeJN7J9r.jpg 1x, https://blog.gavide.dev/_app/immutable/assets/ESP32-pinout-diagram.JbPUU7p4.jpg 2x" type="image/jpeg"/><img src="https://blog.gavide.dev/_app/immutable/assets/ESP32-pinout-diagram.JbPUU7p4.jpg" alt="ESP32 WROOM 32 Pinout diagram" style="width:100%; max-width:700px; height:auto; display:block;" width="1360" height="979"/></picture> <figcaption>ESP32 WROOM 32 pinout diagram, from https://www.teachmemicro.com/wp-content/uploads/2023/12/ESP32-pinout-diagram.jpg</figcaption></figure> <ul><li>Hold the physical <code>BOOT</code> button on the board. The one on the bottom right in this image.</li> <li>Press and release the <code>EN</code>/<code>ENABLE</code>/<code>RST</code>/<code>RESET</code> button (basically the other button)</li> <li>Release the <code>BOOT</code> button</li> <li>The device is now in download mode</li></ul> <p>To reset the <code>ESP32</code>, run this command on Termux:</p> <pre class="language-shell"><code class="language-shell">esptool <span class="token parameter variable">--chip</span> esp32 <span class="token parameter variable">--port</span> <span class="token environment constant">$HOME</span>/esp32 <span class="token parameter variable">--before</span> no-reset <span class="token parameter variable">--after</span> no-reset erase-flash</code></pre> <figure><picture><source srcset="https://blog.gavide.dev/_app/immutable/assets/erase.CXzZZJR8.avif 1x, https://blog.gavide.dev/_app/immutable/assets/erase.awVrYVhW.avif 1.99800796812749x" type="image/avif"/><source srcset="https://blog.gavide.dev/_app/immutable/assets/erase.BTT1ZE-B.webp 1x, https://blog.gavide.dev/_app/immutable/assets/erase.DqUkTtNz.webp 1.99800796812749x" type="image/webp"/><source srcset="https://blog.gavide.dev/_app/immutable/assets/erase.CsCzsHH3.jpg 1x, https://blog.gavide.dev/_app/immutable/assets/erase.v7TxFc-L.jpg 1.99800796812749x" type="image/jpeg"/><img src="https://blog.gavide.dev/_app/immutable/assets/erase.v7TxFc-L.jpg" alt="Screenshot of Termux showing the socat and erase commands" style="width:100%; max-width:500px; height:auto; display:block;" width="1003" height="1280"/></picture></figure> <h2 id="flashing-the-micropython-firmware"><a href="#flashing-the-micropython-firmware" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>Flashing the Micropython firmware</h2> <p>We now need to flash <a href="https://micropython.org/" rel="nofollow">Micropython</a> on the <code>ESP32</code>.</p> <p>The firmware link is obtained from <a href="https://micropython.org/download/ESP32_GENERIC/" rel="nofollow">https://micropython.org/download/ESP32_GENERIC/</a>.</p> <p>Run these commands on Termux to download and flash the firmware. Remember to <strong>go into Download mode before running the second command</strong>:</p> <pre class="language-shell"><code class="language-shell"><span class="token function">curl</span> <span class="token parameter variable">-L</span> https://micropython.org/resources/firmware/ESP32_GENERIC-20250911-v1.26.1.bin <span class="token parameter variable">-o</span> esp32-micropython.bin

esptool <span class="token parameter variable">--chip</span> esp32 <span class="token parameter variable">--port</span> <span class="token environment constant">$HOME</span>/esp32 <span class="token parameter variable">--before</span> no-reset <span class="token parameter variable">--after</span> no-reset write-flash <span class="token parameter variable">-z</span> 0x1000 esp32-micropython.bin</code></pre> <blockquote><p>❗️ <strong>IMPORTANT</strong></p> <p>After the flash is complete, press and release the <code>ENABLE</code>/<code>RESET</code> button in the board to exit download mode.</p></blockquote> <figure><picture><source srcset="https://blog.gavide.dev/_app/immutable/assets/flash.CLrUbUpO.avif 1x, https://blog.gavide.dev/_app/immutable/assets/flash.Lt3NyaUU.avif 2x" type="image/avif"/><source srcset="https://blog.gavide.dev/_app/immutable/assets/flash.DqcpzArx.webp 1x, https://blog.gavide.dev/_app/immutable/assets/flash.Cjjmzjy6.webp 2x" type="image/webp"/><source srcset="https://blog.gavide.dev/_app/immutable/assets/flash.D4sZPbzi.jpg 1x, https://blog.gavide.dev/_app/immutable/assets/flash.DjBmRpmL.jpg 2x" type="image/jpeg"/><img src="https://blog.gavide.dev/_app/immutable/assets/flash.DjBmRpmL.jpg" alt="Screenshot of Termux showing the flash command" style="width:100%; max-width:500px; height:auto; display:block;" width="1080" height="2269"/></picture></figure> <h4 id="-success"><a href="#-success" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>🎉 Success</h4> <p>Congratulations, Micropython should now be flashed in your board.</p> <h2 id="next-steps"><a href="#next-steps" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>Next steps</h2> <p>If you want to try the Micropython REPL, run this command:</p> <pre class="language-shell"><code class="language-shell">mpremote connect port:<span class="token environment constant">$HOME</span>/esp32 repl</code></pre> <p>By the way, there is also <code>minicom</code> if you want to interact with the <code>REPL</code>:</p> <pre class="language-shell"><code class="language-shell">minicom <span class="token parameter variable">-D</span> <span class="token environment constant">$HOME</span>/esp32 <span class="token parameter variable">-b</span> <span class="token number">115200</span>  <span class="token comment"># Quit using Ctrl-A Q</span></code></pre> <p>If you want to upload a program that will run on the ESP32 boot, without the need for it to be connected to your phone:</p> <ul><li>Create a file named <code>program.py</code> with <code>nano</code> (or any other editor) and put it in your <code>$HOME</code> directory</li> <li>Inside it, write the code you want. The code I will be using is:</li></ul> <pre class="language-python"><code class="language-python"><span class="token keyword">import</span> machine
<span class="token keyword">import</span> time

<span class="token comment"># Built-in LED on most ESP32 boards (GPIO 2)</span>
led <span class="token operator">=</span> machine<span class="token punctuation">.</span>Pin<span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> machine<span class="token punctuation">.</span>Pin<span class="token punctuation">.</span>OUT<span class="token punctuation">)</span>

<span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"Starting LED blink..."</span><span class="token punctuation">)</span>
<span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"Press Ctrl+C to stop"</span><span class="token punctuation">)</span>

<span class="token keyword">try</span><span class="token punctuation">:</span>
    <span class="token keyword">while</span> <span class="token boolean">True</span><span class="token punctuation">:</span>
        led<span class="token punctuation">.</span>on<span class="token punctuation">(</span><span class="token punctuation">)</span>
        <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"LED ON"</span><span class="token punctuation">)</span>
        time<span class="token punctuation">.</span>sleep<span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span>
        led<span class="token punctuation">.</span>off<span class="token punctuation">(</span><span class="token punctuation">)</span>
        <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"LED OFF"</span><span class="token punctuation">)</span>
        time<span class="token punctuation">.</span>sleep<span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span>
<span class="token keyword">except</span> KeyboardInterrupt<span class="token punctuation">:</span>
    led<span class="token punctuation">.</span>off<span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token keyword">print</span><span class="token punctuation">(</span><span class="token string">"Stopped"</span><span class="token punctuation">)</span></code></pre> <p>It will blink the builtin LED on the board every second, and will output the logs in the UART serial connection.</p> <ul><li>Uploading the code:</li></ul> <pre class="language-shell"><code class="language-shell">mpremote connect port:<span class="token environment constant">$HOME</span>/esp32 <span class="token function">cp</span> <span class="token environment constant">$HOME</span>/program.py :main.py</code></pre> <ul><li>To run it immediately:</li></ul> <pre class="language-shell"><code class="language-shell">mpremote connect port:<span class="token environment constant">$HOME</span>/esp32 run <span class="token environment constant">$HOME</span>/program.py</code></pre> <figure><picture><source srcset="https://blog.gavide.dev/_app/immutable/assets/run.BPDKQiH-.avif 1x, https://blog.gavide.dev/_app/immutable/assets/run.CcJpj0eq.avif 1.998003992015968x" type="image/avif"/><source srcset="https://blog.gavide.dev/_app/immutable/assets/run.B2-o9MH2.webp 1x, https://blog.gavide.dev/_app/immutable/assets/run.CIdHI6zN.webp 1.998003992015968x" type="image/webp"/><source srcset="https://blog.gavide.dev/_app/immutable/assets/run.CF7m_YEZ.jpg 1x, https://blog.gavide.dev/_app/immutable/assets/run.7lD1axBb.jpg 1.998003992015968x" type="image/jpeg"/><img src="https://blog.gavide.dev/_app/immutable/assets/run.7lD1axBb.jpg" alt="Screenshot of Termux showing the micropython REPL" style="width:100%; max-width:500px; height:auto; display:block;" width="1001" height="1280"/></picture></figure> <h3 id="useful-mpremote-commands"><a href="#useful-mpremote-commands" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>Useful <code>mpremote</code> commands</h3> <h4 id="list-files"><a href="#list-files" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>List files</h4> <pre class="language-shell"><code class="language-shell">mpremote connect port:<span class="token environment constant">$HOME</span>/esp32 fs <span class="token function">ls</span></code></pre> <h4 id="view-a-file"><a href="#view-a-file" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>View a file</h4> <pre class="language-shell"><code class="language-shell">mpremote connect port:<span class="token environment constant">$HOME</span>/esp32 fs <span class="token function">cat</span> main.py</code></pre> <h4 id="delete-a-file"><a href="#delete-a-file" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>Delete a file</h4> <pre class="language-shell"><code class="language-shell">mpremote connect port:<span class="token environment constant">$HOME</span>/esp32 fs <span class="token function">rm</span> unwanted.py</code></pre> <h4 id="interactive-repl"><a href="#interactive-repl" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>Interactive REPL</h4> <pre class="language-shell"><code class="language-shell">mpremote connect port:<span class="token environment constant">$HOME</span>/esp32 repl</code></pre> <h2 id="conclusion"><a href="#conclusion" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>Conclusion</h2> <p>Termux is linked against <a href="https://android.googlesource.com/platform/bionic/" rel="nofollow"><code>Bionic Libc</code></a>, and in my phone specifically it runs on <code>aarch64</code>, so many prebuilt binaries will not work. This means that I could not compile firmware binaries from scratch, as I could not setup a toolchain for it.</p> <p>What I tried that either did not work or I gave up on trying:</p> <ul><li>Running <code>PlatformIO</code>: the <code>xtensa-esp32-elf-g++</code> binary would not execute, as it is compiled for another architecture</li> <li>An Ubuntu proot with <code>PlatformIO</code></li> <li>Using <a href="https://github.com/espressif/esp-idf" rel="nofollow"><code>esp-idf</code></a></li> <li>Rust’s <code>espflash</code>, <code>espup</code>, <code>esp-rs</code></li> <li>To connect to the <code>UART</code> serial: <code>termux-usb</code> and <code>Termux: API</code>. It would disconnect often and get a new device identifier each time, requiring to accept the permission each time. It was not a very practical solution, and I did not even get to making the <code>UART</code> communicate.</li></ul> <p>I believe that there exists a better solution than using a third party app to use the <code>UART</code> serial connection. However, I was not able to make it work.</p><p><a href="https://blog.gavide.dev/blog/esp32-and-termux">Read on Gavide's Blog</a></p></article>]]></content:encoded>
            <media:content url="https://blog.gavide.dev/_app/immutable/assets/thumbnail.DXPcb2Gg.jpg" medium="image"/>
        </item>
        <item>
            <title><![CDATA[JavaScript Obfuscation Through File Stream Side-Channel]]></title>
            <description><![CDATA[Obfuscating JavaScript code in webpages using file stream side-channel techniques.]]></description>
            <link>https://blog.gavide.dev/blog/file-stream-side-channel</link>
            <guid isPermaLink="false">https://blog.gavide.dev/blog/file-stream-side-channel</guid>
            <category><![CDATA[blog]]></category>
            <category><![CDATA[security]]></category>
            <category><![CDATA[web]]></category>
            <category><![CDATA[obfuscation]]></category>
            <category><![CDATA[fun]]></category>
            <dc:creator><![CDATA[Davide Galilei]]></dc:creator>
            <pubDate>Mon, 05 May 2025 00:00:00 GMT</pubDate>
            <enclosure url="https://blog.gavide.dev/_app/immutable/assets/demo.DN610C62.mp4" length="0" type="video/mp4"/>
            <content:encoded><![CDATA[<article><header><h1>JavaScript Obfuscation Through File Stream Side-Channel</h1><p><time datetime="2025-05-05T00:00:00.000Z">Mon, 05 May 2025 00:00:00 GMT</time></p></header><p>I was recently reverse engineering a website that attempts to block devtools and obfuscates its API through HTML/CSS/JS blobs.
Suddenly, a random thought crossed my mind: “If one goes as far as making it that obnoxious to reverse engineer a website, why not stepping it up a notch and obfuscating the JavaScript code in the webpage using file stream side-channel techniques?“.</p> <h2 id="the-idea"><a href="#the-idea" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>The Idea</h2> <p>Browsers load files as a stream from the server, in fact, it is possible to programmatically read the stream in real-time from JavaScript code.
It is possible to encode JavaScript code (or anything, for the matter) to binary code (e.g. <code>10101110</code>), and translate each <code>0</code> to a short delay, and each <code>1</code> to a long delay, similarly to morse code.</p> <p>By being able to read a stream of bytes in real time, such as the stream of a <code>style.css</code> file being loaded, it is also possible to measure those delays and decode them back to binary code, then decoding this binary code back to JS Code and execute it.</p> <h2 id="demo"><a href="#demo" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>Demo</h2> <video autoplay="" loop="" muted="" playsinline="" controls=""><source src="https://blog.gavide.dev/_app/immutable/assets/demo.DN610C62.mp4" type="video/mp4"/> Your browser does not support the video tag.</video> <blockquote><p>You can find the code for this demo <a href="https://github.com/DavideGalilei/articles/tree/master/003-file-stream-side-channel" rel="nofollow">here</a>.</p></blockquote> <h2 id="what-is-going-on"><a href="#what-is-going-on" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>What is going on?</h2> <p>To have a better understanding of how this works, try to play around with the widget below. It doesn’t actually send requests to the server, but simulates locally the stream of a file being loaded.</p> <div id="demo" class="svelte-1pb3ed5"><div class="controls svelte-1pb3ed5"><button class="svelte-1pb3ed5">Start Demo</button> <button disabled="" class="svelte-1pb3ed5">Reset</button></div> <div class="timing-settings svelte-1pb3ed5"><label class="svelte-1pb3ed5">Short Delay (ms): <input type="number" value="70" class="svelte-1pb3ed5"/></label> <label class="svelte-1pb3ed5">Long Delay (ms): <input type="number" value="140" class="svelte-1pb3ed5"/></label> <label class="svelte-1pb3ed5">Detection Threshold (ms): <input type="number" value="110" class="svelte-1pb3ed5"/></label></div> <div class="container svelte-1pb3ed5"><div class="top-section svelte-1pb3ed5"><div class="left svelte-1pb3ed5"><h3 class="svelte-1pb3ed5">CSS File Loading</h3> <div class="file-container svelte-1pb3ed5"><pre class="css-file svelte-1pb3ed5"><code></code></pre> <div class="loading-indicator svelte-1pb3ed5">Loading...</div></div></div> <div class="right svelte-1pb3ed5"><h3 class="svelte-1pb3ed5">Inferred Binary</h3> <pre class="binary svelte-1pb3ed5"><code></code></pre> <h3 class="svelte-1pb3ed5">Decoded Result</h3> <pre class="decoded svelte-1pb3ed5"><code></code></pre></div></div> <div class="graph-section svelte-1pb3ed5"><h3 class="svelte-1pb3ed5">Measured Delays Graph</h3> <div class="graph-container svelte-1pb3ed5"><canvas width="600" height="200" class="svelte-1pb3ed5"></canvas></div></div></div></div> <h2 id="potential-improvements"><a href="#potential-improvements" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>Potential Improvements</h2> <ul><li>Custom headers to hide the stream from tools such as BurpSuite</li> <li>Load images or binary files instead of <code>style.css</code>, as to have a more realistic stream</li> <li>Compress the data to reduce the delay of the stream</li> <li>Multiple streams for faster decoding, e.g. multiple images being loaded in background</li> <li>Checksum to verify the stream integrity, or error correction codes</li> <li>Use encryption</li></ul> <h2 id="potential-issues"><a href="#potential-issues" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>Potential Issues</h2> <p>Security through obscurity is never a good idea, it only makes it harder to reverse engineer, not impossible. If your browser can read the stream, so can a reverse engineer with a bit of patience.</p> <p>Reverse proxies and real world conditions may affect the timing of the stream, making this technique unreliable or impractical in some cases.</p><p><a href="https://blog.gavide.dev/blog/file-stream-side-channel">Read on Gavide's Blog</a></p></article>]]></content:encoded>
            <media:content url="https://blog.gavide.dev/_app/immutable/assets/demo.DN610C62.mp4" medium="video"/>
        </item>
        <item>
            <title><![CDATA[Python Generic Return Types]]></title>
            <description><![CDATA[How to implement generic return types in Python, for RPCs and other use cases]]></description>
            <link>https://blog.gavide.dev/blog/python-generic-return-types</link>
            <guid isPermaLink="false">https://blog.gavide.dev/blog/python-generic-return-types</guid>
            <category><![CDATA[blog]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[rust]]></category>
            <category><![CDATA[types]]></category>
            <dc:creator><![CDATA[Davide Galilei]]></dc:creator>
            <pubDate>Mon, 14 Apr 2025 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<article><header><h1>Python Generic Return Types</h1><p><time datetime="2025-04-14T00:00:00.000Z">Mon, 14 Apr 2025 00:00:00 GMT</time></p></header><h2 id="motivation"><a href="#motivation" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>Motivation</h2> <p>While working with the now-deprecated <a href="https://core.telegram.org/mtproto" rel="nofollow">MTProto</a> library <a href="https://github.com/pyrogram/pyrogram" rel="nofollow">Pyrogram</a>, I encountered a situation where I had to manually type hint the RPC calls’ result types.</p> <pre class="language-python"><code class="language-python"><span class="token keyword">from</span> pyrogram <span class="token keyword">import</span> Client
<span class="token keyword">from</span> pyrogram<span class="token punctuation">.</span>raw<span class="token punctuation">.</span>types <span class="token keyword">import</span> DialogFilter
<span class="token keyword">from</span> pyrogram<span class="token punctuation">.</span>raw<span class="token punctuation">.</span>functions<span class="token punctuation">.</span>messages <span class="token keyword">import</span> GetDialogFilters

app <span class="token operator">=</span> Client<span class="token punctuation">(</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
folders<span class="token punctuation">:</span> <span class="token builtin">list</span><span class="token punctuation">[</span>DialogFilter<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token keyword">await</span> app<span class="token punctuation">.</span>invoke<span class="token punctuation">(</span>GetDialogFilters<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span></code></pre> <p>It wasn’t a big deal and I didn’t think much of it, I just accepted it as something I had to do to improve the quality of my code and make Mypy errors disappear.</p> <p>That is, until I switched to the <a href="https://github.com/Lonami/grammers/" rel="nofollow">gramme.rs</a> Rust library. I noticed that rust-analyzer could automatically detect the RPC return type without me specifying it, so I dug deeper.</p> <p>By looking at the source code of the <a href="https://docs.rs/grammers-tl-types/0.7.0/grammers_tl_types/functions/messages/struct.GetDialogFilters.html#associatedtype.Return" rel="nofollow">library on docs.rs</a> (<a href="https://docs.rs/grammers-tl-types/0.7.0/src/grammers_tl_types/opt/rustwide/target/x86_64-unknown-linux-gnu/debug/build/grammers-tl-types-660ac82ac4208fb4/out/generated.rs.html#64151" rel="nofollow">code permalink</a>. warning: huge webpage), I could find this snippet of code:</p> <pre class="language-rust"><code class="language-rust"><span class="token attribute attr-name">#[derive(Debug)]</span>
<span class="token attribute attr-name">#[derive(Clone, PartialEq)]</span>
<span class="token keyword">pub</span> <span class="token keyword">struct</span> <span class="token type-definition class-name">GetDialogFilters</span> <span class="token punctuation">&#123;</span>
<span class="token punctuation">&#125;</span>
<span class="token keyword">impl</span> <span class="token keyword">crate</span><span class="token punctuation">::</span><span class="token class-name">Identifiable</span> <span class="token keyword">for</span> <span class="token class-name">GetDialogFilters</span> <span class="token punctuation">&#123;</span>
    <span class="token keyword">const</span> <span class="token constant">CONSTRUCTOR_ID</span><span class="token punctuation">:</span> <span class="token keyword">u32</span> <span class="token operator">=</span> <span class="token number">4023684233</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span>
<span class="token keyword">impl</span> <span class="token keyword">crate</span><span class="token punctuation">::</span><span class="token class-name">Serializable</span> <span class="token keyword">for</span> <span class="token class-name">GetDialogFilters</span> <span class="token punctuation">&#123;</span>
    <span class="token keyword">fn</span> <span class="token function-definition function">serialize</span><span class="token punctuation">(</span><span class="token operator">&amp;</span><span class="token keyword">self</span><span class="token punctuation">,</span> buf<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token keyword">mut</span> <span class="token keyword">impl</span> <span class="token class-name">Extend</span><span class="token operator">&lt;</span><span class="token keyword">u8</span><span class="token operator">></span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token keyword">use</span> <span class="token keyword">crate</span><span class="token punctuation">::</span><span class="token class-name">Identifiable</span><span class="token punctuation">;</span>
        <span class="token keyword">Self</span><span class="token punctuation">::</span><span class="token constant">CONSTRUCTOR_ID</span><span class="token punctuation">.</span><span class="token function">serialize</span><span class="token punctuation">(</span>buf<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span>
<span class="token keyword">impl</span> <span class="token keyword">crate</span><span class="token punctuation">::</span><span class="token class-name">RemoteCall</span> <span class="token keyword">for</span> <span class="token class-name">GetDialogFilters</span> <span class="token punctuation">&#123;</span>
    <span class="token keyword">type</span> <span class="token type-definition class-name">Return</span> <span class="token operator">=</span> <span class="token keyword">crate</span><span class="token module-declaration namespace"><span class="token punctuation">::</span>enums<span class="token punctuation">::</span>messages<span class="token punctuation">::</span></span><span class="token class-name">DialogFilters</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span></code></pre> <p>That’s clever! It’s a statically defined return type bound to the RPC call struct. Let’s replicate this pattern in Python.</p> <blockquote><p>Note: the Rust code uses a <code>DialogFilters</code> type instead of a <code>list[DialogFilter]</code>, my snippet comes from an older version of the library. The newer version of the protocol uses a <code>DialogFilters</code> type, which is a list of <code>DialogFilter</code> objects with additional metadata. The <code>DialogFilter</code> type is a simple object with a few attributes, so it is not relevant to the purpose of this post.</p></blockquote> <h2 id="minimal-implementation"><a href="#minimal-implementation" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>Minimal Implementation</h2> <pre class="language-python"><code class="language-python"><span class="token keyword">from</span> typing <span class="token keyword">import</span> Any<span class="token punctuation">,</span> Generic<span class="token punctuation">,</span> Protocol<span class="token punctuation">,</span> TypeVar

R <span class="token operator">=</span> TypeVar<span class="token punctuation">(</span><span class="token string">"R"</span><span class="token punctuation">)</span>

<span class="token keyword">class</span> <span class="token class-name">HasResult</span><span class="token punctuation">(</span>Protocol<span class="token punctuation">,</span> Generic<span class="token punctuation">[</span>R<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
    result<span class="token punctuation">:</span> R

T <span class="token operator">=</span> TypeVar<span class="token punctuation">(</span><span class="token string">"T"</span><span class="token punctuation">,</span> bound<span class="token operator">=</span>HasResult<span class="token punctuation">[</span>Any<span class="token punctuation">]</span><span class="token punctuation">)</span>

<span class="token keyword">def</span> <span class="token function">process</span><span class="token punctuation">(</span>obj<span class="token punctuation">:</span> HasResult<span class="token punctuation">[</span>R<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> R<span class="token punctuation">:</span>
    <span class="token keyword">return</span> obj<span class="token punctuation">.</span>result

<span class="token keyword">class</span> <span class="token class-name">ExampleResult</span><span class="token punctuation">:</span>
    <span class="token keyword">def</span> <span class="token function">__init__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> value<span class="token punctuation">:</span> <span class="token builtin">int</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
        self<span class="token punctuation">.</span>result <span class="token operator">=</span> value

example <span class="token operator">=</span> ExampleResult<span class="token punctuation">(</span><span class="token number">42</span><span class="token punctuation">)</span>
result <span class="token operator">=</span> process<span class="token punctuation">(</span>example<span class="token punctuation">)</span></code></pre> <p>This is a minimal snippet I came up with. As you can see, the linter can correctly detect the type statically.</p> <figure><picture><source srcset="https://blog.gavide.dev/_app/immutable/assets/screen1.y-5AdAAY.avif 1x, https://blog.gavide.dev/_app/immutable/assets/screen1.ChBy8lCR.avif 2x" type="image/avif"/><source srcset="https://blog.gavide.dev/_app/immutable/assets/screen1.BKmdSH-S.webp 1x, https://blog.gavide.dev/_app/immutable/assets/screen1.tpTuI3B1.webp 2x" type="image/webp"/><source srcset="https://blog.gavide.dev/_app/immutable/assets/screen1.BJVG_-9a.jpg 1x, https://blog.gavide.dev/_app/immutable/assets/screen1.DH9d5j2f.jpg 2x" type="image/jpeg"/><img src="https://blog.gavide.dev/_app/immutable/assets/screen1.DH9d5j2f.jpg" alt="VSCode screenshot of the first snippet" style="width:100%; max-width:700px; height:auto; display:block;" width="330" height="97"/></picture></figure> <p>While this snippet is fine for most use cases, I wanted to replicate the design pattern more accurately.</p> <h2 id="advanced-implementation"><a href="#advanced-implementation" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>Advanced Implementation</h2> <pre class="language-python"><code class="language-python"><span class="token keyword">from</span> dataclasses <span class="token keyword">import</span> dataclass
<span class="token keyword">from</span> typing <span class="token keyword">import</span> Any<span class="token punctuation">,</span> Generic<span class="token punctuation">,</span> Protocol<span class="token punctuation">,</span> TypeVar<span class="token punctuation">,</span> Type


D <span class="token operator">=</span> TypeVar<span class="token punctuation">(</span><span class="token string">"D"</span><span class="token punctuation">,</span> covariant<span class="token operator">=</span><span class="token boolean">True</span><span class="token punctuation">)</span>
R <span class="token operator">=</span> TypeVar<span class="token punctuation">(</span><span class="token string">"R"</span><span class="token punctuation">,</span> bound<span class="token operator">=</span><span class="token string">"HasDefault[Any]"</span><span class="token punctuation">)</span>


<span class="token keyword">class</span> <span class="token class-name">HasDefault</span><span class="token punctuation">(</span>Protocol<span class="token punctuation">,</span> Generic<span class="token punctuation">[</span>D<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token decorator annotation punctuation">@classmethod</span>
    <span class="token keyword">def</span> <span class="token function">default</span><span class="token punctuation">(</span>cls<span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> D<span class="token punctuation">:</span>
        <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>


<span class="token keyword">class</span> <span class="token class-name">HasResult</span><span class="token punctuation">(</span>Protocol<span class="token punctuation">,</span> Generic<span class="token punctuation">[</span>R<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
    result_type<span class="token punctuation">:</span> Type<span class="token punctuation">[</span>R<span class="token punctuation">]</span>


<span class="token decorator annotation punctuation">@dataclass</span>
<span class="token keyword">class</span> <span class="token class-name">SendMessageAnswer</span><span class="token punctuation">:</span>
    message_id<span class="token punctuation">:</span> <span class="token builtin">int</span>

    <span class="token decorator annotation punctuation">@classmethod</span>
    <span class="token keyword">def</span> <span class="token function">default</span><span class="token punctuation">(</span>cls<span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token string">"SendMessageAnswer"</span><span class="token punctuation">:</span>
        <span class="token keyword">return</span> cls<span class="token punctuation">(</span>message_id<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">)</span>


<span class="token keyword">class</span> <span class="token class-name">SendMessage</span><span class="token punctuation">:</span>
    result_type <span class="token operator">=</span> SendMessageAnswer

    <span class="token keyword">def</span> <span class="token function">__init__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> <span class="token operator">*</span><span class="token punctuation">,</span> message_text<span class="token punctuation">:</span> <span class="token builtin">str</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
        self<span class="token punctuation">.</span>message_text <span class="token operator">=</span> message_text


<span class="token keyword">def</span> <span class="token function">process_rpc</span><span class="token punctuation">(</span>obj<span class="token punctuation">:</span> HasResult<span class="token punctuation">[</span>R<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> R<span class="token punctuation">:</span>
    <span class="token keyword">return</span> obj<span class="token punctuation">.</span>result_type<span class="token punctuation">.</span>default<span class="token punctuation">(</span><span class="token punctuation">)</span>


request <span class="token operator">=</span> SendMessage<span class="token punctuation">(</span>message_text<span class="token operator">=</span><span class="token string">"Hello, world!"</span><span class="token punctuation">)</span>
result <span class="token operator">=</span> process_rpc<span class="token punctuation">(</span>request<span class="token punctuation">)</span>
<span class="token keyword">print</span><span class="token punctuation">(</span>result<span class="token punctuation">)</span></code></pre> <p>Notable changes:</p> <ul><li><p><code>result_type</code> is now a <code>Type[R]</code> to prevent situations like this:</p> <pre class="language-python"><code class="language-python"><span class="token comment"># Wrong</span>
<span class="token keyword">class</span> <span class="token class-name">SendMessage</span><span class="token punctuation">:</span>
    result_type<span class="token punctuation">:</span> SendMessageAnswer

<span class="token comment"># Correct (notice the &#96;=&#96; sign instead of &#96;:&#96;)</span>
<span class="token keyword">class</span> <span class="token class-name">SendMessage</span><span class="token punctuation">:</span>
    result_type <span class="token operator">=</span> SendMessageAnswer</code></pre></li> <li><p>Added a <code>.default()</code> method interface for the sake of demonstration, but in real code this could be a <code>.deserialize(stream)</code> method that takes a binary data stream (<a href="https://core.telegram.org/mtproto" rel="nofollow">MTProto</a> is a binary protocol) and deserializes it into the expected object.</p></li></ul> <figure><picture><source srcset="https://blog.gavide.dev/_app/immutable/assets/screen2.DUbw6AL0.avif 1x, https://blog.gavide.dev/_app/immutable/assets/screen2.Dbj6bvCu.avif 2x" type="image/avif"/><source srcset="https://blog.gavide.dev/_app/immutable/assets/screen2.D78_fteQ.webp 1x, https://blog.gavide.dev/_app/immutable/assets/screen2.DfMJNoyz.webp 2x" type="image/webp"/><source srcset="https://blog.gavide.dev/_app/immutable/assets/screen2.CgSzyzcs.jpg 1x, https://blog.gavide.dev/_app/immutable/assets/screen2.ChTRYDA9.jpg 2x" type="image/jpeg"/><img src="https://blog.gavide.dev/_app/immutable/assets/screen2.ChTRYDA9.jpg" alt="VSCode screenshot of the second snippet" style="width:100%; max-width:700px; height:auto; display:block;" width="528" height="193"/></picture></figure> <h2 id="conclusion"><a href="#conclusion" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>Conclusion</h2> <p>This is a simple way to implement generic return types in Python. It is not as powerful as Rust’s type system, but it is a good compromise for Python’s dynamic nature. For more information on the <code>Protocol</code> class, check out the <a href="https://typing.python.org/en/latest/spec/protocol.html" rel="nofollow">python documentation</a>.</p> <p>I have a feeling that there could be a better way to implement this, but I haven’t investigated it yet. I will update this post accordingly if I will manage to find a better solution.</p><p><a href="https://blog.gavide.dev/blog/python-generic-return-types">Read on Gavide's Blog</a></p></article>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Understanding and Preventing Race Conditions in Web Applications]]></title>
            <description><![CDATA[Spot and fix race conditions in Python, FastAPI, and PostgreSQL to prevent data corruption and security issues]]></description>
            <link>https://blog.gavide.dev/blog/prevent-race-conditions-in-your-api</link>
            <guid isPermaLink="false">https://blog.gavide.dev/blog/prevent-race-conditions-in-your-api</guid>
            <category><![CDATA[blog]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[security]]></category>
            <category><![CDATA[fastapi]]></category>
            <category><![CDATA[databases]]></category>
            <dc:creator><![CDATA[Davide Galilei]]></dc:creator>
            <pubDate>Mon, 16 Sep 2024 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<article><header><h1>Understanding and Preventing Race Conditions in Web Applications</h1><p><time datetime="2024-09-16T00:00:00.000Z">Mon, 16 Sep 2024 00:00:00 GMT</time></p></header><p>This article aims to correct a subtle but dangerous bad practice that is often overlooked. I will demonstrate practical examples using <strong>Python</strong>, <strong>FastAPI</strong> and <strong>PostgreSQL</strong>, and I will provide various solutions for the problem, each with its own trade-offs. I will role-play as a junior freelance developer, making it easier to understand the chain of thought.</p> <p>The following code is <strong>unsafe</strong>: can you spot why?</p> <pre class="language-python"><code class="language-python"><span class="token keyword">class</span> <span class="token class-name">BuyItemRequest</span><span class="token punctuation">(</span>BaseModel<span class="token punctuation">)</span><span class="token punctuation">:</span>
    item_id<span class="token punctuation">:</span> <span class="token builtin">int</span>

<span class="token decorator annotation punctuation">@app<span class="token punctuation">.</span>post</span><span class="token punctuation">(</span><span class="token string">"/buy_item"</span><span class="token punctuation">)</span>
<span class="token keyword">async</span> <span class="token keyword">def</span> <span class="token function">buy_item</span><span class="token punctuation">(</span>
    request<span class="token punctuation">:</span> BuyItemRequest<span class="token punctuation">,</span>
    user_id<span class="token punctuation">:</span> <span class="token builtin">int</span> <span class="token operator">=</span> Depends<span class="token punctuation">(</span>get_user_from_jwt<span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span><span class="token punctuation">:</span>
    item <span class="token operator">=</span> <span class="token keyword">await</span> Item<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token builtin">id</span><span class="token operator">=</span>request<span class="token punctuation">.</span>item_id<span class="token punctuation">)</span>
    user <span class="token operator">=</span> <span class="token keyword">await</span> User<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token builtin">id</span><span class="token operator">=</span>user_id<span class="token punctuation">)</span>

    <span class="token keyword">if</span> user<span class="token punctuation">.</span>money <span class="token operator">&lt;</span> item<span class="token punctuation">.</span>cost<span class="token punctuation">:</span>
        <span class="token keyword">return</span> <span class="token punctuation">&#123;</span><span class="token string">"error"</span><span class="token punctuation">:</span> <span class="token string">"Not enough money to buy item"</span><span class="token punctuation">&#125;</span>

    user<span class="token punctuation">.</span>money <span class="token operator">-=</span> item<span class="token punctuation">.</span>cost
    <span class="token keyword">await</span> user<span class="token punctuation">.</span>save<span class="token punctuation">(</span>update_fields<span class="token operator">=</span><span class="token punctuation">[</span><span class="token string">"money"</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
    <span class="token comment"># successfully bought. TODO: add product in the database</span>
    <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>

    <span class="token keyword">return</span> <span class="token punctuation">&#123;</span><span class="token string">"bought"</span><span class="token punctuation">:</span> bought<span class="token punctuation">&#125;</span></code></pre> <h2 id="what-is-a-race-condition-anyway"><a href="#what-is-a-race-condition-anyway" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>What is a race condition, anyway?</h2> <p>Read here: <a href="https://en.wikipedia.org/wiki/Race_condition" rel="nofollow">https://en.wikipedia.org/wiki/Race_condition</a></p> <p>A race condition occurs when two or more operations compete to access shared resources in an unpredictable order, potentially leading to unexpected and erroneous results. It’s like multiple runners racing towards a finish line, where the outcome depends on who gets there first.</p> <p>The main takeaway is that race conditions can cause data corruption, crashes, or <strong><mark>security vulnerabilities</mark></strong>.</p> <p>As a matter of fact, there have been more than 174 vulnerabilities reported in 2024 alone, with some of them being of <a href="https://nvd.nist.gov/vuln/search/results?form_type=Basic&amp;results_type=overview&amp;query=race+condition&amp;search_type=all" rel="nofollow"><strong>high severity</strong></a> (source: <a href="https://nvd.nist.gov/vuln/search/statistics?form_type=Basic&amp;results_type=statistics&amp;query=race+condition&amp;search_type=all&amp;isCpeNameSearch=false" rel="nofollow">NIST</a>)</p> <p><picture><source srcset="https://blog.gavide.dev/_app/immutable/assets/race_conditions_statistics_source_NIST.CX_TbOKp.avif 1x, https://blog.gavide.dev/_app/immutable/assets/race_conditions_statistics_source_NIST.B_SY1GJM.avif 1.9979123173277662x" type="image/avif"/><source srcset="https://blog.gavide.dev/_app/immutable/assets/race_conditions_statistics_source_NIST.BOsZAIXQ.webp 1x, https://blog.gavide.dev/_app/immutable/assets/race_conditions_statistics_source_NIST.C4awRtW6.webp 1.9979123173277662x" type="image/webp"/><source srcset="https://blog.gavide.dev/_app/immutable/assets/race_conditions_statistics_source_NIST.CDYymoge.png 1x, https://blog.gavide.dev/_app/immutable/assets/race_conditions_statistics_source_NIST.DWpE4uwY.png 1.9979123173277662x" type="image/png"/><img src="https://blog.gavide.dev/_app/immutable/assets/race_conditions_statistics_source_NIST.DWpE4uwY.png" alt="Race conditions statistics graph. Source: NIST" width="957" height="699"/></picture></p> <h2 id="first-case-blog-views"><a href="#first-case-blog-views" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>First case: blog views</h2> <p>A small company asked me to create a custom CMS website for their blog. They also want to know how many times an article has been viewed in total.</p> <blockquote><p>NOTE:
I am omitting the boilerplate parts of the code for the sake of demonstration. The full code is available on my GitHub and it’s linked in the end.</p></blockquote> <hr/> <p>Let’s start by defining our database model. I am using <code>tortoise-orm</code> as my ORM of choice. It will automatically generate the SQL for us. The code will be written in <code>worker.py</code>.</p> <pre class="language-python"><code class="language-python"><span class="token keyword">class</span> <span class="token class-name">Post</span><span class="token punctuation">(</span>Model<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token builtin">id</span> <span class="token operator">=</span> fields<span class="token punctuation">.</span>IntField<span class="token punctuation">(</span>pk<span class="token operator">=</span><span class="token boolean">True</span><span class="token punctuation">)</span>
    title <span class="token operator">=</span> fields<span class="token punctuation">.</span>CharField<span class="token punctuation">(</span>max_length<span class="token operator">=</span><span class="token number">255</span><span class="token punctuation">)</span>
    content <span class="token operator">=</span> fields<span class="token punctuation">.</span>TextField<span class="token punctuation">(</span>max_length<span class="token operator">=</span><span class="token number">4096</span><span class="token punctuation">)</span>
    views <span class="token operator">=</span> fields<span class="token punctuation">.</span>BigIntField<span class="token punctuation">(</span>default<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">)</span>

    <span class="token keyword">class</span> <span class="token class-name">Meta</span><span class="token punctuation">:</span>
        table <span class="token operator">=</span> <span class="token string">"posts"</span></code></pre> <p>Next, let’s create a simple REST API:</p> <ul><li>Create a database connection and insert a default blog post with <code>id=1</code></li> <li><code>/post/&lt;post id></code> will return the post’s content and metadata</li> <li><code>/view/&lt;post id></code> will increase the post’s views by one</li></ul> <pre class="language-python"><code class="language-python"><span class="token comment"># ... omitted boilerplate</span>

app <span class="token operator">=</span> FastAPI<span class="token punctuation">(</span>lifespan<span class="token operator">=</span>lifespan<span class="token punctuation">,</span> debug<span class="token operator">=</span><span class="token boolean">True</span><span class="token punctuation">)</span>

<span class="token decorator annotation punctuation">@app<span class="token punctuation">.</span>get</span><span class="token punctuation">(</span><span class="token string">"/post/&#123;post_id&#125;"</span><span class="token punctuation">)</span>
<span class="token keyword">async</span> <span class="token keyword">def</span> <span class="token function">get_post</span><span class="token punctuation">(</span>post_id<span class="token punctuation">:</span> <span class="token builtin">int</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
    post <span class="token operator">=</span> <span class="token keyword">await</span> Post<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token builtin">id</span><span class="token operator">=</span>post_id<span class="token punctuation">)</span>
    <span class="token keyword">return</span> <span class="token punctuation">&#123;</span>
        <span class="token string">"post_id"</span><span class="token punctuation">:</span> post<span class="token punctuation">.</span><span class="token builtin">id</span><span class="token punctuation">,</span>
        <span class="token string">"title"</span><span class="token punctuation">:</span> post<span class="token punctuation">.</span>title<span class="token punctuation">,</span>
        <span class="token string">"content"</span><span class="token punctuation">:</span> post<span class="token punctuation">.</span>content<span class="token punctuation">,</span>
        <span class="token string">"views"</span><span class="token punctuation">:</span> post<span class="token punctuation">.</span>views<span class="token punctuation">,</span>
    <span class="token punctuation">&#125;</span>

<span class="token decorator annotation punctuation">@app<span class="token punctuation">.</span>post</span><span class="token punctuation">(</span><span class="token string">"/view/&#123;post_id&#125;"</span><span class="token punctuation">)</span>
<span class="token keyword">async</span> <span class="token keyword">def</span> <span class="token function">view_post</span><span class="token punctuation">(</span>post_id<span class="token punctuation">:</span> <span class="token builtin">int</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token comment"># UNSAFE!! Do not use this in production</span>
    post <span class="token operator">=</span> <span class="token keyword">await</span> Post<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token builtin">id</span><span class="token operator">=</span>post_id<span class="token punctuation">)</span>
    post<span class="token punctuation">.</span>views <span class="token operator">+=</span> <span class="token number">1</span>
    <span class="token keyword">await</span> post<span class="token punctuation">.</span>save<span class="token punctuation">(</span>update_fields<span class="token operator">=</span><span class="token punctuation">[</span><span class="token string">"views"</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
    <span class="token keyword">return</span> <span class="token punctuation">&#123;</span><span class="token string">"current_views"</span><span class="token punctuation">:</span> post<span class="token punctuation">.</span>views<span class="token punctuation">&#125;</span></code></pre> <details><summary>Generated SQL Queries</summary> <pre class="language-sql"><code class="language-sql"><span class="token keyword">SELECT</span> <span class="token string">"content"</span><span class="token punctuation">,</span> <span class="token string">"title"</span><span class="token punctuation">,</span> <span class="token string">"id"</span><span class="token punctuation">,</span> <span class="token string">"views"</span>
<span class="token keyword">FROM</span> <span class="token string">"posts"</span>
<span class="token keyword">WHERE</span> <span class="token string">"id"</span> <span class="token operator">=</span> <span class="token number">1</span>
<span class="token keyword">LIMIT</span> <span class="token number">2</span><span class="token punctuation">;</span>

<span class="token keyword">UPDATE</span> <span class="token string">"posts"</span>
<span class="token keyword">SET</span> <span class="token string">"views"</span> <span class="token operator">=</span> <span class="token number">1</span>
<span class="token keyword">WHERE</span> <span class="token string">"id"</span> <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span></code></pre> <p><b>Note:</b> <code>SET "views" = 1</code> is just an example here, assuming the post had <code>0</code> views before this request.</p> <p><b>Note:</b> the <code>LIMIT 2</code> is generated by the ORM to check against multiple results in a <code>.get()</code> operation expecting only a single element.</p></details> <p>So far so good. Let’s test our code!</p> <pre class="language-console"><code class="language-console">$ <span class="token function">curl</span> <span class="token parameter variable">-X</span> GET <span class="token string">'http://127.0.0.1:8000/post/1'</span>
<span class="token punctuation">&#123;</span><span class="token string">"post_id"</span>:1,<span class="token string">"title"</span><span class="token builtin class-name">:</span><span class="token string">"Example blog post"</span>,<span class="token string">"content"</span><span class="token builtin class-name">:</span><span class="token string">"Hello! This is a blog post"</span>,<span class="token string">"views"</span>:0<span class="token punctuation">&#125;</span></code></pre> <p>Nice, the post has 0 views. Let’s try to increase them and fetch the post again.</p> <pre class="language-console"><code class="language-console">$ <span class="token function">curl</span> <span class="token parameter variable">-X</span> POST <span class="token string">'http://127.0.0.1:8000/view/1'</span>
<span class="token punctuation">&#123;</span><span class="token string">"current_views"</span>:1<span class="token punctuation">&#125;</span>
$ <span class="token function">curl</span> <span class="token parameter variable">-X</span> POST <span class="token string">'http://127.0.0.1:8000/view/1'</span>
<span class="token punctuation">&#123;</span><span class="token string">"current_views"</span>:2<span class="token punctuation">&#125;</span>
$ <span class="token function">curl</span> <span class="token parameter variable">-X</span> POST <span class="token string">'http://127.0.0.1:8000/view/1'</span>
<span class="token punctuation">&#123;</span><span class="token string">"current_views"</span>:3<span class="token punctuation">&#125;</span>

$ <span class="token function">curl</span> <span class="token parameter variable">-X</span> GET <span class="token string">'http://127.0.0.1:8000/post/1'</span>
<span class="token punctuation">&#123;</span><span class="token string">"post_id"</span>:1,<span class="token string">"title"</span><span class="token builtin class-name">:</span><span class="token string">"Example blog post"</span>,<span class="token string">"content"</span><span class="token builtin class-name">:</span><span class="token string">"Hello! This is a blog post"</span>,<span class="token string">"views"</span>:3<span class="token punctuation">&#125;</span></code></pre> <p>Great! It seems to be working perfectly, doesn’t it? The view count increases with each request, and we can retrieve the updated post with the correct number of views. Everything appears to be in order… right?</p> <p>…right?</p> <p>I was happy, yet another side gig has been completed, and I delivered the project in time. Let’s call it a day, I told myself.</p> <p>Until, the following day the company that asked me to make the website called. They said that despite millions of visitors after the initial release, the views counter is reporting a few tens of thousands at most.</p> <p>As my enthusiasm crumbles down, I try to think of a possible cause. So I begin troubleshooting. I remembered that the company told me that the page has been swarmed with visitors from the very moment it came online. This means that a lot of requests were made at the same time. So I try to test this theory by myself, by creating a script.</p> <pre class="language-python"><code class="language-python">In <span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">:</span> <span class="token keyword">import</span> asyncio

In <span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">:</span> <span class="token keyword">import</span> httpx

In <span class="token punctuation">[</span><span class="token number">3</span><span class="token punctuation">]</span><span class="token punctuation">:</span> <span class="token keyword">async</span> <span class="token keyword">def</span> <span class="token function">get_views</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
   <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">:</span>     <span class="token keyword">async</span> <span class="token keyword">with</span> httpx<span class="token punctuation">.</span>AsyncClient<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">as</span> client<span class="token punctuation">:</span>
   <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">:</span>         r <span class="token operator">=</span> <span class="token keyword">await</span> client<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">"http://127.0.0.1:8000/post/1"</span><span class="token punctuation">)</span>
   <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">:</span>         <span class="token keyword">return</span> r<span class="token punctuation">.</span>json<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token string">"views"</span><span class="token punctuation">]</span>
   <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">:</span> 

In <span class="token punctuation">[</span><span class="token number">4</span><span class="token punctuation">]</span><span class="token punctuation">:</span> <span class="token keyword">async</span> <span class="token keyword">def</span> <span class="token function">view_post</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
   <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">:</span>     <span class="token keyword">async</span> <span class="token keyword">with</span> httpx<span class="token punctuation">.</span>AsyncClient<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">as</span> client<span class="token punctuation">:</span>
   <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">:</span>         <span class="token keyword">await</span> client<span class="token punctuation">.</span>post<span class="token punctuation">(</span><span class="token string">"http://127.0.0.1:8000/view/1"</span><span class="token punctuation">)</span>
   <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">:</span> 

In <span class="token punctuation">[</span><span class="token number">5</span><span class="token punctuation">]</span><span class="token punctuation">:</span> <span class="token keyword">await</span> get_views<span class="token punctuation">(</span><span class="token punctuation">)</span>
Out<span class="token punctuation">[</span><span class="token number">5</span><span class="token punctuation">]</span><span class="token punctuation">:</span> <span class="token number">8</span>

In <span class="token punctuation">[</span><span class="token number">6</span><span class="token punctuation">]</span><span class="token punctuation">:</span> _ <span class="token operator">=</span> <span class="token keyword">await</span> asyncio<span class="token punctuation">.</span>gather<span class="token punctuation">(</span><span class="token operator">*</span><span class="token punctuation">[</span>view_post<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">for</span> i <span class="token keyword">in</span> <span class="token builtin">range</span><span class="token punctuation">(</span><span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">)</span>

In <span class="token punctuation">[</span><span class="token number">7</span><span class="token punctuation">]</span><span class="token punctuation">:</span> <span class="token keyword">await</span> get_views<span class="token punctuation">(</span><span class="token punctuation">)</span>
Out<span class="token punctuation">[</span><span class="token number">7</span><span class="token punctuation">]</span><span class="token punctuation">:</span> <span class="token number">10</span></code></pre> <p>The script is supposed to send 100 concurrent requests to the API at once. I fetched the views before and after making 100 requests, and I noticed that the difference is just 2, instead of 100. Bingo!</p> <p>As I search for a solution online I stumble on an old answer from a forum, telling me to “lock the record row-wise with <code>SELECT FOR UPDATE</code>”. It makes sense, I remember learning locks in my classes, so I assume databases must have a similar built-in function.</p> <p>Without wasting a second, I update my code:</p> <pre class="language-python"><code class="language-python"><span class="token decorator annotation punctuation">@app<span class="token punctuation">.</span>post</span><span class="token punctuation">(</span><span class="token string">"/view/&#123;post_id&#125;"</span><span class="token punctuation">)</span>
<span class="token keyword">async</span> <span class="token keyword">def</span> <span class="token function">view_post</span><span class="token punctuation">(</span>post_id<span class="token punctuation">:</span> <span class="token builtin">int</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token comment"># UNSAFE!! Do not use this in production</span>
    post <span class="token operator">=</span> <span class="token keyword">await</span> Post<span class="token punctuation">.</span><span class="token builtin">filter</span><span class="token punctuation">(</span><span class="token builtin">id</span><span class="token operator">=</span>post_id<span class="token punctuation">)</span><span class="token punctuation">.</span>select_for_update<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token punctuation">)</span>
    post<span class="token punctuation">.</span>views <span class="token operator">+=</span> <span class="token number">1</span>
    <span class="token keyword">await</span> post<span class="token punctuation">.</span>save<span class="token punctuation">(</span>update_fields<span class="token operator">=</span><span class="token punctuation">[</span><span class="token string">"views"</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
    <span class="token keyword">return</span> <span class="token punctuation">&#123;</span><span class="token string">"current_views"</span><span class="token punctuation">:</span> post<span class="token punctuation">.</span>views<span class="token punctuation">&#125;</span></code></pre> <details><summary>Generated SQL Queries</summary> <pre class="language-sql"><code class="language-sql"><span class="token keyword">SELECT</span> <span class="token string">"views"</span><span class="token punctuation">,</span> <span class="token string">"title"</span><span class="token punctuation">,</span> <span class="token string">"id"</span><span class="token punctuation">,</span> <span class="token string">"content"</span>
<span class="token keyword">FROM</span> <span class="token string">"posts"</span>
<span class="token keyword">WHERE</span> <span class="token string">"id"</span> <span class="token operator">=</span> <span class="token number">1</span>
<span class="token keyword">LIMIT</span> <span class="token number">2</span>
<span class="token keyword">FOR</span> <span class="token keyword">UPDATE</span><span class="token punctuation">;</span>

<span class="token keyword">UPDATE</span> <span class="token string">"posts"</span>
<span class="token keyword">SET</span> <span class="token string">"views"</span> <span class="token operator">=</span> <span class="token number">2</span>
<span class="token keyword">WHERE</span> <span class="token string">"id"</span> <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span></code></pre></details> <p>And test it again:</p> <pre class="language-python"><code class="language-python">In <span class="token punctuation">[</span><span class="token number">10</span><span class="token punctuation">]</span><span class="token punctuation">:</span> <span class="token keyword">await</span> get_views<span class="token punctuation">(</span><span class="token punctuation">)</span>
Out<span class="token punctuation">[</span><span class="token number">10</span><span class="token punctuation">]</span><span class="token punctuation">:</span> <span class="token number">1</span>

In <span class="token punctuation">[</span><span class="token number">11</span><span class="token punctuation">]</span><span class="token punctuation">:</span> _ <span class="token operator">=</span> <span class="token keyword">await</span> asyncio<span class="token punctuation">.</span>gather<span class="token punctuation">(</span><span class="token operator">*</span><span class="token punctuation">[</span>view_post<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">for</span> i <span class="token keyword">in</span> <span class="token builtin">range</span><span class="token punctuation">(</span><span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">)</span>

In <span class="token punctuation">[</span><span class="token number">12</span><span class="token punctuation">]</span><span class="token punctuation">:</span> <span class="token keyword">await</span> get_views<span class="token punctuation">(</span><span class="token punctuation">)</span>
Out<span class="token punctuation">[</span><span class="token number">12</span><span class="token punctuation">]</span><span class="token punctuation">:</span> <span class="token number">12</span></code></pre> <p>Wait, what? Even locking the row doesn’t work… Or does it? After some troubleshooting, I realized that something was amiss.</p> <p>After asking for help to a friend, he tells me I am missing a transaction.</p> <pre class="language-python"><code class="language-python"><span class="token decorator annotation punctuation">@app<span class="token punctuation">.</span>post</span><span class="token punctuation">(</span><span class="token string">"/view/&#123;post_id&#125;"</span><span class="token punctuation">)</span>
<span class="token keyword">async</span> <span class="token keyword">def</span> <span class="token function">view_post</span><span class="token punctuation">(</span>post_id<span class="token punctuation">:</span> <span class="token builtin">int</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token keyword">async</span> <span class="token keyword">with</span> in_transaction<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
        post <span class="token operator">=</span> <span class="token keyword">await</span> Post<span class="token punctuation">.</span><span class="token builtin">filter</span><span class="token punctuation">(</span><span class="token builtin">id</span><span class="token operator">=</span>post_id<span class="token punctuation">)</span><span class="token punctuation">.</span>select_for_update<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token punctuation">)</span>
        post<span class="token punctuation">.</span>views <span class="token operator">+=</span> <span class="token number">1</span>
        <span class="token keyword">await</span> post<span class="token punctuation">.</span>save<span class="token punctuation">(</span>update_fields<span class="token operator">=</span><span class="token punctuation">[</span><span class="token string">"views"</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
        <span class="token keyword">return</span> <span class="token punctuation">&#123;</span><span class="token string">"current_views"</span><span class="token punctuation">:</span> post<span class="token punctuation">.</span>views<span class="token punctuation">&#125;</span></code></pre> <details><summary>Generated SQL Queries</summary> <pre class="language-sql"><code class="language-sql"><span class="token keyword">BEGIN</span>
<span class="token keyword">SELECT</span> <span class="token string">"views"</span><span class="token punctuation">,</span> <span class="token string">"id"</span><span class="token punctuation">,</span> <span class="token string">"content"</span><span class="token punctuation">,</span> <span class="token string">"title"</span>
<span class="token keyword">FROM</span> <span class="token string">"posts"</span>
<span class="token keyword">WHERE</span> <span class="token string">"id"</span> <span class="token operator">=</span> <span class="token number">1</span>
<span class="token keyword">LIMIT</span> <span class="token number">2</span>
<span class="token keyword">FOR</span> <span class="token keyword">UPDATE</span><span class="token punctuation">;</span>

<span class="token keyword">UPDATE</span> <span class="token string">"posts"</span>
<span class="token keyword">SET</span> <span class="token string">"views"</span> <span class="token operator">=</span> <span class="token number">1</span>
<span class="token keyword">WHERE</span> <span class="token string">"id"</span> <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>

<span class="token keyword">COMMIT</span></code></pre></details> <p>Code updated, testing begins again.</p> <pre class="language-python"><code class="language-python">In <span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">:</span> <span class="token keyword">await</span> get_views<span class="token punctuation">(</span><span class="token punctuation">)</span>
Out<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">:</span> <span class="token number">0</span>  <span class="token comment"># yes, I had to reset my local database</span>

In <span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">:</span> _ <span class="token operator">=</span> <span class="token keyword">await</span> asyncio<span class="token punctuation">.</span>gather<span class="token punctuation">(</span><span class="token operator">*</span><span class="token punctuation">[</span>view_post<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">for</span> i <span class="token keyword">in</span> <span class="token builtin">range</span><span class="token punctuation">(</span><span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">)</span>

In <span class="token punctuation">[</span><span class="token number">3</span><span class="token punctuation">]</span><span class="token punctuation">:</span> <span class="token keyword">await</span> get_views<span class="token punctuation">(</span><span class="token punctuation">)</span>
Out<span class="token punctuation">[</span><span class="token number">3</span><span class="token punctuation">]</span><span class="token punctuation">:</span> <span class="token number">100</span>

In <span class="token punctuation">[</span><span class="token number">4</span><span class="token punctuation">]</span><span class="token punctuation">:</span> _ <span class="token operator">=</span> <span class="token keyword">await</span> asyncio<span class="token punctuation">.</span>gather<span class="token punctuation">(</span><span class="token operator">*</span><span class="token punctuation">[</span>view_post<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">for</span> i <span class="token keyword">in</span> <span class="token builtin">range</span><span class="token punctuation">(</span><span class="token number">1000</span><span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">)</span>

In <span class="token punctuation">[</span><span class="token number">5</span><span class="token punctuation">]</span><span class="token punctuation">:</span> <span class="token keyword">await</span> get_views<span class="token punctuation">(</span><span class="token punctuation">)</span>
Out<span class="token punctuation">[</span><span class="token number">5</span><span class="token punctuation">]</span><span class="token punctuation">:</span> <span class="token number">1100</span></code></pre> <blockquote><p>NOTE:
This works because the two queries are executed inside the same transaction. When selecting <code>FOR UPDATE</code>, the row is locked for the whole duration of the transaction. Earlier, <code>tortoise-orm</code> was probably ending a transaction instantly after selecting the row, therefore unlocking it.</p></blockquote> <p>Hurrah! It works. I even tried to simulate 1000 concurrent requests. I shipped the code to production without hesitating, and I notify the company, which is happy to hear I fixed the issue.</p> <p>Or have I?</p> <p>Another day, another call. “What is it, now?”, I think to myself before answering. They told me that the site is being very slow, the request for updating the view counter is hanging for most users.</p> <p>I make an attempt at figuring out what’s wrong. Every time the <code>/view/&lt;post id></code> endpoint is called, the row is being locked until the transaction ends. Analogously, it’s as if all the requests towards that endpoint get queued one by one. Computers are fast, but locking and unlocking a mutex is a very expensive operation.</p> <blockquote><p>TIP:
Namely, the following line of code is waiting for the row to get unlocked before being able to select it. This is the cause of the delays. Concurrency is impaired because of this very bottleneck.</p></blockquote> <pre class="language-python"><code class="language-python">    <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
    <span class="token keyword">async</span> <span class="token keyword">with</span> in_transaction<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
        post <span class="token operator">=</span> <span class="token keyword">await</span> Post<span class="token punctuation">.</span><span class="token builtin">filter</span><span class="token punctuation">(</span><span class="token builtin">id</span><span class="token operator">=</span>post_id<span class="token punctuation">)</span><span class="token punctuation">.</span>select_for_update<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token punctuation">)</span></code></pre> <p>Once again, I try to look for a solution.</p> <p>After a simple search of “ways to avoid select for update incrementing counter”, this <a href="https://stackoverflow.com/a/24408531/13673785" rel="nofollow">StackOverflow answer</a> comes up.</p> <p>The correct SQL Query should be atomic, as in it should update the value atomically. Simply put, it means that adding one actually adds one to the value, in practice.</p> <blockquote><p>TIP:
The <code>UPDATE</code> statement is atomic by nature. It performs the read and write operations in a single, indivisible step. This eliminates the need for an explicit <code>SELECT ... FOR UPDATE</code> in application code. This approach scales much better under high concurrency, as it doesn’t keep the lock open across a separate application-side read-modify-write cycle.</p></blockquote> <pre class="language-sql"><code class="language-sql"><span class="token keyword">UPDATE</span> posts
<span class="token keyword">SET</span> views <span class="token operator">=</span> views <span class="token operator">+</span> <span class="token number">1</span>
<span class="token keyword">WHERE</span> id <span class="token operator">=</span> $<span class="token number">1</span>
<span class="token keyword">RETURNING</span> views<span class="token punctuation">;</span>
<span class="token comment">-- $1 is a placeholder for the actual post_id value</span></code></pre> <p>The aforementioned logic, can then be translated into Python code as following:</p> <pre class="language-python"><code class="language-python"><span class="token decorator annotation punctuation">@app<span class="token punctuation">.</span>post</span><span class="token punctuation">(</span><span class="token string">"/view/&#123;post_id&#125;"</span><span class="token punctuation">)</span>
<span class="token keyword">async</span> <span class="token keyword">def</span> <span class="token function">view_post</span><span class="token punctuation">(</span>post_id<span class="token punctuation">:</span> <span class="token builtin">int</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token keyword">await</span> Post<span class="token punctuation">.</span><span class="token builtin">filter</span><span class="token punctuation">(</span><span class="token builtin">id</span><span class="token operator">=</span>post_id<span class="token punctuation">)</span><span class="token punctuation">.</span>update<span class="token punctuation">(</span>views<span class="token operator">=</span>F<span class="token punctuation">(</span><span class="token string">"views"</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span>
    post <span class="token operator">=</span> <span class="token keyword">await</span> Post<span class="token punctuation">.</span><span class="token builtin">filter</span><span class="token punctuation">(</span><span class="token builtin">id</span><span class="token operator">=</span>post_id<span class="token punctuation">)</span><span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token keyword">return</span> <span class="token punctuation">&#123;</span><span class="token string">"current_views"</span><span class="token punctuation">:</span> post<span class="token punctuation">.</span>views<span class="token punctuation">&#125;</span></code></pre> <blockquote><p>NOTE:
Note: in tortoise-orm there is currently no way to add a <code>RETURNING</code> statement, this is why I am fetching the value twice. This is of course highly inefficient, but it’s alright for demonstrative purposes. Ironically, there is a race condition with this very approach, where the updated value after the <code>UPDATE</code> statement might not match with the one obtained later with <code>.get()</code>. In this case, it’s not really a problem, as it would return the most recently updated <code>views</code> value anyway.</p></blockquote> <blockquote><p>WARNING:
Oh, and by the way, the code doesn’t account for invalid post ids. Be careful.</p></blockquote> <h2 id="second-case-a-game-shop"><a href="#second-case-a-game-shop" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>Second case: a game shop</h2> <p>Let’s step up the game (pun intended) by adding another constraint to our riddle.</p> <p>Assume that you are working on a game. It is possible to buy level upgrades, only if the player has enough coins. This means that the balance must never reach below zero.</p> <p>Let’s define our model:</p> <pre class="language-python"><code class="language-python"><span class="token keyword">class</span> <span class="token class-name">Player</span><span class="token punctuation">(</span>Model<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token builtin">id</span> <span class="token operator">=</span> fields<span class="token punctuation">.</span>IntField<span class="token punctuation">(</span>pk<span class="token operator">=</span><span class="token boolean">True</span><span class="token punctuation">)</span>
    name <span class="token operator">=</span> fields<span class="token punctuation">.</span>CharField<span class="token punctuation">(</span>max_length<span class="token operator">=</span><span class="token number">255</span><span class="token punctuation">)</span>
    money <span class="token operator">=</span> fields<span class="token punctuation">.</span>IntField<span class="token punctuation">(</span>default<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">)</span>
    level <span class="token operator">=</span> fields<span class="token punctuation">.</span>IntField<span class="token punctuation">(</span>default<span class="token operator">=</span><span class="token number">1</span><span class="token punctuation">)</span>

    <span class="token keyword">class</span> <span class="token class-name">Meta</span><span class="token punctuation">:</span>
        table <span class="token operator">=</span> <span class="token string">"players"</span></code></pre> <p>We will also create a dummy player, Alice, on startup:</p> <pre class="language-python"><code class="language-python"><span class="token keyword">await</span> Player<span class="token punctuation">.</span>create<span class="token punctuation">(</span><span class="token builtin">id</span><span class="token operator">=</span><span class="token number">1</span><span class="token punctuation">,</span> name<span class="token operator">=</span><span class="token string">"Alice"</span><span class="token punctuation">,</span> money<span class="token operator">=</span><span class="token number">1000</span><span class="token punctuation">,</span> level<span class="token operator">=</span><span class="token number">1</span><span class="token punctuation">)</span></code></pre> <p>And after learning our previous lessons, we write the following endpoint:</p> <pre class="language-python"><code class="language-python">COST <span class="token operator">=</span> <span class="token number">150</span>

<span class="token decorator annotation punctuation">@app<span class="token punctuation">.</span>post</span><span class="token punctuation">(</span><span class="token string">"/upgrade/&#123;player_id&#125;"</span><span class="token punctuation">)</span>
<span class="token keyword">async</span> <span class="token keyword">def</span> <span class="token function">upgrade_level</span><span class="token punctuation">(</span>player_id<span class="token punctuation">:</span> <span class="token builtin">int</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token comment"># Slow, but safe</span>
    <span class="token comment"># row level locking</span>
    <span class="token keyword">async</span> <span class="token keyword">with</span> in_transaction<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">as</span> conn<span class="token punctuation">:</span>
        player <span class="token operator">=</span> <span class="token keyword">await</span> Player<span class="token punctuation">.</span><span class="token builtin">filter</span><span class="token punctuation">(</span><span class="token builtin">id</span><span class="token operator">=</span>player_id<span class="token punctuation">)</span><span class="token punctuation">.</span>select_for_update<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token punctuation">)</span>

        <span class="token keyword">if</span> player<span class="token punctuation">.</span>money <span class="token operator">&lt;</span> COST<span class="token punctuation">:</span>
            <span class="token keyword">return</span> <span class="token punctuation">&#123;</span><span class="token string">"error"</span><span class="token punctuation">:</span> <span class="token string">"Not enough money"</span><span class="token punctuation">&#125;</span>

        <span class="token keyword">await</span> Player<span class="token punctuation">.</span><span class="token builtin">filter</span><span class="token punctuation">(</span><span class="token builtin">id</span><span class="token operator">=</span>player<span class="token punctuation">.</span><span class="token builtin">id</span><span class="token punctuation">)</span><span class="token punctuation">.</span>update<span class="token punctuation">(</span>
            level<span class="token operator">=</span>F<span class="token punctuation">(</span><span class="token string">"level"</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">,</span>
            money<span class="token operator">=</span>F<span class="token punctuation">(</span><span class="token string">"money"</span><span class="token punctuation">)</span> <span class="token operator">-</span> COST<span class="token punctuation">,</span>
        <span class="token punctuation">)</span>
        <span class="token keyword">await</span> player<span class="token punctuation">.</span>refresh_from_db<span class="token punctuation">(</span><span class="token punctuation">)</span>

        <span class="token keyword">return</span> <span class="token punctuation">&#123;</span><span class="token string">"user_id"</span><span class="token punctuation">:</span> player<span class="token punctuation">.</span><span class="token builtin">id</span><span class="token punctuation">,</span> <span class="token string">"money"</span><span class="token punctuation">:</span> player<span class="token punctuation">.</span>money<span class="token punctuation">,</span> <span class="token string">"level"</span><span class="token punctuation">:</span> player<span class="token punctuation">.</span>level<span class="token punctuation">&#125;</span></code></pre> <p>Although slow, it is completely free from race conditions. But what if we wanted the best of both worlds: safe AND fast?</p> <p>Sure enough, there are multiple ways.</p> <ol><li><p>We could write the following atomic <code>UPDATE</code> query:</p> <pre class="language-sql"><code class="language-sql"><span class="token keyword">UPDATE</span> players p
<span class="token keyword">SET</span>
    money <span class="token operator">=</span> money <span class="token operator">-</span> $<span class="token number">1</span><span class="token punctuation">,</span>
    <span class="token keyword">level</span> <span class="token operator">=</span> <span class="token keyword">level</span> <span class="token operator">+</span> <span class="token number">1</span>
<span class="token keyword">WHERE</span>
    p<span class="token punctuation">.</span>id <span class="token operator">=</span> $<span class="token number">2</span>
    <span class="token operator">AND</span> p<span class="token punctuation">.</span>money <span class="token operator">>=</span> $<span class="token number">1</span>
<span class="token keyword">RETURNING</span> p<span class="token punctuation">;</span>

<span class="token comment">-- $1 is replaced with the COST variable and $2 with the player_id</span></code></pre> <p>It would translate to the following Python code:</p> <pre class="language-python"><code class="language-python"><span class="token decorator annotation punctuation">@app<span class="token punctuation">.</span>post</span><span class="token punctuation">(</span><span class="token string">"/upgrade/&#123;player_id&#125;"</span><span class="token punctuation">)</span>
<span class="token keyword">async</span> <span class="token keyword">def</span> <span class="token function">upgrade_level</span><span class="token punctuation">(</span>player_id<span class="token punctuation">:</span> <span class="token builtin">int</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token comment"># Fast and safe</span>

    rows_updated <span class="token operator">=</span> <span class="token keyword">await</span> Player<span class="token punctuation">.</span><span class="token builtin">filter</span><span class="token punctuation">(</span>
        <span class="token builtin">id</span><span class="token operator">=</span>player_id<span class="token punctuation">,</span>
        money__gte<span class="token operator">=</span>COST<span class="token punctuation">,</span>  <span class="token comment"># gte = greater than or equal</span>
    <span class="token punctuation">)</span><span class="token punctuation">.</span>update<span class="token punctuation">(</span>
        level<span class="token operator">=</span>F<span class="token punctuation">(</span><span class="token string">"level"</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">,</span>
        money<span class="token operator">=</span>F<span class="token punctuation">(</span><span class="token string">"money"</span><span class="token punctuation">)</span> <span class="token operator">-</span> COST<span class="token punctuation">,</span>
    <span class="token punctuation">)</span>

    <span class="token keyword">if</span> rows_updated <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">:</span>
        <span class="token keyword">return</span> <span class="token punctuation">&#123;</span><span class="token string">"error"</span><span class="token punctuation">:</span> <span class="token string">"Player not found, or not enough money"</span><span class="token punctuation">&#125;</span>

    player <span class="token operator">=</span> <span class="token keyword">await</span> Player<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token builtin">id</span><span class="token operator">=</span>player_id<span class="token punctuation">)</span>

    <span class="token keyword">return</span> <span class="token punctuation">&#123;</span><span class="token string">"user_id"</span><span class="token punctuation">:</span> player<span class="token punctuation">.</span><span class="token builtin">id</span><span class="token punctuation">,</span> <span class="token string">"money"</span><span class="token punctuation">:</span> player<span class="token punctuation">.</span>money<span class="token punctuation">,</span> <span class="token string">"level"</span><span class="token punctuation">:</span> player<span class="token punctuation">.</span>level<span class="token punctuation">&#125;</span></code></pre> <blockquote><p>IMPORTANT: <strong>Important:</strong> in your real application the endpoint must be authenticated. It is a <strong>really bad</strong> practice to allow any user to pass an arbitrary, unsanitized, id to your API.</p></blockquote></li> <li><p><strong>Database-level constraints:</strong> PostgreSQL offers the possibility to add <strong>constraints</strong> to your columns. We could write our <code>CREATE</code> statement as such:</p> <pre class="language-sql"><code class="language-sql"><span class="token keyword">CREATE</span> <span class="token keyword">TABLE</span> players <span class="token punctuation">(</span>
    id <span class="token keyword">SERIAL</span> <span class="token keyword">PRIMARY</span> <span class="token keyword">KEY</span><span class="token punctuation">,</span>
    name <span class="token keyword">TEXT</span><span class="token punctuation">,</span>
    money <span class="token keyword">INTEGER</span> <span class="token keyword">CHECK</span> <span class="token punctuation">(</span>money <span class="token operator">>=</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token keyword">level</span> <span class="token keyword">INTEGER</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> <p>Or alternatively, <code>ALTER</code> an existing table:</p> <pre class="language-sql"><code class="language-sql"><span class="token keyword">ALTER</span> <span class="token keyword">TABLE</span> players
<span class="token keyword">ADD</span> <span class="token keyword">CONSTRAINT</span> check_money_non_negative <span class="token keyword">CHECK</span> <span class="token punctuation">(</span>money <span class="token operator">>=</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> <p>This ensures that any write operation that sets the <code>money</code> field to a value lesser than 0 will fail at the database-level, throwing an error.</p></li></ol> <h2 id="further-reading"><a href="#further-reading" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>Further reading</h2> <ul><li><p>Transactions do not inherently prevent all race conditions. Do not live under the false presumption that they do, or it could result in catastrophic errors.</p> <p>I recommend reading the following answer: <a href="https://stackoverflow.com/a/26081833/13673785" rel="nofollow">https://stackoverflow.com/a/26081833/13673785</a></p></li> <li><p><strong>Transaction Isolation</strong></p> <p><a href="https://www.postgresql.org/docs/current/transaction-iso.html" rel="nofollow">https://www.postgresql.org/docs/current/transaction-iso.html</a></p></li> <li><p><strong>PostgreSQL anti-patterns: read-modify-write cycles</strong></p> <p>In-depth explanation for avoiding race conditions in PostgreSQL.
It also covers:</p> <ul><li><p><code>SERIALIZABLE</code> <strong>transactions</strong></p></li> <li><p><strong>Optimistic concurrency control</strong></p> <p><a href="https://web.archive.org/web/20240707234427/https://www.2ndquadrant.com/en/blog/postgresql-anti-patterns-read-modify-write-cycles/" rel="nofollow">https://web.archive.org/web/20240707234427/https://www.2ndquadrant.com/en/blog/postgresql-anti-patterns-read-modify-write-cycles/</a></p></li></ul></li></ul> <p>Oh, and by the way, you might find online solutions suggesting to use <code>LOCK TABLE</code>. It is a terrible idea.</p> <blockquote><p>NOTE:
Sure, a software-side Mutex could be used, but it won’t protect you when you have multiple instances of your application running. It’s a good idea to use a database-level lock, as it will work across all instances of your application, and it can be used in a distributed environment as well.</p></blockquote> <h2 id="conclusion"><a href="#conclusion" aria-hidden="true" tabindex="-1"><span class="icon icon-link"></span></a>Conclusion</h2> <p>We’ve seen how easily race conditions can sneak into our code, and how tricky they can be to squash. From simple views counters to game economies, we’ve explored practical solutions like atomic updates and database constraints.</p> <p>Always think about concurrency, even when it’s not obvious. Test rigorously, and do not assume that your database operations are safe by default.</p> <p>Remember that there’s no silver bullet and each solution has its own trade-offs, so choose wisely based on your specific needs.</p><p><a href="https://blog.gavide.dev/blog/prevent-race-conditions-in-your-api">Read on Gavide's Blog</a></p></article>]]></content:encoded>
        </item>
    </channel>
</rss>