<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>Vasilios Syrakis</title>
    <subtitle>A simple blog made with Zola and Duckquill</subtitle>
    <link rel="self" type="application/atom+xml" href="https://cetanu.github.io/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://cetanu.github.io"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-07-11T00:00:00+00:00</updated>
    <id>https://cetanu.github.io/atom.xml</id>
    <entry xml:lang="en">
        <title>How to Avoid Sharding Yourself</title>
        <published>2026-07-11T00:00:00+00:00</published>
        <updated>2026-07-11T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Vasilios Syrakis
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://cetanu.github.io/blog/how-to-avoid-sharding-yourself/"/>
        <id>https://cetanu.github.io/blog/how-to-avoid-sharding-yourself/</id>
        
        <content type="html" xml:base="https://cetanu.github.io/blog/how-to-avoid-sharding-yourself/">&lt;p&gt;I watched a video named &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=rxR4nIQ0hCk&quot;&gt;The Problem Sharding a Database
Solves&lt;&#x2F;a&gt; by &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;@WebDevCody&quot;&gt;Web Dev
Cody&lt;&#x2F;a&gt;. He has quite a large number of
subscribers, his video popped up on my feed, and I enjoyed the video. Also, the
diagramming tool he’s using is cool and if anyone knows what it is - please
tell me!&lt;&#x2F;p&gt;
&lt;p&gt;However, one thing stuck out to me that I wanted to intercept in order to
provide advice on, based on my own real professional experience.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;the-part-i-m-focusing-on&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-part-i-m-focusing-on&quot; aria-label=&quot;Anchor link for: the-part-i-m-focusing-on&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
The part I’m focusing on&lt;&#x2F;h1&gt;
&lt;p&gt;It’s not the whole video, it’s just one minor part. Cody delivers on what I
think the point of his video is - to describe how sharding solves a particular
problem - but there is some detail that comes across as a suggestion for how to
implement routing a tenant to the shards and this is what I’ll be discussing.&lt;&#x2F;p&gt;
&lt;p&gt;Cody describes a naive approach where you take some kind of tenant identifier
and hash it, which gives you a deterministic way to determine which shard the
tenant lives on.&lt;&#x2F;p&gt;
&lt;p&gt;He then rightly points out that if you pick this approach, as the number and
size of tenants grows you’ve now made it harder to rebalance your shards, or
migrate tenants, because when you insert new shards, the deterministic hashing
essentially breaks.&lt;&#x2F;p&gt;
&lt;p&gt;This leads Cody to describe a possibly more elegant approach called “virtual
buckets” where instead of a hash being a 1:1 mapping between a tenant and a
shard, you take the modulus of the hash which maps a range of tenants to a
shard instead.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;multi-tenant-routing-with-an-algorithm-is-an-anti-pattern&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#multi-tenant-routing-with-an-algorithm-is-an-anti-pattern&quot; aria-label=&quot;Anchor link for: multi-tenant-routing-with-an-algorithm-is-an-anti-pattern&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
Multi-tenant Routing with an Algorithm is an Anti-Pattern&lt;&#x2F;h1&gt;
&lt;p&gt;These methods of routing tenants to shards aren’t broken, but let’s call them anti-patterns.&lt;&#x2F;p&gt;
&lt;p&gt;First, let’s talk about why hashing is an attractive option.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;It’s stateless&lt;&#x2F;li&gt;
&lt;li&gt;It’s fast&lt;&#x2F;li&gt;
&lt;li&gt;It doesn’t need a database lookup&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;But it leaves you open to &lt;strong&gt;operational pain&lt;&#x2F;strong&gt;, even with the use of virtual buckets.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;one-tenant-within-a-bucket-can-dwarf-other-tenants&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#one-tenant-within-a-bucket-can-dwarf-other-tenants&quot; aria-label=&quot;Anchor link for: one-tenant-within-a-bucket-can-dwarf-other-tenants&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
One tenant within a bucket can dwarf other tenants&lt;&#x2F;h3&gt;
&lt;p&gt;If we just assume that the Pareto principle will play out eventually, you’re
going to have one tenant which consumes 80% of a single shards capacity.&lt;&#x2F;p&gt;
&lt;p&gt;In order to rebalance them, you need to change their identifier. Now you have
to choose between accepting the noisy-neighbour problem where a huge tenant
fights for dominance with the other small tenants on the shard, or you add a
special-case to your hashing algorithm that lives on forever, and most likely
grows, needing to be maintained by operators.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;no-flexibility-for-isolation&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#no-flexibility-for-isolation&quot; aria-label=&quot;Anchor link for: no-flexibility-for-isolation&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
No flexibility for isolation&lt;&#x2F;h3&gt;
&lt;p&gt;This is semi-related to the above, essentially,  if you wanted to have
different shards, like a “quarantine” shard for misbehaving tenants, or a “free
tier&#x2F;trial” shard, or an “important enterprise customer” shard with only 1
tenant on it, you again have to special-case the algorithm.&lt;&#x2F;p&gt;
&lt;p&gt;These requirements will predictably appear as you grow and get more customers.&lt;br &#x2F;&gt;
Maybe they won’t, but we can take care of this with very little operational
friction as we’ll see later.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;rebalancing-friction-doesn-t-go-away&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#rebalancing-friction-doesn-t-go-away&quot; aria-label=&quot;Anchor link for: rebalancing-friction-doesn-t-go-away&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
Rebalancing friction doesn’t go away&lt;&#x2F;h3&gt;
&lt;p&gt;Cody does point out that even when you add new shards, you still might need to
rebalance tenants, but you can maybe do this with some background process or
lazy migration that writes to both shards at the same time.&lt;&#x2F;p&gt;
&lt;p&gt;For some businesses these might be acceptable operational events that someone
has to take care of, perhaps after-hours or during scheduled downtime.&lt;&#x2F;p&gt;
&lt;p&gt;Note: you &lt;em&gt;can&lt;&#x2F;em&gt; mitigate this particular downside with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Consistent_hashing&quot;&gt;Consistent
Hashing&lt;&#x2F;a&gt; but the others are
still unsolved.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;your-tenants-deserve-their-own-directory&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#your-tenants-deserve-their-own-directory&quot; aria-label=&quot;Anchor link for: your-tenants-deserve-their-own-directory&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
Your tenants deserve their own directory&lt;&#x2F;h1&gt;
&lt;p&gt;I know it’s attractive to use clever software to avoid adding infrastructure to
solve a problem, but I think this situation justifies the addition of a
database to act as a lookup table or directory for your tenants.&lt;&#x2F;p&gt;
&lt;p&gt;For the vast majority of businesses, a dedicated Redis or Postgres instance can
serve this purpose and take you very far.&lt;&#x2F;p&gt;
&lt;p&gt;Yes, it is a single point of failure, and if it goes down so does your entire
app probably, but do a little back of the napkin calculation on the probability
of redis (in a cluster, with high-availability), whose only job is to keep key
value pairs, becoming suddenly unavailable for any serious amount of time. It
probably won’t go under 3 nines of availability.&lt;&#x2F;p&gt;
&lt;p&gt;Externalising the tenant routing to a lookup table unlocks advantages.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;surgical-rebalancing&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#surgical-rebalancing&quot; aria-label=&quot;Anchor link for: surgical-rebalancing&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
Surgical Rebalancing&lt;&#x2F;h3&gt;
&lt;p&gt;Your rebalancing goes from a stressful situation of changing the algorithm,
propagating it to your gateways and praying, to a 5 step process that an intern
can execute without bringing the company down.&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Identify: you surface a big noisy tenant via monitoring or regular capacity planning&lt;&#x2F;li&gt;
&lt;li&gt;Provision: a new shard is created&lt;&#x2F;li&gt;
&lt;li&gt;Sync: background job to copy data, stream new writes in the meantime&lt;&#x2F;li&gt;
&lt;li&gt;Switch: single atomic database query to point at the new shard&lt;&#x2F;li&gt;
&lt;li&gt;Cleanup: wait a week and delete the old copy&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h3 id=&quot;better-isolation&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#better-isolation&quot; aria-label=&quot;Anchor link for: better-isolation&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
Better Isolation&lt;&#x2F;h3&gt;
&lt;p&gt;Before, changing the way hashing works to satisfy the needs of a particular
tenant or tenants meant that you were changing the algorithm for &lt;em&gt;everyone&lt;&#x2F;em&gt;.
This represented a huge blast radius.&lt;&#x2F;p&gt;
&lt;p&gt;With a lookup table, you’re changing one record.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;fives-nines-of-availability-no-i-said-nine-fives&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#fives-nines-of-availability-no-i-said-nine-fives&quot; aria-label=&quot;Anchor link for: fives-nines-of-availability-no-i-said-nine-fives&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
Fives nines of availability? No, I said nine fives…&lt;&#x2F;h1&gt;
&lt;p&gt;If you’ve somehow reached the point where that lookup table is no longer
sufficient (and you’re still reading this blog? lol), or you just feel
uncomfortable with having to make a lookup on every request to a single point
of failure, there’s a pattern you can employ to scale things further and bring
the probability of an outage to a number approximating zero.&lt;&#x2F;p&gt;
&lt;p&gt;You can actually
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.atlassian.com&#x2F;blog&#x2F;atlassian-engineering&#x2F;aws-scaling-multi-region-low-latency-service&quot;&gt;read&lt;&#x2F;a&gt;
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.atlassian.com&#x2F;blog&#x2F;atlassian-engineering&#x2F;atlassian-critical-services-above-six-nines-of-availability&quot;&gt;about&lt;&#x2F;a&gt;
how Atlassian solved this exact problem using the
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Command_Query_Responsibility_Segregation&quot;&gt;CQRS pattern&lt;&#x2F;a&gt;.
However, I’m going to explain it here even more concisely.&lt;&#x2F;p&gt;
&lt;p&gt;CQRS, or “Command Query Responsibility Segregation”, can enable a caching
pattern where distributed nodes are able to keep a local copy of the data that
they can query even while the lookup table is offline.&lt;&#x2F;p&gt;
&lt;p&gt;Distribute caches to each of the gateways that needs the data, and have those caches update  &amp;lt; more detail &amp;gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Do We Really Need TLS for Cat Pictures?</title>
        <published>2026-07-10T00:00:00+00:00</published>
        <updated>2026-07-10T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Vasilios Syrakis
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://cetanu.github.io/blog/do-we-really-need-tls-for-cat-pictures/"/>
        <id>https://cetanu.github.io/blog/do-we-really-need-tls-for-cat-pictures/</id>
        
        <content type="html" xml:base="https://cetanu.github.io/blog/do-we-really-need-tls-for-cat-pictures/">&lt;p&gt;It is a common piece of skepticism: &lt;em&gt;&quot;Is encrypting every single corner of the
