<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Fernando Simoes - Homepage on</title><link>https://frn.sh/</link><description>Recent content in Fernando Simoes - Homepage on</description><generator>Hugo</generator><language>en-US</language><copyright>Copyright © Fernando Simões.</copyright><lastBuildDate>Fri, 03 Apr 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://frn.sh/index.xml" rel="self" type="application/rss+xml"/><item><title>What /proc sees in 19 MiB</title><link>https://frn.sh/smaps/</link><pubDate>Fri, 03 Apr 2026 00:00:00 +0000</pubDate><guid>https://frn.sh/smaps/</guid><description>A few weeks ago I profiled a Node.js server with recurring memory spikes. I found out dirty pages, allocator behavior and memory that never came back. To build a cleaner mental model, I stripped the problem down to something smaller: python3 -m http.server.
root@debian:/# ps aux | grep http.server | grep -v grep root 479226 0.0 0.1 32104 19324 pts/1 T 16:10 0:00 python3 -m http.server 19 MiB of resident memory.</description></item><item><title>Where did 400 MiB go?</title><link>https://frn.sh/pmem/</link><pubDate>Sat, 21 Mar 2026 00:00:00 +0000</pubDate><guid>https://frn.sh/pmem/</guid><description>I restarted all 60+ pods of a Node.js websocket app earlier today. Every single pod sitting at ~330 MiB of memory. Except one, which was double the rest - at 640 MiB.
This is a statefulset. When I built the cluster, I estimated each pod&amp;rsquo;s footprint: ~198 MiB base, plus ~25 MiB per websocket. With 30 websockets per pod, that&amp;rsquo;s roughly 900 MiB. I was wrong about the per-websocket cost - it&amp;rsquo;s lower than 25 MiB in practice.</description></item><item><title>Between select and disk</title><link>https://frn.sh/iops/</link><pubDate>Sun, 08 Feb 2026 00:00:00 +0000</pubDate><guid>https://frn.sh/iops/</guid><description>We had a Postgres incident this week. Heroku timeouts, multiple queries running for 30+ minutes, and the IOPS pinned at the provisioned limit.
I knew I needed a better index, but I wanted to understand what &amp;ldquo;reading from disk&amp;rdquo; actually means first. How many layers of caching sit between a SELECT and the storage device?
Three.
First: shared buffers, Postgres&amp;rsquo; own cache living in the process memory. If the page is there, we need no system call - just a memory read.</description></item><item><title>108,725 forks</title><link>https://frn.sh/tforks/</link><pubDate>Thu, 11 Dec 2025 00:00:00 +0000</pubDate><guid>https://frn.sh/tforks/</guid><description>First week at a new job. A colleague was showing me around our Grafana dashboards, just routine monitoring of the baremetal machines. One caught my eye: a machine with 32GB RAM and a top-of-the-line processor was hitting 90% CPU. A few containers running, no alerts, and nobody had reported anything.
I found a process with cmd bash startup.sh that had been running for 28 minutes.
I straced it for a few minutes:</description></item><item><title>Sigterm a D state process</title><link>https://frn.sh/sigterm/</link><pubDate>Sun, 08 Jun 2025 00:00:00 +0000</pubDate><guid>https://frn.sh/sigterm/</guid><description>Load average hit 12 on a 2 vCPU machine during a production incident. My first thought was that CPU must be the bottleneck - 12 is 6x the core count.
But it wasn&amp;rsquo;t.
Linux load average counts three things: processes running on a CPU, processes waiting in the run queue, and processes in uninterruptible sleep - D state. From the kernel source:
The global load average is an exponentially decaying average of nr_running + nr_uninterruptible.</description></item><item><title>writing to disk with O_SYNC</title><link>https://frn.sh/til/osync/</link><pubDate>Sun, 18 May 2025 00:00:00 +0000</pubDate><guid>https://frn.sh/til/osync/</guid><description>write(2) doesn&amp;rsquo;t actually write to disk imediatelly. Instead, it writes to a page cache and the OS periodically handles writes to disk. Using O_SYNC, though, write(2) returns only when it fully wrote the data to a data block. Linux exposes the actual timeframe for periodic writings:
➜ ~ cat /proc/sys/vm/dirty_writeback_centisecs 500</description></item><item><title>I bought a DEC-style terminal keyboard</title><link>https://frn.sh/tty/</link><pubDate>Sat, 26 Apr 2025 00:00:00 +0000</pubDate><guid>https://frn.sh/tty/</guid><description>My birthday is next month, so I bought myself something I&amp;rsquo;d wanted for a while: a terminal keyboard. Last year I read this article about the history of the terminal and it fascinated me. I found a DEC-style Televideo terminal keyboard from the 70s or 80s (I can&amp;rsquo;t be sure of the exact date) and took advantage of the opportunity to learn a few things.
These old terminal keyboards were sold alongside a &amp;ldquo;computer terminal,&amp;rdquo; which was essentially a dumb display and input device.</description></item><item><title>source and export diff</title><link>https://frn.sh/til/source-and-export/</link><pubDate>Sun, 05 Jan 2025 00:00:00 +0000</pubDate><guid>https://frn.sh/til/source-and-export/</guid><description>Non-interactive shells don&amp;rsquo;t load initialization files, so bash -c &amp;lsquo;declare -f&amp;rsquo; doesn&amp;rsquo;t output anything. But we can source it: bash -c &amp;lsquo;source ~/.bashrc; hello&amp;rsquo;. Or even: bash -c &amp;lsquo;hello() { echo &amp;ldquo;hi&amp;rdquo;; }; declare -f&amp;rsquo;. It&amp;rsquo;s all about memory share in shell modes: source changes only affect current shell memory. export marks variables to be passed to child processes. Subtile difference that can save us a lof of debugging time.</description></item><item><title>Builtins and source</title><link>https://frn.sh/shell-source/</link><pubDate>Wed, 22 May 2024 00:00:00 +0000</pubDate><guid>https://frn.sh/shell-source/</guid><description>Understanding how the shell manages environment variables and session state requires understanding builtins, process groups, and memory space. This post traces how these pieces connect.
Types, builtins There is a classification in the shell called builtin. Some commands are builtin:
➜ ~ type cd cd is a shell builtin ➜ ~ type echo echo is a shell builtin But some are not:
➜ ~ type mkdir mkdir is /usr/bin/mkdir When a shell runs a command, it does one of two things: execute something available in its own memory space (a builtin), or execute something external (via execve).</description></item><item><title>About</title><link>https://frn.sh/about/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://frn.sh/about/</guid><description>Hello! I&amp;rsquo;m Fernando Simões. I enjoy sharing debugging stories and other computer-kernel-database-runtime-performance explorations. Also, structured procrastination.
You can reach me at frn@frn.sh or the only social account I have.</description></item></channel></rss>