web actually necessary? If I’m just reading a static blog post or looking at a
cute picture of a cat, there are no passwords or credit cards involved. Why do
we need to wrap all of that in TLS? Aren’t we just wasting a massive amount of
CPU cycles globally?&quot;&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;On the surface, it seems like a reasonable complaint. Wrapping every HTTP
response in a layer of cryptography must have a cost.&lt;&#x2F;p&gt;
&lt;p&gt;But when you dig into the mechanics of modern networks and the history of how
ISPs behave when we leave our traffic in plaintext, the answer becomes obvious.
Ubiquitous TLS isn’t paranoia or wasteful bloat—it is the bare minimum required
to keep the web functional.&lt;&#x2F;p&gt;
&lt;p&gt;Let’s break down the actual compute cost of TLS and look at what happens to
your data when you don’t encrypt it.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;the-compute-myth-is-tls-actually-wasting-cpu&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-compute-myth-is-tls-actually-wasting-cpu&quot; aria-label=&quot;Anchor link for: the-compute-myth-is-tls-actually-wasting-cpu&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
The Compute Myth: Is TLS Actually Wasting CPU?&lt;&#x2F;h2&gt;
&lt;p&gt;Historically, yes, SSL&#x2F;TLS was computationally expensive. In the 1990s and
early 2000s, setting up a secure connection and encrypting a stream of data was
a heavy burden for servers. Websites that wanted encryption often had to buy
dedicated, expensive SSL accelerator expansion cards to offload the
cryptographic math from the main CPU.&lt;&#x2F;p&gt;
&lt;p&gt;Today, that bottleneck is long gone.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;1-hardware-accelerated-cryptography-aes-ni-modern-cpus-both-client-side&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#1-hardware-accelerated-cryptography-aes-ni-modern-cpus-both-client-side&quot; aria-label=&quot;Anchor link for: 1-hardware-accelerated-cryptography-aes-ni-modern-cpus-both-client-side&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
1. Hardware-Accelerated Cryptography (AES-NI) Modern CPUs (both client-side&lt;&#x2F;h3&gt;
&lt;p&gt;and server-side) have dedicated hardware instructions built directly into the
silicon to handle symmetric encryption. The most famous of these is Intel and
AMD’s &lt;strong&gt;AES-NI&lt;&#x2F;strong&gt; (Advanced Encryption Standard New Instructions).&lt;&#x2F;p&gt;
&lt;p&gt;With AES-NI, the CPU doesn’t need to run a software loop to encrypt data
block-by-block. Instead, it offloads it to hardware execution units that run
the operations in a handful of clock cycles.&lt;&#x2F;p&gt;
&lt;p&gt;Because of this, modern processors can encrypt and decrypt AES-GCM (the primary
cipher suite used in TLS 1.3) at near-memory speeds. A single modern CPU core
can easily encrypt multiple gigabits of data per second. On a typical web
server, the CPU overhead of encrypting the payload is a fraction of a percent.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2-tls-1-3-handshake-optimizations-the-most-computationally-intensive-part&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#2-tls-1-3-handshake-optimizations-the-most-computationally-intensive-part&quot; aria-label=&quot;Anchor link for: 2-tls-1-3-handshake-optimizations-the-most-computationally-intensive-part&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
2. TLS 1.3 Handshake Optimizations The most computationally intensive part&lt;&#x2F;h3&gt;
&lt;p&gt;of TLS isn’t actually encrypting the data stream (symmetric cryptography)—it is
the initial handshake where the client and server negotiate keys (asymmetric
cryptography).&lt;&#x2F;p&gt;
&lt;p&gt;TLS 1.3, finalized in 2018, radically streamlined this process:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;One Round-Trip (1-RTT):&lt;&#x2F;strong&gt; The handshake was cut from two round-trips to
one, reducing latency.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Pre-Shared Key (PSK) Session Resumption:&lt;&#x2F;strong&gt; Returning visitors can resume a
session in zero round-trips (0-RTT), skipping the expensive asymmetric key
generation entirely.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Faster Algorithms:&lt;&#x2F;strong&gt; Modern deployments favor elliptic curve algorithms
like &lt;code&gt;X25519&lt;&#x2F;code&gt; for key exchange and &lt;code&gt;ECDSA&lt;&#x2F;code&gt; or &lt;code&gt;Ed25519&lt;&#x2F;code&gt; for signatures. These
are orders of magnitude faster and lighter than the classic, bulky RSA keys
of the past.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;If you profile a modern web application, the resources spent on TLS are a drop
in the bucket. Parsing a JSON payload, running a database query, rendering a
React component, or executing a snippet of JavaScript takes orders of magnitude
more CPU cycles than encrypting a packet.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;if-the-compute-cost-is-low-why-encrypt-cat-pictures&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#if-the-compute-cost-is-low-why-encrypt-cat-pictures&quot; aria-label=&quot;Anchor link for: if-the-compute-cost-is-low-why-encrypt-cat-pictures&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
If the Compute Cost is Low, Why Encrypt Cat Pictures?&lt;&#x2F;h2&gt;
&lt;p&gt;If the computational cost is negligible, we still have to ask: what is the
point of encrypting non-sensitive content? If someone intercepts your request
to see a cat picture, they just see a cat picture. Who cares?&lt;&#x2F;p&gt;
&lt;p&gt;The issue isn’t just &lt;strong&gt;confidentiality&lt;&#x2F;strong&gt; (keeping secrets secret). The real
threat is &lt;strong&gt;integrity&lt;&#x2F;strong&gt; (making sure the data isn’t tampered with).&lt;&#x2F;p&gt;
&lt;p&gt;Without TLS, your connection is just plain HTTP. That means every router,
switch, and intermediate network node between your browser and the web server
can read your traffic—and more importantly, they can &lt;strong&gt;modify&lt;&#x2F;strong&gt; it.&lt;&#x2F;p&gt;
&lt;p&gt;This isn’t a theoretical threat. Throughout the 2010s, Internet Service
Providers (ISPs), public Wi-Fi operators, and network gateways repeatedly
behaved like active man-in-the-middle attackers to monetize and track plaintext
web traffic.&lt;&#x2F;p&gt;
&lt;p&gt;Here are a few of the most egregious examples of what ISPs did when they were
allowed to read plaintext streams.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;when-isps-act-like-malicious-actors&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#when-isps-act-like-malicious-actors&quot; aria-label=&quot;Anchor link for: when-isps-act-like-malicious-actors&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
When ISPs Act Like Malicious Actors&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;1-verizon-s-supercookie-uidh&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#1-verizon-s-supercookie-uidh&quot; aria-label=&quot;Anchor link for: 1-verizon-s-supercookie-uidh&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
1. Verizon’s “Supercookie” (UIDH)&lt;&#x2F;h3&gt;
&lt;p&gt;From 2012 to 2016, Verizon Wireless silently injected a unique tracking header
called the &lt;strong&gt;UIDH&lt;&#x2F;strong&gt; (Unique Identifier Header) into every unencrypted HTTP
request sent by its mobile users.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;http&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #BB9AF7;&quot;&gt;GET&lt;&#x2F;span&gt;&lt;span&gt; &#x2F;cat-pictures&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt; HTTP&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF9E64;&quot;&gt;1.1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F7768E;&quot;&gt;Host&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECE6A;&quot;&gt; example.com&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F7768E;&quot;&gt;X-UIDH&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECE6A;&quot;&gt; 9f8a3c2e1b0d7f...  &amp;lt;-- Injected by Verizon&amp;#39;s network&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Because HTTP traffic was plaintext, Verizon’s network equipment could intercept
the packets in transit, append this custom header, and forward it to the
destination server.&lt;&#x2F;p&gt;
&lt;p&gt;This header acted as an undeletable tracking cookie. Even if users cleared
their browser cookies, blocked trackers, or used incognito mode, third-party
advertising networks (like Turn) could read this injected &lt;code&gt;X-UIDH&lt;&#x2F;code&gt; header and
use it to reconstruct the user’s tracking profile across the web.&lt;&#x2F;p&gt;
&lt;p&gt;Because it was done at the network level, users had no way to disable it. It
was only stopped after a security researcher exposed the practice, leading to
public outcry and a &lt;strong&gt;$1.35 million fine&lt;&#x2F;strong&gt; from the FCC in 2016. If the sites
had been using HTTPS, Verizon would not have been able to inject the header
without breaking the cryptographic signature of the packet.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2-comcast-s-javascript-injection-in-2017-comcast-users-began-noticing&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#2-comcast-s-javascript-injection-in-2017-comcast-users-began-noticing&quot; aria-label=&quot;Anchor link for: 2-comcast-s-javascript-injection-in-2017-comcast-users-began-noticing&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
2. Comcast’s JavaScript Injection In 2017, Comcast users began noticing&lt;&#x2F;h3&gt;
&lt;p&gt;strange popups appearing on top of the websites they were browsing.&lt;&#x2F;p&gt;
&lt;p&gt;Comcast was using deep packet inspection (DPI) to monitor customer traffic.
When they detected a user was browsing an unencrypted HTTP site, they would
modify the HTML on the fly, injecting custom JavaScript code into the webpage
before it reached the user’s browser.&lt;&#x2F;p&gt;
&lt;p&gt;The injected code was used to display “courtesy notices” telling users they
were approaching their monthly data cap, or warning them that their modem was
out of date.&lt;&#x2F;p&gt;
&lt;p&gt;While Comcast claimed this was a feature to help consumers, injecting arbitrary
JavaScript into third-party websites is incredibly dangerous. It can break the
site’s layout, cause conflicts with the site’s own scripts, and introduce major
security vulnerabilities. If a hacker managed to compromise Comcast’s injection
system, they could have executed arbitrary code on millions of customer
browsers.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;3-hotel-and-airport-wi-fi-ad-injection-if-you-ve-ever-connected-to-a-hotel&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#3-hotel-and-airport-wi-fi-ad-injection-if-you-ve-ever-connected-to-a-hotel&quot; aria-label=&quot;Anchor link for: 3-hotel-and-airport-wi-fi-ad-injection-if-you-ve-ever-connected-to-a-hotel&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
3. Hotel and Airport Wi-Fi Ad Injection If you’ve ever connected to a hotel&lt;&#x2F;h3&gt;
&lt;p&gt;or airport Wi-Fi network and seen a strange floating toolbar at the bottom of
every page, or unexpected ads on sites that don’t usually host them, you’ve
experienced ad injection.&lt;&#x2F;p&gt;
&lt;p&gt;Public Wi-Fi gateways frequently intercept HTTP traffic to inject local
advertisements, promotions, or terms-of-service agreements. By rewriting the
HTML of the websites you visit, they degrade your browsing experience and can
expose your browser to malicious ads (malvertising).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;4-dns-hijacking-and-search-redirection-many-isps-including-charter-cox&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#4-dns-hijacking-and-search-redirection-many-isps-including-charter-cox&quot; aria-label=&quot;Anchor link for: 4-dns-hijacking-and-search-redirection-many-isps-including-charter-cox&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
4. DNS Hijacking and Search Redirection Many ISPs (including Charter, Cox,&lt;&#x2F;h3&gt;
&lt;p&gt;and Rogers) historically practiced DNS hijacking on unencrypted traffic. If you
typed in a non-existent URL (like &lt;code&gt;http:&#x2F;&#x2F;this-does-not-exist.com&lt;&#x2F;code&gt;), instead of
showing a browser error page, the ISP would intercept the request and redirect
you to a custom search page loaded with ads and sponsored links, monetizing
your typos.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;the-three-pillars-of-tls&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-three-pillars-of-tls&quot; aria-label=&quot;Anchor link for: the-three-pillars-of-tls&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
The Three Pillars of TLS&lt;&#x2F;h2&gt;
&lt;p&gt;When we encrypt the web, we aren’t just hiding passwords. We are establishing
three fundamental guarantees:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th style=&quot;text-align: left&quot;&gt;Guarantee&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: left&quot;&gt;What It Means&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: left&quot;&gt;What Happens Without It&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;&lt;strong&gt;Confidentiality&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Only you and the server can read the data.&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;ISPs, governments, or hackers on your local Wi-Fi can see exactly which pages, articles, and pictures you are viewing.&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;&lt;strong&gt;Integrity&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;The data cannot be modified in transit.&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Network middleboxes can inject ads, tracking headers, popups, or malicious scripts into the page.&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;&lt;strong&gt;Authenticity&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;You are communicating with the real website.&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;An attacker can spoof the website’s identity (e.g., DNS poisoning) and serve you a fake page without your browser raising any warnings.&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;Even if you are only looking at cat pictures, you deserve to know that the cat
pictures you are seeing are the actual ones sent by the server, and not a
modified payload injected with tracking scripts and banner ads.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;The push for a 100% encrypted web—championed by projects like &lt;em&gt;Let’s Encrypt&lt;&#x2F;em&gt;,
search engines prioritizing HTTPS, and browsers displaying warnings for
insecure sites—was a necessary response to the reality of the internet’s
infrastructure.&lt;&#x2F;p&gt;
&lt;p&gt;The internet is not a direct wire between your computer and a server; it is a
chain of intermediaries. And history has proven that if those intermediaries
are given the power to read and modify your traffic, they will exploit it for
profit.&lt;&#x2F;p&gt;
&lt;p&gt;Thanks to modern hardware acceleration like AES-NI and the design of TLS 1.3,
we can secure the entire web with virtually zero performance penalty. Encrypted
cat pictures aren’t a waste of compute—they are a victory for user privacy and
web integrity.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Profit or Poverty: False Sharing</title>
        <published>2026-07-07T00:00:00+00:00</published>
        <updated>2026-07-07T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Vasilios Syrakis
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://cetanu.github.io/blog/false-sharing/"/>
        <id>https://cetanu.github.io/blog/false-sharing/</id>
        
        <content type="html" xml:base="https://cetanu.github.io/blog/false-sharing/">&lt;p&gt;In previous blogs I talked a little bit about
&lt;a href=&quot;https:&#x2F;&#x2F;cetanu.github.io&#x2F;blog&#x2F;why-numa-is-important-in-trading&#x2F;&quot;&gt;NUMA&lt;&#x2F;a&gt; and the
&lt;a href=&quot;https:&#x2F;&#x2F;cetanu.github.io&#x2F;blog&#x2F;translation-lookaside-buffer&#x2F;&quot;&gt;TLB&lt;&#x2F;a&gt;, if you haven’t read those, I
recommend starting there before reading this.&lt;&#x2F;p&gt;
&lt;p&gt;You should already understand that accessing main memory is relatively slow for
the CPU, especially in a low-latency environment, and you may be familiar with
the fact that the CPU has small caches of varying sizes on the chip, known as
the L1, L2, and L3 caches.&lt;&#x2F;p&gt;
&lt;p&gt;As their name implies, there are levels, or a hierarchy, from fastest (and
smallest) to slowest (and largest). The L1 cache is also typically split into
two caches, one being an instruction cache and the other being a data cache.&lt;&#x2F;p&gt;
&lt;p&gt;Fun fact: these caches are usually made of SRAM and usually they are the
largest part on the chip.&lt;&#x2F;p&gt;
&lt;img class=&quot;no-hover&quot;alt=&quot;relative speed of hardware&quot;src=&quot;&amp;#x2F;img&amp;#x2F;relative-hw-speed.png&quot;&#x2F;&gt;&lt;h2 id=&quot;requesting-data-from-the-cache&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#requesting-data-from-the-cache&quot; aria-label=&quot;Anchor link for: requesting-data-from-the-cache&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
Requesting data from the cache&lt;&#x2F;h2&gt;
&lt;p&gt;You may have heard of “cache lines” before (also called cache blocks), maybe on
some article about performance on hackernews.&lt;&#x2F;p&gt;
&lt;p&gt;A cache line can be thought of a single unit of data which is transferred from
RAM to the cache. Usually one cache line is 64 bytes, but this can differ
depending on your CPU architecture.&lt;&#x2F;p&gt;
&lt;p&gt;This introduces the concept of cache locality. When the CPU needs to access the
same memory several times in a row, after it’s already been pulled into the L1
cache, this is referred to as &lt;strong&gt;temporal locality&lt;&#x2F;strong&gt;. When the CPU pulls in a
cache line and all the data it needs is within that block, it’s referred to as
spatial locality. A good example of &lt;strong&gt;spatial locality&lt;&#x2F;strong&gt; is when you have an
array that fits into one cache line, resulting in traversal being extremely
fast. An example of potentially terrible cache performance might be something
like a linked-list.&lt;&#x2F;p&gt;
&lt;p&gt;On systems with multiple CPU cores, they might share the same chunk of memory
in their local caches. Most processors use what is known as the MESI protocol
to prevent overwriting each others data. Jon Gjengset has a &lt;a href=&quot;https:&#x2F;&#x2F;cetanu.github.io&#x2F;blog&#x2F;false-sharing&#x2F;www.youtube.com&#x2F;watch?v=tND-wBBZ8RY&quot;&gt;great
talk&lt;&#x2F;a&gt; about Mutexes which also covers the
MESI protocol.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;where-it-can-go-wrong&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#where-it-can-go-wrong&quot; aria-label=&quot;Anchor link for: where-it-can-go-wrong&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
Where it can go wrong&lt;&#x2F;h2&gt;
&lt;p&gt;CPUs can enter what is called a coherence livelock or cache-line pingponging,
where multiple cores fight over the same memory, or the same cache line.&lt;&#x2F;p&gt;
&lt;p&gt;The first way this can happen is called &lt;strong&gt;False Sharing&lt;&#x2F;strong&gt;, where your data may
reside in a cache line, but it may not be occupying that entire cache line by
itself. If your data sits in the first half but some other data sits in the
latter half, it’s possible that two CPUs may continuously write to the same
cache line, requiring them to communicate with each other to gain ownership in
order to modify the cache line. If you watched Jon’s video above you would know
that this can eat up dozens of precious nanoseconds, just for the comms between
CPUs. As you increase the number of cores on a system this problem can be
exacerbated.&lt;&#x2F;p&gt;
&lt;img class=&quot;no-hover&quot;alt=&quot;diagram of false sharing&quot;src=&quot;&amp;#x2F;img&amp;#x2F;false-sharing.png&quot;&#x2F;&gt;
&lt;p&gt;The second way is &lt;strong&gt;True Sharing&lt;&#x2F;strong&gt;, where cores may write to the same variable or
data structure in memory. This could happen during atomic operations or when
using synchronization primitives such as mutexes, semaphores, barriers,
spinlocks, and so on.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-to-protect-against-it&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#how-to-protect-against-it&quot; aria-label=&quot;Anchor link for: how-to-protect-against-it&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
How to protect against it&lt;&#x2F;h2&gt;
&lt;p&gt;At least for false sharing, the solution seems simple: just pad your data
structure or variable so that it occupies an entire cache line. Obviously it
would be silly to do this with every variable in your program, so you have to
pick the time-critical parts of your application here.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span style=&quot;color: #0DB9D7;&quot;&gt; std&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #0DB9D7;&quot;&gt;sync&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #0DB9D7;&quot;&gt;atomic&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C0CAF5;&quot;&gt;AtomicU64&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;#[&lt;&#x2F;span&gt;&lt;span&gt;repr&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;align&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span&gt;64&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;))]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;pub&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BB9AF7;&quot;&gt; struct&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C0CAF5;&quot;&gt; PaddedAtomic&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;    pub&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C0CAF5;&quot;&gt; value&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C0CAF5;&quot;&gt; AtomicU64&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;For true sharing, it seems quite a bit more complicated to get right. Sometimes
you really do need atomic operations on global memory.&lt;&#x2F;p&gt;
&lt;p&gt;You could pull data into thread local storage, only merging back at the end of
expensive or time critical execution.&lt;&#x2F;p&gt;
&lt;p&gt;You could reorganize your program to do batching to reduce how often it needs
to write back to the shared memory.&lt;&#x2F;p&gt;
&lt;p&gt;Maybe a lock-free data structure could be used.&lt;&#x2F;p&gt;
&lt;p&gt;The solution most likely depends on exactly what you are willing to accept in
terms of trade-offs.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;Like I usually say, for the most part things like this are unnoticeable and
work just fine for the majority of situations. If you’re running a flask webapp
you probably don’t and should not care about this.&lt;&#x2F;p&gt;
&lt;p&gt;When you require low-latency, then you need to understand how to write your
programs such that they exploit cache-lines for your architecture because it
makes the difference between a 1ns L1 cache hit, or falling off a cliff with a
100x slower execution.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>For 12 years, I ignored Kubernetes</title>
        <published>2026-06-30T00:00:00+00:00</published>
        <updated>2026-06-30T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Vasilios Syrakis
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://cetanu.github.io/blog/i-ignored-kubernetes-for-12-years/"/>
        <id>https://cetanu.github.io/blog/i-ignored-kubernetes-for-12-years/</id>
        
        <content type="html" xml:base="https://cetanu.github.io/blog/i-ignored-kubernetes-for-12-years/">&lt;h1 id=&quot;my-introduction-to-kubernetes&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#my-introduction-to-kubernetes&quot; aria-label=&quot;Anchor link for: my-introduction-to-kubernetes&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
My introduction to Kubernetes&lt;&#x2F;h1&gt;
&lt;p&gt;Like many people I heard about kubernetes because it was software from Google
that people were hyped about. I understood a little bit about it - it’s a
container orchestrator, right?&lt;&#x2F;p&gt;
&lt;p&gt;I also heard about painful experiences that people had with it and I got this
impression that it was way too complicated for what it provided in most cases.&lt;&#x2F;p&gt;
&lt;p&gt;We’ve all heard the meme “I deployed my blog on kubernetes” as a classic
example of overengineering, and resume driven development when a team sets up
kubernetes for a single application.&lt;&#x2F;p&gt;
&lt;p&gt;While working in the industry, I was happy to let other people deal with this
perceived complexity. I was willing to let them work out all the problems and
figure out how to offer kubernetes to me as an end-user.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;post-layoff&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#post-layoff&quot; aria-label=&quot;Anchor link for: post-layoff&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
Post-layoff&lt;&#x2F;h1&gt;
&lt;p&gt;However, as time went on, it seemed that the entire industry made a decision to
adopt kubernetes, even if the overheads and complexity were of some cost,
because the cost was outweighed by standardisation.&lt;&#x2F;p&gt;
&lt;p&gt;Suddenly, every SRE &#x2F; platform engineer role seemed to require knowledge of
kubernetes. It was never too clear how much knowledge or skill was required,
and there have been several interviews in which I’ve expressed that I don’t
have any experience operating kubernetes (probably a bad thing to say in an
interview) and yet interviewers did not seem to be able to give me a picture of
how they use kubernetes and what I would need to know in order to sufficiently
operate their clusters&#x2F;infrastructure.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;start-of-the-learning-journey&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#start-of-the-learning-journey&quot; aria-label=&quot;Anchor link for: start-of-the-learning-journey&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
Start of the learning journey&lt;&#x2F;h1&gt;
&lt;p&gt;So after being laid off in March 2026, it became apparent that learning
kubernetes would be a good idea. Later in May, when my video went viral and an
audience appeared with the desire to learn new things, particularly by being
taught by myself, it made even more sense to learn kubernetes because it would
give me the opportunity to both learn and teach at the same time.&lt;&#x2F;p&gt;
&lt;p&gt;So I embarked on a journey to learn kubernetes with the least amount of
patience possible, pressuring myself to learn it in a week. Over 7
live-streams, approximately 2-3 hours each, I gathered a decent picture of the
kubernetes landscape, a bit of history, and what tools people use to deploy and
manage kubernetes in modern times.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#&quot; aria-label=&quot;Anchor link for: &quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
&lt;&#x2F;h2&gt;
&lt;p&gt;The very first thing I did was started streaming on Twitch, with the stream
title “Added Kubernetes to my CV, now I have to learn it” and opened a browser,
went to the kubernetes website and started reading docs linearly. After going
through various concepts and basically trying to memorize them and failing, a
few chatters informed me of Kelsey Hightower’s guide “Learning Kubernetes The
Hard Way”.&lt;&#x2F;p&gt;
&lt;p&gt;I started going through this tutorial, and found that it required at
least 3 machines with 2GB of memory each, and I didn’t have that hardware
laying around, and I didn’t want to run them on my stream PC. So I signed up to
AWS and started to create EC2 instances manually, and SSHing onto them to set
up hostnames and run OpenSSL commands to do PKI.&lt;&#x2F;p&gt;
&lt;p&gt;At some point the tutorial was telling me to execute blobs of shell scripts and
I became enraged at the suggestion because it’s been two decades since the
advent of configuration management, and at least a decade since kubernetes has
been released. So I thought that all this friction had already been smoothed out.&lt;&#x2F;p&gt;
&lt;p&gt;In hindsight, the entire purpose of Kelsey’s guide is to expose someone to
these low-level concerns. However, I am personally more in favor of getting
something practical up and running quickly, and dealing with problems as they
arise.&lt;&#x2F;p&gt;
&lt;p&gt;So I started an argument with Gemini about Kelsey’s guide, and it graciously
agreed that no sane engineer would set up kubernetes by following that guide in
a profession scenario. It led me to discover &lt;code&gt;kubeadm&lt;&#x2F;code&gt; which gave me a way to
bootstrap the cluster nodes in one command.&lt;&#x2F;p&gt;
&lt;p&gt;I used this newly found tool to then set all the infrastructure up with
Cloudformation, running the tool during the init of the EC2 instances via
UserData. I successfully got a cluster running and then tried to use Helm to
install “charts” to get a valheim server pod running.&lt;&#x2F;p&gt;
&lt;p&gt;Using helm got a bit messy, and a bunch of pods were crashing, and to be honest
I had no idea why. My hypothesis at the time was that the process of pushing
YAML to kube via helm and kubectl was fragile and that it was running into
issues with state becoming corrupted or partially deployed.&lt;&#x2F;p&gt;
&lt;p&gt;Finding myself frustrated, I took a break to think about what my next approach would be.&lt;&#x2F;p&gt;
&lt;p&gt;I had a friend tell me about his setup where he doesn’t run any helm
commands against the cluster, but instead uses it to produce kubeconfigs as
build artifacts which are then ingested via GitOps.&lt;&#x2F;p&gt;
&lt;p&gt;I also started to remember a recommendation to use Talos Linux from another
peer so I decided to see what that was about. It sold this idea of an immutable
kubernetes cluster which supposedly would prevent the kind of state corruption
that I thought I was running into.&lt;&#x2F;p&gt;
&lt;p&gt;At this stage I decided to completely pivot, use Talos Linux, and switch to
Pulumi instead of Cloudformation for the infrastructure even though it would
cost me some time in the learning process.&lt;&#x2F;p&gt;
&lt;p&gt;I quite enjoyed Pulumi; it feels like a type-checked infrastructure, which is
cool. I used the golang SDK.&lt;&#x2F;p&gt;
&lt;p&gt;After a bunch of troubleshooting and looking at logs of pods, and feeding those
logs into Gemini, I discovered that there was a bootstrap dependency issue
where other pods like Argo required that AWS CCM was setup before it would work
properly.&lt;&#x2F;p&gt;
&lt;p&gt;After this, it was pretty smooth sailing. I could add CRDs and charts to my
gitrepo and argo would pick them up and provision pods quickly. I started to
thinkabout how to platformatize everything that I’d built but I realised that
thiscan become quite subjective, and so for this article I won’t go into that
atall, but the options that I was aware of were tools like KubeVela,
Crossplane,and Backstage.&lt;&#x2F;p&gt;
&lt;blockquote class=&quot;warning&quot;&gt;
	&lt;p class=&quot;alert-title&quot;&gt;
		&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;Warning&lt;&#x2F;p&gt;
	&lt;p&gt;Still writing this up&lt;&#x2F;p&gt;

&lt;&#x2F;blockquote&gt;
&lt;iframe
	class=&quot;youtube-embed&quot;
	src=&quot;https:&#x2F;&#x2F;www.youtube-nocookie.com&#x2F;embed&#x2F;V8iT_2YHtns&quot;
	allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot;
	referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen&gt;
&lt;&#x2F;iframe&gt;
&lt;!--

&lt; here is where I boil the entire article to its essence &gt;

Two paths:

## Managed-k8s

* No-brainer
* Good for small scale
* EKS - Locked into AWS Bottlerocket
* GKE - ???
* AKS - ???

## Run-Your-Own-Cluster

* Don&#x27;t want vendor lock-in
* Strong need for multi-cloud, especially mixed with baremetal
* 

---

# notes


* looked up docs on kubernetes.org

* kubernetes the hard way
- manually provisioning kube nodes with AWS
- hated it, wanted something more polished
- started asking LLMs about better ways

* discovered kubeadm
- started using cloudformation to provision the nodes
- created token on the control-plane via userdata script
- uploaded the token to AWS ParameterStore
- worker nodes pulled the token and joined cluster
- tried using Helm to provision pods and such

* discovered Talos linux
- Replaced the EC2s (Ubuntu) with Talos
- Rewrote the Cloudformation into Pulumi
- Used a Pulumi library to do the Talos bootstrapping
- Looots of troubleshooting
- Found that AWS CCM was a dependency of Argo
- Fixed the bootstrap, Argo working
- Started deploying pods
- Logged into Valheim game server as a test
- Made a cats server

* Started thinking about how to really platformatize kube
- found KubeVela
- learnt about the OAM (Open Application Model)
- refactored existing pods into OAM apps
- we are now here







---


Of course, it&#x27;s obvious that people do this usually as an exercise to learn
kubernetes in whatever way they can, as did I with my recent adventure to
deploy a single valheim server.



At a kubernetes interview they may ask you about Operators and CRDs. Also there
is a question like &quot;What exactly happens when you run kubectl apply -f
deployment.yaml&quot;?

kubestronaut


short: what is the best piece of advice you&#x27;ve ever gotten?

video: recent experience with interviews
* coding portion
    - fundamental knowledge of DSA
    - methodical approach:
        * making a naive version first
        * addressing edge cases
        * optimizing performance
        * awareness of time&#x2F;space complexity
        * communication the entire way through - justifying tradeoffs
* system design
    - requirements gathering to a professional degree
        * understand what the hiring company wants&#x2F;needs
        --&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Profit or Poverty: TLB</title>
        <published>2026-04-28T00:00:00+00:00</published>
        <updated>2026-04-28T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Vasilios Syrakis
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://cetanu.github.io/blog/translation-lookaside-buffer/"/>
        <id>https://cetanu.github.io/blog/translation-lookaside-buffer/</id>
        
        <content type="html" xml:base="https://cetanu.github.io/blog/translation-lookaside-buffer/">&lt;h2 id=&quot;the-illusion-of-memory&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-illusion-of-memory&quot; aria-label=&quot;Anchor link for: the-illusion-of-memory&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
The Illusion of Memory&lt;&#x2F;h2&gt;
&lt;p&gt;Applications do not allocate memory directly to physical addresses on the RAM.
They are presented with virtual memory by the operating system, so that it can
pretend that the application has a contiguous block of memory when in reality
its memory is scattered across the physical RAM in fixed-size chunks of memory
called pages, kept track of in a page table.&lt;&#x2F;p&gt;
&lt;p&gt;When a process starts, the operating system allocates a range of virtual
addresses to it in the form of pages, but leaves the entries in the page table
blank until they’re accessed (i.e. they are lazy-loaded)&lt;&#x2F;p&gt;
&lt;p&gt;If the process tries to access memory, the operating system follows the virtual
memory address to the physical memory address and hands back the data. The
actual translation from virtual address to physical address is not performed by
the OS but by a dedicated hardware component on the CPU called a memory
management unit (MMU).&lt;&#x2F;p&gt;
&lt;p&gt;Sometimes, if RAM is running low or hasn’t been accessed in a while, data is
taken out of RAM and swapped to disk. If the process then tries to access that
memory again, a &lt;strong&gt;major&lt;&#x2F;strong&gt; page fault occurs, the operating system has to retrieve the
data from disk, load it into RAM, and then update the page table before the
process can proceed.&lt;&#x2F;p&gt;
&lt;p&gt;A minor page fault is when the data is still in RAM, but the page table for the
process has not been mapped to the physical page yet. By default the operating
system is lazy about mapping page table entries, but there are strategies for
ensuring that all pages are mapped.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-bottleneck&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-bottleneck&quot; aria-label=&quot;Anchor link for: the-bottleneck&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
The Bottleneck&lt;&#x2F;h2&gt;
&lt;p&gt;Every time you’re reading variables, or loading instructions, or saving data,
the system is translating virtual memory addresses to physical addresses.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;a-hardware-page-table-walk&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#a-hardware-page-table-walk&quot; aria-label=&quot;Anchor link for: a-hardware-page-table-walk&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
A hardware page table walk&lt;&#x2F;h3&gt;
&lt;p&gt;This translation can be quite involved if we dig really deep into the detail.
The MMU looks at a register to find the physical address of the root page
table. Then it takes some bits from the virtual address, goes to RAM, and reads
the page table entry at that index. Then it takes the result from that lookup,
goes back to RAM again, and finds the entry point to another intermediate
table, and another intermediate table… until it gets the physical page frame
number. It then combines the address with some of the bits from the original
virtual address to find the exact byte in memory.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;introducing-the-tlb&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#introducing-the-tlb&quot; aria-label=&quot;Anchor link for: introducing-the-tlb&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
Introducing the TLB&lt;&#x2F;h3&gt;
&lt;p&gt;The translation look-aside buffer (TLB) is a specialized cache within the MMU
which remembers recent mappings to avoid the CPU having to check the full page
table (in RAM) on every single memory access (billions of times per second)&lt;&#x2F;p&gt;
&lt;p&gt;The consequence of a TLB miss is that the operation takes 100-200x longer. In
reality this equates to about 100 nanoseconds, which is miniscule, but the
volume of these operations causes that time to add up rapidly. Not to mention
that 1 nanosecond would be much preferred.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-to-make-money&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#how-to-make-money&quot; aria-label=&quot;Anchor link for: how-to-make-money&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
How to make 💵 MONEY 💵&lt;&#x2F;h2&gt;
&lt;p&gt;Since the consequence of TLB cache misses are so dire, it makes sense to avoid
them. Working around the cache is out of the question, so the way forward is to
enhance the effectiveness of the cache by making it have to cache less things,
thereby improving its hit rate.&lt;&#x2F;p&gt;
&lt;p&gt;The TLB has a limit on the number of page table entries it can hold. A page is
typically 4KB but this may differ based on your hardware. So if your system has
32GB of memory, you need 8 million entries to map all the available memory on
the system, which is not possible.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;hugepages&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#hugepages&quot; aria-label=&quot;Anchor link for: hugepages&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
Hugepages&lt;&#x2F;h3&gt;
&lt;p&gt;This is where “Hugepages” come in.&lt;&#x2F;p&gt;
&lt;p&gt;Hugepages is a feature in the linux kernel that allows a program to use much
larger page sizes, usually either 2MB or 1GB. By using this option, you can
effectively map all of the memory that you need such that it can fit in the
limited number of entries in the TLB easily.&lt;&#x2F;p&gt;
&lt;p&gt;For example, with the 32GB example from before, using 1GB hugepages, you would
only need 32 entries, rather than 8,000,000.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;enabling-hugepages-for-your-application&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#enabling-hugepages-for-your-application&quot; aria-label=&quot;Anchor link for: enabling-hugepages-for-your-application&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
Enabling hugepages for your application&lt;&#x2F;h4&gt;
&lt;p&gt;You could set a sysctl (&lt;code&gt;vm.nr_hugepages&lt;&#x2F;code&gt;), or you could control it from your program, for example…&lt;&#x2F;p&gt;
&lt;p&gt;In C++, when calling &lt;code&gt;mmap&lt;&#x2F;code&gt;, you can pass the &lt;code&gt;MAP_HUGETLB&lt;&#x2F;code&gt; bitflag or &lt;code&gt;MAP_HUGE_1GB&lt;&#x2F;code&gt; for example.&lt;&#x2F;p&gt;
&lt;p&gt;In Rust, there are some crates like &lt;code&gt;memmap2&lt;&#x2F;code&gt; or &lt;code&gt;hugepage-rs&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;memory-pinning&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#memory-pinning&quot; aria-label=&quot;Anchor link for: memory-pinning&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
Memory Pinning&lt;&#x2F;h3&gt;
&lt;p&gt;I mentioned earlier that when a process starts, the OS allocates virtual
memory, but it lazily maps the page table entries. So you may have hugepages
enabled, but you can still encounter a page fault if you access memory that
hasn’t been mapped yet.&lt;&#x2F;p&gt;
&lt;p&gt;There is a linux system call, &lt;code&gt;mlockall&lt;&#x2F;code&gt; which can be used to eagerly map all
the virtual memory for your process. It’s advised to call this before entering
the critical part of the program, such as any part of the program which needs
to be real-time (requires deterministic timing).&lt;&#x2F;p&gt;
&lt;p&gt;This system call not only “pre-faults” the pages so that they’re ready, it also
pins them so that the operating system cannot swap them back to disk.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;how-to-pin-memory-from-within-your-program&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#how-to-pin-memory-from-within-your-program&quot; aria-label=&quot;Anchor link for: how-to-pin-memory-from-within-your-program&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
How to pin memory, from within your program&lt;&#x2F;h4&gt;
&lt;p&gt;In C++ you would include the &lt;code&gt;sys&#x2F;mman.h&lt;&#x2F;code&gt; header, allowing you to call mlockall directly.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;c&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;#&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BB9AF7;&quot;&gt;include&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECE6A;&quot;&gt;sys&#x2F;mman.h&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;#&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BB9AF7;&quot;&gt;include&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECE6A;&quot;&gt;iostream&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;#&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BB9AF7;&quot;&gt;include&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECE6A;&quot;&gt;cstring&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #BB9AF7;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7AA2F7;&quot;&gt; main&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ABDF5;&quot;&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #BB9AF7;&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ABDF5;&quot;&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7AA2F7;&quot;&gt;mlockall&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ABDF5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7AA2F7;&quot;&gt;MCL_CURRENT &lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7AA2F7;&quot;&gt; MCL_FUTURE&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ABDF5;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BB9AF7;&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF9E64;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ABDF5;&quot;&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ABDF5;&quot;&gt;        std::cerr &lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;&amp;lt;&amp;lt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECE6A;&quot;&gt;Failed to lock memory: &lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;&amp;quot; &amp;lt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7AA2F7;&quot;&gt; std::strerror&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ABDF5;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7AA2F7;&quot;&gt;errno&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ABDF5;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt; &amp;lt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ABDF5;&quot;&gt; std::endl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #BB9AF7;&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF9E64;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ABDF5;&quot;&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7AA2F7;&quot;&gt;    munlockall&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ABDF5;&quot;&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ABDF5;&quot;&gt; &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #BB9AF7;&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF9E64;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ABDF5;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In Rust, you would use the &lt;code&gt;libc&lt;&#x2F;code&gt; crate and call it similarly.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span style=&quot;color: #0DB9D7;&quot;&gt; libc&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;::{&lt;&#x2F;span&gt;&lt;span&gt;mlockall&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C0CAF5;&quot;&gt; MCL_CURRENT&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C0CAF5;&quot;&gt; MCL_FUTURE&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;};&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7AA2F7;&quot;&gt; main&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #BB9AF7;&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C0CAF5;&quot;&gt; locked&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt; = unsafe {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7AA2F7;&quot;&gt;        mlockall&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF9E64;&quot;&gt;MCL_CURRENT&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BB9AF7;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF9E64;&quot;&gt; MCL_FUTURE&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;    };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #BB9AF7;&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C0CAF5;&quot;&gt; locked&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BB9AF7;&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF9E64;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #BB9AF7;&quot;&gt;        let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C0CAF5;&quot;&gt; err&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #0DB9D7;&quot;&gt; std&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #0DB9D7;&quot;&gt;io&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C0CAF5;&quot;&gt;Error&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #7AA2F7;&quot;&gt;last_os_error&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #7AA2F7;&quot;&gt;        panic!&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECE6A;&quot;&gt;Failed to lock memory: &lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;{}&amp;quot;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #C0CAF5;&quot;&gt; err&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;Ultimately, the illusion of memory that modern systems provide is a
double-edged sword. While virtual memory addressing solves major problems
around process isolation for most systems, the introduction of page tables and
the need to perform multi-level page table walks introduces latency that is
unnoticable to humans but harmful to low-latency and real-time applications.&lt;&#x2F;p&gt;
&lt;p&gt;With this small bit of operating systems and linux knowledge you can use
hugepages and memory pinning to reduce memory latency by up to 100 to 200 times
in certain scenarios.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Profit or Poverty: Realtime kernel patch</title>
        <published>2026-04-20T00:00:00+00:00</published>
        <updated>2026-04-20T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Vasilios Syrakis
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://cetanu.github.io/blog/linux-is-not-a-realtime-system/"/>
        <id>https://cetanu.github.io/blog/linux-is-not-a-realtime-system/</id>
        
        <content type="html" xml:base="https://cetanu.github.io/blog/linux-is-not-a-realtime-system/">&lt;p&gt;&lt;strong&gt;Linux is not a realtime operating system.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This statement confused me at first, but as I looked further, it began to make
sense.&lt;&#x2F;p&gt;
&lt;p&gt;Linux provides an environment where multitasking is possible, facilitated by a
scheduler which has goals such as minimizing wait times and latency, maximizing
throughput, and maximizing fairness.&lt;&#x2F;p&gt;
&lt;p&gt;These goals can sometimes pull in different directions. If you maximize
throughput for one task, you increase latency for others. If you minimize
latency by switching tasks more often, overheads start to pile up. If every
process gets an equal slice of time for fairness, heavy tasks are starved while
light tasks sit idle.&lt;&#x2F;p&gt;
&lt;p&gt;Taking the general approach is the best decision for most usecases like servers
and desktop computing, but it means that Linux is a “soft realtime” system.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;defining-realtime&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#defining-realtime&quot; aria-label=&quot;Anchor link for: defining-realtime&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
Defining “realtime”&lt;&#x2F;h2&gt;
&lt;p&gt;What is evoked when you hear someone talk about something happening in
&lt;strong&gt;realtime?&lt;&#x2F;strong&gt; We often think that it is happening immediately, like we’re
describing something in the present.&lt;&#x2F;p&gt;
&lt;p&gt;In computing, realtime refers to something more specific. From Wikipedia:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;A system is said to be realtime if the total correctness of an operation
depends not only upon its logical correctness, but also upon the time in
which it is performed.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;That is to say that it does not mean “fast” necessarily, it’s more to do with
deterministic behavior in relation to time.&lt;&#x2F;p&gt;
&lt;p&gt;Linux is a &lt;strong&gt;soft&lt;&#x2F;strong&gt; realtime system, meaning that if an operation misses its
deadline, it isn’t a hard failure. Your process might wait a few hundred
microseconds before the scheduler gives it a time-slice, and for the vast
majority of cases, this is acceptable and nobody notices. If a deadline is
missed frequently, the user experiences a degradation of service. For example,
a web page might load slower, but still eventually load.&lt;&#x2F;p&gt;
&lt;p&gt;A &lt;strong&gt;hard&lt;&#x2F;strong&gt; realtime system would be something like an assembly line, where if a
part isn’t processed before its deadline, that part is no longer reachable by
the robot, and the next robot won’t be able to process it correctly.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-scheduler&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-scheduler&quot; aria-label=&quot;Anchor link for: the-scheduler&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
The Scheduler&lt;&#x2F;h2&gt;
&lt;p&gt;I mentioned earlier that Linux provides multitasking. It really provides the
illusion of multitasking, at least on a single core. The task scheduler is what
creates this illusion, by allocating slices of time to each task, interleaving
their execution with one another to create the illusion of simultaneous
progression. This is referred to as concurrency, as opposed to parallelism.&lt;&#x2F;p&gt;
&lt;small&gt;
Concurrency comes from the Latin &quot;con&quot; (together) and &quot;currere&quot; (to run,
literally, to move faster than walking).
&lt;&#x2F;small&gt;
&lt;p&gt;Concurrency is great, especially when tasks have to wait for something to
become available - the scheduler can pause that task until it’s ready to be
executed, and give time-slices to other tasks in the meanwhile.&lt;&#x2F;p&gt;
&lt;p&gt;The task itself can communicate to the scheduler that it should be paused to
allow other tasks to progress. Often, a task will do this implicitly by
executing a particular system call, such as &lt;code&gt;read()&lt;&#x2F;code&gt; or &lt;code&gt;sleep()&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;However, the &lt;em&gt;scheduler&lt;&#x2F;em&gt; can also pause a task &lt;em&gt;without any cooperation from the
task.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;img class=&quot;no-hover&quot;alt=&quot;Scheduler standing behind the
application, about to ruin its day&quot;src=&quot;&amp;#x2F;img&amp;#x2F;preemption_meme_1.png&quot;&#x2F;&gt;&lt;h2 id=&quot;preemption&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#preemption&quot; aria-label=&quot;Anchor link for: preemption&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
Preemption&lt;&#x2F;h2&gt;
&lt;p&gt;This particular style of multitasking on Linux is called preemptive
multitasking. Tasks can be “preempted”.&lt;&#x2F;p&gt;
&lt;p&gt;Sounds interesting, but what does it mean? &lt;em&gt;What&lt;&#x2F;em&gt; is being preempted?&lt;&#x2F;p&gt;
&lt;p&gt;The current task’s execution time is being preempted - cut short - so that a
more important task can be run.&lt;&#x2F;p&gt;
&lt;p&gt;During preemption, the process state is saved, and another’s is loaded. This
mechanism is what we know to be a context switch.&lt;&#x2F;p&gt;
&lt;p&gt;An example where this is good: you run an application, and that application
chews up all your compute, and you want to cancel it. If it couldn’t be
preempted, you wouldn’t be able to move your cursor to close or cancel it.&lt;&#x2F;p&gt;
&lt;p&gt;Where preemption becomes undesirable is when you’re in a low-latency
environment such as high-frequency trading, and your trading application gets
preempted and has to wait &lt;strong&gt;hundreds of microseconds&lt;&#x2F;strong&gt; to execute.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;switch-it-off&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#switch-it-off&quot; aria-label=&quot;Anchor link for: switch-it-off&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
Switch it off!!&lt;&#x2F;h2&gt;
&lt;p&gt;You can use a tool like &lt;code&gt;chrt&lt;&#x2F;code&gt; to change how your running process is treated.
For example, maybe you set its priority very high, in FIFO mode where it runs
until it voluntarily yields, like so:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;chrt --pid --fifo 99 &amp;lt;PID&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;small&gt;You can also check the current scheduling attributes with &lt;code&gt;chrt -p &amp;lt;PID&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;small&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This will reduce the worst-case kernel latency but &lt;strong&gt;doesn’t get rid of all
sources of jitter.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;There are still things that can preempt the task. Here’s a few:&lt;&#x2F;p&gt;
&lt;h3 id=&quot;hard-interrupts-irqs&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#hard-interrupts-irqs&quot; aria-label=&quot;Anchor link for: hard-interrupts-irqs&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
Hard interrupts (IRQs)&lt;&#x2F;h3&gt;
&lt;p&gt;These are signals from hardware - network cards, disk controllers, or system
timers. If your application runs on the same core that is handling interrupts,
the CPU will pause your app to run the handler.&lt;&#x2F;p&gt;
&lt;p&gt;You can examine the interrupts on an existing machine by inspecting
&lt;code&gt;&#x2F;proc&#x2F;interrupts&lt;&#x2F;code&gt;. This will show you devices and their IRQ number.&lt;&#x2F;p&gt;
&lt;p&gt;By default, all cores can service interrupts.&lt;&#x2F;p&gt;
&lt;blockquote class=&quot;tip&quot;&gt;
	&lt;p class=&quot;alert-title&quot;&gt;
		&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;Tip&lt;&#x2F;p&gt;
	&lt;p&gt;Non-essential hardware interrupts can be moved to another core or cores with
interrupt affinity (&lt;code&gt;&#x2F;proc&#x2F;irq&#x2F;&amp;lt;IRQ_NUMBER&amp;gt;&#x2F;smp_affinity&lt;&#x2F;code&gt;). See &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.redhat.com&#x2F;en&#x2F;documentation&#x2F;red_hat_enterprise_linux&#x2F;6&#x2F;html&#x2F;performance_tuning_guide&#x2F;s-cpu-irq&quot;&gt;Interrupts and IRQ
Tuning&lt;&#x2F;a&gt;
in the Red Hat tuning guide&lt;&#x2F;p&gt;

&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;non-maskable-interrupts-nmi&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#non-maskable-interrupts-nmi&quot; aria-label=&quot;Anchor link for: non-maskable-interrupts-nmi&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
Non-maskable interrupts (NMI)&lt;&#x2F;h3&gt;
&lt;p&gt;An NMI is another hardware interrupt which the CPU cannot ignore or disable.
These come from critical events like fatal hardware problems, memory errors,
power failures, and watchdog timer timeouts.&lt;&#x2F;p&gt;
&lt;p&gt;The watchdog is a daemon that keeps track of &#x2F;dev&#x2F;watchdog, which contains a
heartbeat from a hardware timer on the motherboard. This is to detect CPU lockups.&lt;&#x2F;p&gt;
&lt;p&gt;This can be and usually is disabled in HFT environments as far as I’m aware.
This supposedly the most frequent cause of this type of interrupt. The other
NMIs cannot be disabled and usually result in a kernel panic or system restart.&lt;&#x2F;p&gt;
&lt;blockquote class=&quot;tip&quot;&gt;
	&lt;p class=&quot;alert-title&quot;&gt;
		&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;Tip&lt;&#x2F;p&gt;
	&lt;p&gt;You can disable the watchdog while the system is running by setting the sysctl
&lt;code&gt;kernel.nmi_watchdog&lt;&#x2F;code&gt; to &lt;code&gt;0&lt;&#x2F;code&gt;, or you can edit your bootloader such as GRUB with
&lt;code&gt;nmi_watchdog=0&lt;&#x2F;code&gt; at the end of the line that starts with
&lt;code&gt;GRUB_CMDLINE_LINUX_DEFAULT&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Occasionally it can show up in BIOS as “IPMI Watchdog Timer” which can be disabled.&lt;&#x2F;p&gt;

&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;system-management-interrupts-smi&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#system-management-interrupts-smi&quot; aria-label=&quot;Anchor link for: system-management-interrupts-smi&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
System Management Interrupts (SMI)&lt;&#x2F;h3&gt;
&lt;p&gt;This is also technically a non-maskable hardware interrupt, but it originates
from the BIOS or EFI and pauses the entire OS. They’re particularly insidious
because they cannot be detected via any standard linux observability.&lt;&#x2F;p&gt;
&lt;p&gt;These interrupts occur when the motherboard needs to perform some kind of
thermal throttling or power management, and can take in excess of 100
microseconds to complete.&lt;&#x2F;p&gt;
&lt;blockquote class=&quot;tip&quot;&gt;
	&lt;p class=&quot;alert-title&quot;&gt;
		&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;Tip&lt;&#x2F;p&gt;
	&lt;p&gt;There is nothing you can configure in the OS to prevent this, but there may be
BIOS options like disabling “Global SMI” or “C-States”, and these options may
only be present in server hardware that has been specifically optimized for HFT
by the vendor.&lt;&#x2F;p&gt;

&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;software-faults&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#software-faults&quot; aria-label=&quot;Anchor link for: software-faults&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
Software faults&lt;&#x2F;h3&gt;
&lt;p&gt;If the trading app tries to access memory and encounters a page fault or a TLB
miss, the kernel needs to take over and fetch the page.&lt;&#x2F;p&gt;
&lt;blockquote class=&quot;tip&quot;&gt;
	&lt;p class=&quot;alert-title&quot;&gt;
		&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;Tip&lt;&#x2F;p&gt;
	&lt;p&gt;Use huge-pages and pre-fault all pages. I’ll cover this more in a future
article about the TLB specifically.&lt;&#x2F;p&gt;

&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;validation&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#validation&quot; aria-label=&quot;Anchor link for: validation&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
Validation&lt;&#x2F;h2&gt;
&lt;p&gt;There are some tools out there which can be used to run tasks and measure interference, such as:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;cyclictest&lt;&#x2F;code&gt; can be used to measure kernel scheduling latency&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;hwlatdetect&lt;&#x2F;code&gt; can be used to measure latency caused by NMIs&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;The systems we use daily work seamlessly for us, but under the surface they’re
executing sophisticated multitasking routines to give every process a fair
chance at execution, with low-level hardware events stepping in and taking
priority to avoid abrupt system failures.&lt;&#x2F;p&gt;
&lt;p&gt;In a trading environment, without knowledge of these interactions, your
application would be subject to all kinds of jitter, causing you to miss a
“fill” or to get sniped on a fast-moving order book…&lt;&#x2F;p&gt;
&lt;p&gt;… and that can be the difference between profit and poverty.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Profit or Poverty: NUMA</title>
        <published>2026-04-17T00:00:00+00:00</published>
        <updated>2026-04-17T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Vasilios Syrakis
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://cetanu.github.io/blog/why-numa-is-important-in-trading/"/>
        <id>https://cetanu.github.io/blog/why-numa-is-important-in-trading/</id>
        
        <content type="html" xml:base="https://cetanu.github.io/blog/why-numa-is-important-in-trading/">&lt;p&gt;While modern software tries to hide hardware complexity behind abstractions,
staff working in HFT must dig down to ensure that every nanosecond is spent
executing trading logic, not chewed up by operating system overheads or noisy
neighbouring processes.&lt;&#x2F;p&gt;
&lt;p&gt;Learning the ins and outs of NUMA is not a micro-optimization, it is a requirement.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;short-history-of-numa&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#short-history-of-numa&quot; aria-label=&quot;Anchor link for: short-history-of-numa&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
Short history of NUMA&lt;&#x2F;h3&gt;
&lt;p&gt;Back in the day, CPUs were slower than memory. In the 1960s, processors began
to overtake memory in speed and as a result found themselves stuck waiting for
data to arrive from a memory access.&lt;&#x2F;p&gt;
&lt;p&gt;This was solved by either avoiding memory access, or by adding cache memory
some of which you may have heard of like L1 and L2 cache.&lt;&#x2F;p&gt;
&lt;p&gt;However, as operating systems and applications have grown, these caches are no
longer as effective. Additionally, on modern servers with multiple CPU sockets, only
one processor can access the memory at one time, causing the other to wait.&lt;&#x2F;p&gt;
&lt;p&gt;Non-uniform memory access (NUMA) was created to solve this problem by providing
separate memory to each socket.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;why-numa-must-be-considered-in-a-high-frequency-trading-hft-environment&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#why-numa-must-be-considered-in-a-high-frequency-trading-hft-environment&quot; aria-label=&quot;Anchor link for: why-numa-must-be-considered-in-a-high-frequency-trading-hft-environment&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
Why NUMA must be considered in a high-frequency trading (HFT) environment&lt;&#x2F;h3&gt;
&lt;p&gt;In HFT, nanoseconds matter. If your application has to wait an extra amount of
nanoseconds every time it needs to access some memory, it quickly adds up into
microseconds and perhaps even milliseconds.&lt;&#x2F;p&gt;
&lt;p&gt;On hardware with NUMA, data could reside in non-local memory aka the memory of
the other socket on the server board. For the local core to access this memory
it has to traverse an interconnect which can take 100-200 nanoseconds.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;how-to-protect-your-application-from-the-downsides-of-numa&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#how-to-protect-your-application-from-the-downsides-of-numa&quot; aria-label=&quot;Anchor link for: how-to-protect-your-application-from-the-downsides-of-numa&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
How to protect your application from the downsides of NUMA&lt;&#x2F;h3&gt;
&lt;p&gt;Firstly, on Linux, NUMA is modelled as nodes. For example, Node 0 is Socket 0
plus its attached memory, for however many sockets your system has.&lt;&#x2F;p&gt;
&lt;p&gt;You can configure different policies per-thread or per-allocation.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;local&lt;&#x2F;code&gt; (default) - allocate on the node where the thread is running.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;bind&lt;&#x2F;code&gt; - allocate &lt;em&gt;only&lt;&#x2F;em&gt; on specified nodes.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;preferred&lt;&#x2F;code&gt; - try a particular node first but fallback to others.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;interleave&lt;&#x2F;code&gt; - round-robin across nodes.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;In HFT you probably want to use &lt;code&gt;bind&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;discover-your-topology&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#discover-your-topology&quot; aria-label=&quot;Anchor link for: discover-your-topology&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
Discover your topology&lt;&#x2F;h4&gt;
&lt;p&gt;Even on your laptop or desktop linux computer, you should be able to install
&lt;code&gt;numactl&lt;&#x2F;code&gt;, &lt;code&gt;lscpu&lt;&#x2F;code&gt;, or &lt;code&gt;lstopo&lt;&#x2F;code&gt; and look at the nodes.&lt;&#x2F;p&gt;
&lt;p&gt;This is my desktop for example, which has one socket with eight cores, and 32
GB of memory:&lt;&#x2F;p&gt;
&lt;img class=&quot;no-hover&quot;alt=&quot;CPU topography&quot;src=&quot;&amp;#x2F;img&amp;#x2F;lstopo.png&quot;&#x2F;&gt;
&lt;p&gt;On a server system with multiple cores you should see additional separate NUMA nodes.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;disable-automatic-numa-balancing&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#disable-automatic-numa-balancing&quot; aria-label=&quot;Anchor link for: disable-automatic-numa-balancing&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
Disable automatic NUMA balancing&lt;&#x2F;h4&gt;
&lt;p&gt;Automatic balancing is done by &lt;code&gt;autonuma&lt;&#x2F;code&gt; which scans processes’ memory and
migrates pages to try to optimise locality, which is fine for most cases but
can cause latency spikes that are unacceptable in a trading environment.&lt;&#x2F;p&gt;
&lt;p&gt;There are two things that must be done to disable balancing.&lt;&#x2F;p&gt;
&lt;p&gt;Adjust the following sysctl:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #A9B1D6; background-color: #1A1B26;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #0DB9D7;&quot;&gt;echo&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF9E64;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #89DDFF;&quot;&gt; &amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECE6A;&quot;&gt; &#x2F;proc&#x2F;sys&#x2F;kernel&#x2F;numa_balancing&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;and then add &lt;code&gt;numa=off&lt;&#x2F;code&gt; to the boot-loader configuration, or similar, depending
on your boot-loader.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;pin-your-application&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#pin-your-application&quot; aria-label=&quot;Anchor link for: pin-your-application&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
Pin your application&lt;&#x2F;h4&gt;
&lt;p&gt;Use &lt;code&gt;numactl --cpunodebind=&amp;lt;node&amp;gt; --membind=&amp;lt;node&amp;gt; &amp;lt;the_program&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;It should be noted that the scheduler can still move threads between cores on
that node, so in addition to this you’ll want to pin your application to the
same cores using &lt;code&gt;taskset&lt;&#x2F;code&gt; or similar, like &lt;code&gt;taskset -acp &amp;lt;cores&amp;gt; &amp;lt;pid&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;h4 id=&quot;validate-the-setup&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#validate-the-setup&quot; aria-label=&quot;Anchor link for: validate-the-setup&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
Validate the setup&lt;&#x2F;h4&gt;
&lt;p&gt;Check that your process is actually bound correctly with &lt;code&gt;numastat -p &amp;lt;pid&amp;gt;&lt;&#x2F;code&gt;.
This will print out a table detailing its memory usage for each node, where the
node should only be the one you’ve bound the process to.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;what-if-my-systems-engineers-misconfigure-numa&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-if-my-systems-engineers-misconfigure-numa&quot; aria-label=&quot;Anchor link for: what-if-my-systems-engineers-misconfigure-numa&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
What if my systems engineers misconfigure NUMA?&lt;&#x2F;h3&gt;
&lt;p&gt;If you’re a developer in a trading firm and you’ve deployed your application,
you can still check NUMA at runtime and ensure your allocations land on the
correct node. The library &lt;code&gt;libnuma&lt;&#x2F;code&gt; has an API that allows you to check if NUMA
is available, how many nodes there are, which node a given core belongs to, and
the ability to allocate on a particular node.&lt;&#x2F;p&gt;
&lt;p&gt;In reality, any competent linux&#x2F;production&#x2F;systems&#x2F;sre team in a HFT should
already be working with their developers to ensure everything it set up
correctly.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
Conclusion&lt;&#x2F;h3&gt;
&lt;p&gt;In the overwhelming majority of environments NUMA goes by unnoticed, with the
defaults being suitable for most workloads. However in trading, if you ignore
NUMA, the results can be catastrophic. Microseconds of latency across millions
of memory accesses can be the difference between profit and poverty.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>A Philosophy of Software Design</title>
        <published>2026-03-25T00:00:00+00:00</published>
        <updated>2026-03-25T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Vasilios Syrakis
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://cetanu.github.io/blog/a-philosophy-of-software-design/"/>
        <id>https://cetanu.github.io/blog/a-philosophy-of-software-design/</id>
        
        <content type="html" xml:base="https://cetanu.github.io/blog/a-philosophy-of-software-design/">&lt;p&gt;Today I finished reading the book, “A Philosophy of Software Design”, by John
Outsterhout, a professor at Stanford University.&lt;&#x2F;p&gt;
&lt;p&gt;I wanted to write a little about what I learnt from the book, plus how I see it
mapping to my experience in my work where I’ve dealt with software and choices
around design.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;complexity-is-incremental&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#complexity-is-incremental&quot; aria-label=&quot;Anchor link for: complexity-is-incremental&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
Complexity is incremental&lt;&#x2F;h3&gt;
&lt;p&gt;This is probably obvious to anyone who has worked on software in any serious
capacity, but (unless you’re vibe-coding) complexity doesn’t just show up in a
single pull-request.&lt;&#x2F;p&gt;
&lt;p&gt;It’s the “Boiling Frog” problem, except you’re not the frog, you’re the chef…&lt;br &#x2F;&gt;
If we increase the temperature by just one degree, surely it’s not that bad,
right?&lt;br &#x2F;&gt;
This is how we justify adding complexity to ourselves when we say “it works for
now, I’ll clean it up later” on a pull-request.&lt;&#x2F;p&gt;
&lt;p&gt;The incremental quality of complexity is what makes it so insidious, because
it’s so easy to trade a small bit of complexity to get something delivered. You
can even continue to make that trade-off, again and again, for years. Who cares
if an abstraction leaks a little bit, as long as I can fix up this edge case
right now?&lt;&#x2F;p&gt;
&lt;p&gt;It’s hard to pinpoint the exact moment when things become sluggish, hard to
change, prone to errors, and difficult for new engineers to understand. That’s
because there’s no exact moment.&lt;br &#x2F;&gt;
Just like how a frog doesn’t suddenly start boiling when the water goes from
99°C to 100°C.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;it-s-shared-discipline-not-a-technical-challenge&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#it-s-shared-discipline-not-a-technical-challenge&quot; aria-label=&quot;Anchor link for: it-s-shared-discipline-not-a-technical-challenge&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
It’s shared discipline, not a technical challenge&lt;&#x2F;h4&gt;
&lt;p&gt;I might be totally wrong on this, but I get a general sense that when people
think about complexity, the focus is on technical decisions that get made over
time. It almost implies that you can solve complexity all by yourself if you
just work hard enough.&lt;&#x2F;p&gt;
&lt;p&gt;We work with other people. I believe it’s more pragmatic to shift this
perception so that the focus is on person-to-person interactions - which I
assert is where the problem arises.&lt;&#x2F;p&gt;
&lt;p&gt;When a reviewer fails to stand their ground, complexity is welcomed in. This
can be tricky, because often you’re standing against a peer who you trust, who
means well, but who might be too busy to spend the extra time to come up with a
design that doesn’t attach one more tiny anchor to the codebase.&lt;&#x2F;p&gt;
&lt;p&gt;I comfort myself by thinking that nobody intentionally adds complexity. They
can’t see a better way, or maybe they didn’t ask themselves if there could be a
better design. Thinking takes energy and I don’t blame anyone for taking the
path of least resistance to get their work done. Perhaps what we need is better
ways to measure complexity, so that we can reward its reduction.&lt;&#x2F;p&gt;
&lt;p&gt;For people working solo, you are both the author and reviewer. If you aren’t
trying to see your own work from a different perspective, then your “reviewer”
persona is effectively rubber-stamping the work, shrugging, and walking away.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;abstractions-are-about-hiding-unnecessary-details&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#abstractions-are-about-hiding-unnecessary-details&quot; aria-label=&quot;Anchor link for: abstractions-are-about-hiding-unnecessary-details&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
Abstractions are about hiding unnecessary details&lt;&#x2F;h3&gt;
&lt;p&gt;This one probably sounds like common sense. Duh! That’s what an abstraction is.&lt;&#x2F;p&gt;
&lt;p&gt;However, for me, I think it’s valuable to consider that you and I sometimes
make abstractions that accidentally hide &lt;em&gt;necessary&lt;&#x2F;em&gt; details.&lt;&#x2F;p&gt;
&lt;p&gt;This is one of those things where, if you zoom out a little bit, makes software
engineering seem like such a challenging field. The subjectivity of this -
where to draw the boundaries of an abstraction - highlights a constant
cognitive requirement towards making a balanced decision.&lt;&#x2F;p&gt;
&lt;p&gt;You can’t open a book and have it tell you how to make the right abstraction.
You just have to fail, walk into a mess, learn from the mess, and make the next
project better, only to fall into another mess. A mess that grows as it feeds
on entropy and junior developers.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;deep-modules-are-simpler-than-shallow-modules&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#deep-modules-are-simpler-than-shallow-modules&quot; aria-label=&quot;Anchor link for: deep-modules-are-simpler-than-shallow-modules&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
Deep modules are simpler than shallow modules&lt;&#x2F;h3&gt;
&lt;p&gt;First, what does this even mean? What is a deep module?&lt;br &#x2F;&gt;
A module is deep when its implementation is large compared to its interface.&lt;&#x2F;p&gt;
&lt;p&gt;That means that you could have a module that is long (in terms of lines of
code), but if every function in that module is exported, the interface is
large.&lt;&#x2F;p&gt;
&lt;p&gt;Instead, a deep module would be something with perhaps a few key functions or
classes exported, which encapsulate the implementation behind a concise
interface.&lt;&#x2F;p&gt;
&lt;p&gt;This was something that surprised me at first but makes total sense.
If make a library, I don’t want the caller to have to know about 20 different
functions or methods that they need to call, especially if most of the time all
of the arguments passed in are very similar.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;general-purpose-code-is-simpler-than-special-cased-code&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#general-purpose-code-is-simpler-than-special-cased-code&quot; aria-label=&quot;Anchor link for: general-purpose-code-is-simpler-than-special-cased-code&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
General-purpose code is simpler than special-cased code&lt;&#x2F;h3&gt;
&lt;p&gt;What exactly is the difference between the two?&lt;br &#x2F;&gt;
General purpose code solve a class of problems. Special case code solves one problem.&lt;&#x2F;p&gt;
&lt;p&gt;Example:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Special case: A function that adds the &lt;code&gt;Authorization&lt;&#x2F;code&gt; request header with a particular value&lt;&#x2F;li&gt;
&lt;li&gt;General purpose: A function that adds a request header, with a given key and value.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Special cases pop up in codebases after their initial design has been
implemented, a bunch of users have been onboarded, it’s operating fine, but one
particular (and important) user needs it to do something that wasn’t in the
original design. This is expected in software, and rigidity isn’t a good
response.&lt;&#x2F;p&gt;
&lt;p&gt;If you immediately know how to change the system so that their particular need
is solved as a general case it’s a clear win, but this doesn’t seem to be how
things go in most cases. Instead, it takes painful thinking to determine what
needs to be rearranged, migrated, and mutated, in order for the special case to
be handled in such a way that it doesn’t require someone to carve out time from
their day to maintain it.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;philosophy-not-science&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#philosophy-not-science&quot; aria-label=&quot;Anchor link for: philosophy-not-science&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
Philosophy, not Science&lt;&#x2F;h2&gt;
&lt;p&gt;There is a particular theme that seems to be common across the above concepts.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;What makes a good abstraction?&lt;&#x2F;li&gt;
&lt;li&gt;How deep should a module be?&lt;&#x2F;li&gt;
&lt;li&gt;How general should my code be?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;There’s no formula. A book can’t teach it to you. A committee can’t develop a standard for it.&lt;&#x2F;p&gt;
&lt;p&gt;The fundamental question being wrestled with is “Where do I draw the boundary?”
and the answer is different from wherever you are standing.&lt;&#x2F;p&gt;
&lt;p&gt;It’s the human factors that help answer this question. How much cognitive load
can the user or reader bear? How often will I have to change the codebase? A
compiler or linter can’t answer these questions. It requires a person to draw a
subjective line in the sand and then defend it.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-element-of-taste&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-element-of-taste&quot; aria-label=&quot;Anchor link for: the-element-of-taste&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
The element of “Taste”&lt;&#x2F;h3&gt;
&lt;p&gt;I hear about how the future of software will be influenced by having good
“taste” now that LLMs are writing all the code (I don’t actually believe this).&lt;br &#x2F;&gt;
If that’s true then I sincerely hope that all the people that intend to map
their taste into software learn good software design first.&lt;&#x2F;p&gt;
&lt;p&gt;Ironically, the way to learn good software design is through experience. You
have to ship something and collect the feedback. Sometimes that feedback is
customers telling you what causes friction. Other times it shows up in the
volume of questions directed at a particular feature… at 3AM after your site
blows up.&lt;&#x2F;p&gt;
&lt;p&gt;While reading the book you get a sense that it’s never going to answer this
question, “What’s a good abstraction?” because it can’t. The professor
encourages people to find the right balance for themselves, and it introduces
distinctions that might enable them to do so.&lt;&#x2F;p&gt;
&lt;p&gt;You might be creating a good design if the abstraction aligns with a
business domain, or when you provide a neat general-purpose interface across an
entire system. You might be creating a bad design when you add logic that
purely addresses an immediate need and nothing else.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;my-closing-thought&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#my-closing-thought&quot; aria-label=&quot;Anchor link for: my-closing-thought&quot;&gt;&lt;i class=&quot;icon&quot;&gt;&lt;&#x2F;i&gt;&lt;&#x2F;a&gt;
My closing thought&lt;&#x2F;h2&gt;
&lt;p&gt;Writing this page made me realise that these problems that I found
unapproachable - how to decide where to draw the line - are simply the way
things are. It’s the essence of design.&lt;&#x2F;p&gt;
&lt;p&gt;Just like other art forms, you can practice, fail, learn, and improve, but
there will always be some people that either love or hate the art that you
created.&lt;&#x2F;p&gt;
&lt;p&gt;Often that person is your own self. The inner-critic looks at the past and is
ashamed of mistakes. I choose to forgive myself for the imperfections and focus
on the parts that I’m proud of.&lt;&#x2F;p&gt;
</content>
        
    </entry>
</feed>
