<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>Sergey&#39;s Blog</title>
    <link rel="self" type="application/atom+xml" href="https://polovko.me/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://polovko.me"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-08-22T00:00:00+00:00</updated>
    <id>https://polovko.me/atom.xml</id>
    <entry xml:lang="en">
        <title>Git at any scale</title>
        <published>2026-08-22T00:00:00+00:00</published>
        <updated>2026-08-22T00:00:00+00:00</updated>
        
        <author>
          <name>Sergey Polovko</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://polovko.me/pins/git-at-any-scale/"/>
        <id>https://polovko.me/pins/git-at-any-scale/</id>
        
        <content type="html" xml:base="https://polovko.me/pins/git-at-any-scale/">&lt;p&gt;A post from Cursor about Continuity, the Git storage system they built to host
repositories at real scale. Instead of treating each repo as one precious copy
on one disk, every push goes to a write ahead log in S3 first, and the client
only hears back once that write is durable. Local disks become warm caches that
can be rebuilt at any time, and replicas stay in sync with gossip over UDP plus
conditional reads against S3. The post also covers repacking without making
every replica redo the same expensive work, and gives real numbers: about 120
pushes a second on standard S3 and over 300 on S3 Express One Zone.&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Rendezvous hashing</title>
        <published>2026-08-22T00:00:00+00:00</published>
        <updated>2026-08-22T00:00:00+00:00</updated>
        
        <author>
          <name>Sergey Polovko</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://polovko.me/pins/rendezvous-hashing/"/>
        <id>https://polovko.me/pins/rendezvous-hashing/</id>
        
        <content type="html" xml:base="https://polovko.me/pins/rendezvous-hashing/">&lt;p&gt;An overview of rendezvous hashing, a simple way to assign objects to servers
without a central coordinator. Each server gets a score from the object
and server names, and the highest score wins. Every client reaches the same
result, and adding or removing a server moves only the objects affected by that
change. The article also compares the method with consistent hashing and covers
replication, weights, and faster variants.&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>The when, why and how of waiting and backoff in multi-threaded applications on Arm</title>
        <published>2026-08-08T00:00:00+00:00</published>
        <updated>2026-08-08T00:00:00+00:00</updated>
        
        <author>
          <name>Sergey Polovko</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://polovko.me/pins/the-when-why-and-how-of-waiting-and-backoff-in-multi-threaded-applications-on-arm/"/>
        <id>https://polovko.me/pins/the-when-why-and-how-of-waiting-and-backoff-in-multi-threaded-applications-on-arm/</id>
        
        <content type="html" xml:base="https://polovko.me/pins/the-when-why-and-how-of-waiting-and-backoff-in-multi-threaded-applications-on-arm/">&lt;p&gt;An Arm post on what a thread should do while it waits, whether on a lock or
after a failed atomic. Spinning in a tight loop is the obvious move, but it
floods memory with traffic and slows every other core touching the same
location, so backing off helps both throughput and fairness. The post covers
backoff strategies that space out the checks, and the Arm specific tools for
them: the counter timer for timed waits, the WFET instruction on Armv8.7 and
later that lets a core sleep for a set duration instead of burning power, and
barriers like ISB and SB that control how far ahead the processor looks. It also
lists patterns that look correct but are not, such as empty loops and simple
LDXR plus WFE combinations, and explains why they fall apart as thread counts
grow.&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>uringscope: Portable, Low-Overhead Observability for io_uring</title>
        <published>2026-08-08T00:00:00+00:00</published>
        <updated>2026-08-08T00:00:00+00:00</updated>
        
        <author>
          <name>Sergey Polovko</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://polovko.me/pins/uringscope-portable-low-overhead-observability-for-io-uring/"/>
        <id>https://polovko.me/pins/uringscope-portable-low-overhead-observability-for-io-uring/</id>
        
        <content type="html" xml:base="https://polovko.me/pins/uringscope-portable-low-overhead-observability-for-io-uring/">&lt;p&gt;A paper on why io_uring workloads are so hard to see into. Submission and
completion happen in rings shared with the kernel, so strace catches only the
setup call and you are left guessing when something goes wrong. uringscope is a
single binary eBPF tool that watches those rings and rebuilds the life of each
request from raw kernel events, using CO-RE, BTF probes, and flexible field
lookups so one build survives the tracepoint churn across kernel versions. On
real NVMe workloads it costs about 0.7 to 9.9 percent of throughput, cheaper
than the other tools measured at the same level of detail. The same data feeds a
doctor mode that turns raw measurements into named problems with the evidence
behind them, aimed at someone chasing a tail latency bug rather than browsing
histograms.&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>DuckDB Internals: Why is DuckDB Fast?</title>
        <published>2026-07-26T00:00:00+00:00</published>
        <updated>2026-07-26T00:00:00+00:00</updated>
        
        <author>
          <name>Sergey Polovko</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://polovko.me/pins/duckdb-internals-why-is-duckdb-fast-part-1/"/>
        <id>https://polovko.me/pins/duckdb-internals-why-is-duckdb-fast-part-1/</id>
        
        <content type="html" xml:base="https://polovko.me/pins/duckdb-internals-why-is-duckdb-fast-part-1/">&lt;p&gt;A walk through why DuckDB, an in process analytical database, runs SQL so fast.
It follows a query from parsing to execution and shows how each step avoids
extra work: running as a library instead of a server cuts out network and
serialization overhead, the optimizer pushes filters down and picks join order
with dynamic programming, and columnar storage with zone maps lets whole chunks
of data be skipped when they cannot match a filter. It also explains how a query
is split into pipelines so the work can run in parallel across threads, each
with its own local state. The result is a full picture of the system rather than
a list of buzzwords.&lt;/p&gt;
&lt;p&gt;There is also a &lt;a rel=&quot;external&quot; href=&quot;https://www.greybeam.ai/blog/duckdb-internals-part-2&quot;&gt;second part&lt;/a&gt;
from the series which covers how those plans actually run: vectorized execution
in batches of 2048 rows, selection vectors that filter without copying, and a
push based model that spreads work across CPU cores.&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Offloading I/O to Dedicated Cores: An Asymmetric io_uring Backend for Seastar and ScyllaDB</title>
        <published>2026-07-23T00:00:00+00:00</published>
        <updated>2026-07-23T00:00:00+00:00</updated>
        
        <author>
          <name>Sergey Polovko</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://polovko.me/pins/offloading-i-o-to-dedicated-cores-an-asymmetric-io-uring-backend-for-seastar-and-scylladb/"/>
        <id>https://polovko.me/pins/offloading-i-o-to-dedicated-cores-an-asymmetric-io-uring-backend-for-seastar-and-scylladb/</id>
        
        <content type="html" xml:base="https://polovko.me/pins/offloading-i-o-to-dedicated-cores-an-asymmetric-io-uring-backend-for-seastar-and-scylladb/">&lt;p&gt;A ScyllaDB engineering post about a new io_uring backend for Seastar that breaks
the usual shared nothing rule, where every core does its own I/O and compute.
The asymmetric backend instead sets aside a few cores as dedicated networking
workers while the rest run only application logic, and routes I/O syscalls to
those workers through io_uring queues. To make it work the team had to remove a
speculative fast path that let a shard skip io_uring and issue a plain syscall
on its own core, since that shortcut defeats the point of offloading. The
numbers are honest about the tradeoff: raw I/O throughput trails the older linux
aio backend, but compute shards get back the CPU time they used to spend on
sockets and disk calls.&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Aurora DSQL: Scalable, Multi-Region OLTP</title>
        <published>2026-07-18T00:00:00+00:00</published>
        <updated>2026-07-18T00:00:00+00:00</updated>
        
        <author>
          <name>Sergey Polovko</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://polovko.me/pins/aurora-dsql-scalable-multi-region-oltp-2/"/>
        <id>https://polovko.me/pins/aurora-dsql-scalable-multi-region-oltp-2/</id>
        
        <content type="html" xml:base="https://polovko.me/pins/aurora-dsql-scalable-multi-region-oltp-2/">&lt;p&gt;A paper from Amazon engineers on how Aurora DSQL works inside. DSQL is a
serverless SQL database that runs active active across regions, so any region
can take reads and writes at once. The design splits the two apart: reads use
multiversion concurrency control with precise timestamps and never coordinate
with other nodes, while writes use optimistic concurrency control and coordinate
only at commit time, through components called adjudicators and a replication
layer called the Journal. Query processors run in small Firecracker microVMs and
keep no local state, which lets compute, storage, and coordination scale on
their own, from idle up to millions of transactions per second. The paper shows
how all of this keeps full ACID transactions even when an availability zone or a
whole region fails.&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Introducing Meerkat: an experiment in global consensus</title>
        <published>2026-07-11T00:00:00+00:00</published>
        <updated>2026-07-11T00:00:00+00:00</updated>
        
        <author>
          <name>Sergey Polovko</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://polovko.me/pins/introducing-meerkat-an-experiment-in-global-consensus/"/>
        <id>https://polovko.me/pins/introducing-meerkat-an-experiment-in-global-consensus/</id>
        
        <content type="html" xml:base="https://polovko.me/pins/introducing-meerkat-an-experiment-in-global-consensus/">&lt;p&gt;A Cloudflare post about Meerkat, a consensus system they built to keep control
plane state consistent across their 330 plus data centers. It runs on QuePaxa,
an algorithm that needs no leader. In Raft a dead leader or a slow network
stalls writes until a new one is elected, and the timeouts are hard to tune
across the wide area internet. QuePaxa instead lets any replica propose a write
at any time, and concurrent proposals help each other reach agreement rather
than block each other. The post is honest about the cost, since each write still
takes one to three round trips, so Meerkat suits data that changes rarely and
must stay correct rather than a busy database. It is also the first time QuePaxa
has run at production scale.&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>How /dev/null and Friends Work Inside the Linux Kernel</title>
        <published>2026-03-08T00:00:00+00:00</published>
        <updated>2026-03-08T00:00:00+00:00</updated>
        
        <author>
          <name>Sergey Polovko</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://polovko.me/blog/how-dev-null-works-linux-kernel/"/>
        <id>https://polovko.me/blog/how-dev-null-works-linux-kernel/</id>
        
        <summary type="html">&lt;p&gt;If you list all device files with major number 1 on a Linux machine, you&#39;ll see something like this:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #24292E; background-color: #FFFFFF;&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ ls -la /dev/ | grep &amp;#39; 1, &amp;#39;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;crw-rw-rw-  1 root root      1,     7 Jan 18 20:01 full&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;crw-r--r--  1 root root      1,    11 Jan 18 20:01 kmsg&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;crw-r-----  1 root kmem      1,     1 Jan 18 20:01 mem&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;crw-rw-rw-  1 root root      1,     3 Jan 18 20:01 null&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;crw-r-----  1 root kmem      1,     4 Jan 18 20:01 port&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;crw-rw-rw-  1 root root      1,     8 Jan 18 20:01 random&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;crw-rw-rw-  1 root root      1,     9 Jan 18 20:01 urandom&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;crw-rw-rw-  1 root root      1,     5 Jan 18 20:01 zero&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Every Linux system has them. You&#39;ve used &lt;code&gt;/dev/null&lt;/code&gt; a thousand times. You&#39;ve probably read from &lt;code&gt;/dev/urandom&lt;/code&gt; or &lt;code&gt;/dev/random&lt;/code&gt;. But have you ever looked at what &lt;em&gt;backs&lt;/em&gt; these files inside the kernel?&lt;/p&gt;
&lt;p&gt;What makes these eight devices special is that they are among the simplest real drivers in Linux. There&#39;s no hardware to initialize, no DMA buffers to manage, no firmware to load. Just pure logic: &quot;when userspace calls &lt;code&gt;read()&lt;/code&gt;, do &lt;em&gt;this&lt;/em&gt;.&quot; That makes them the perfect starting point for understanding how &lt;em&gt;any&lt;/em&gt; Linux character device driver works.&lt;/p&gt;
&lt;p&gt;We&#39;ll walk through their implementations line by line. Along the way, you&#39;ll see the interface every character device must conform to, the patterns that the kernel uses to dispatch I/O to the right handler, and the design choices that separate a minimal driver from a production-ready one. By the end, you&#39;ll have a concrete blueprint for writing your own.&lt;/p&gt;
&lt;p&gt;All the code in this post comes from &lt;strong&gt;Linux 6.19&lt;/strong&gt; (&lt;a rel=&quot;external&quot; href=&quot;https://elixir.bootlin.com/linux/v6.19/source/drivers/char/mem.c&quot;&gt;&lt;code&gt;drivers/char/mem.c&lt;/code&gt;&lt;/a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https://elixir.bootlin.com/linux/v6.19/source/drivers/char/random.c&quot;&gt;&lt;code&gt;drivers/char/random.c&lt;/code&gt;&lt;/a&gt;). Let&#39;s start with &lt;code&gt;open(&quot;/dev/null&quot;, ...)&lt;/code&gt; and trace it all the way down to the two-line C function that makes it work.&lt;/p&gt;</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>Memory Barriers: a Hardware View for Software Hackers</title>
        <published>2025-03-21T00:00:00+00:00</published>
        <updated>2025-03-21T00:00:00+00:00</updated>
        
        <author>
          <name>Sergey Polovko</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://polovko.me/pins/memory-barriers-a-hardware-view-for-software-hackers/"/>
        <id>https://polovko.me/pins/memory-barriers-a-hardware-view-for-software-hackers/</id>
        
        <content type="html" xml:base="https://polovko.me/pins/memory-barriers-a-hardware-view-for-software-hackers/">&lt;p&gt;Paul McKenney&#39;s paper on why memory barriers exist at all, built from the
hardware up. It starts with how a CPU cache is laid out, then how the MESI
protocol keeps caches agreeing on the value of each location, then how store
buffers and invalidate queues quietly break that agreement in exchange for
speed. Once you see those two queues, read and write barriers stop looking
arbitrary and start looking like the obvious fix.&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>What Every Programmer Should Know About Memory</title>
        <published>2025-03-20T00:00:00+00:00</published>
        <updated>2025-03-20T00:00:00+00:00</updated>
        
        <author>
          <name>Sergey Polovko</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://polovko.me/pins/what-every-programmer-should-know-about-memory/"/>
        <id>https://polovko.me/pins/what-every-programmer-should-know-about-memory/</id>
        
        <content type="html" xml:base="https://polovko.me/pins/what-every-programmer-should-know-about-memory/">&lt;p&gt;Ulrich Drepper&#39;s long guide from 2007 on how memory hardware really works and
why memory, not the CPU, is often what makes a program slow. It covers how RAM
chips work, how CPU caches are built and why they exist, and how virtual memory
and NUMA change the picture, then spends a large middle section on concrete
advice for writing code that uses caches well. There are plenty of diagrams and
real numbers measured on real hardware, along with practical topics like data
layout, cache line size, and tools that help you find memory related slowdowns.
Some of the hardware details have aged, but the core ideas still hold up.&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>A Decade of Dynamo: Powering the next wave of high-performance apps</title>
        <published>2025-01-15T00:00:00+00:00</published>
        <updated>2025-01-15T00:00:00+00:00</updated>
        
        <author>
          <name>Sergey Polovko</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://polovko.me/pins/a-decade-of-dynamo/"/>
        <id>https://polovko.me/pins/a-decade-of-dynamo/</id>
        
        <content type="html" xml:base="https://polovko.me/pins/a-decade-of-dynamo/">&lt;p&gt;A USENIX ATC paper that looks back at how DynamoDB grew from the original Dynamo
design into a managed service. It shares what the team learned from running the
system at a very large scale and shows how distributed storage works in
production, not just in theory.&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>GCRA: a simple and elegant rate-limiting algorithm</title>
        <published>2024-09-01T00:00:00+00:00</published>
        <updated>2024-09-01T00:00:00+00:00</updated>
        
        <author>
          <name>Sergey Polovko</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://polovko.me/pins/gcra/"/>
        <id>https://polovko.me/pins/gcra/</id>
        
        <content type="html" xml:base="https://polovko.me/pins/gcra/">&lt;p&gt;A clear guide to the Generic Cell Rate Algorithm (GCRA), which is used for leaky
bucket rate limiting. Instead of tracking a counter and refilling it on a timer,
GCRA stores one timestamp and uses a simple calculation to decide whether to
allow a request. The post explains the idea step by step and shows why it needs
little memory and is easy to build.&lt;/p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Introduction to JIT Compilation</title>
        <published>2012-10-04T00:00:00+00:00</published>
        <updated>2012-10-04T00:00:00+00:00</updated>
        
        <author>
          <name>Sergey Polovko</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://polovko.me/blog/introduction-in-jit-compilation/"/>
        <id>https://polovko.me/blog/introduction-in-jit-compilation/</id>
        
        <summary type="html">&lt;p&gt;The Java HotSpot VM (which Oracle acquired after purchasing Sun Microsystems) forms the basis for both the Java Virtual Machine (JVM) and OpenJDK (an open-source project). Like all Java virtual machines, the Java HotSpot VM provides the necessary environment for executing bytecode. In practice, it is responsible for three main functions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;bytecode interpretation&lt;/li&gt;
&lt;li&gt;class loading and type checking&lt;/li&gt;
&lt;li&gt;memory management&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This article focuses on bytecode interpretation, specifically the optimizations performed by the virtual machine.&amp;hellip;
&lt;/p&gt;
</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>About Sizes in Java</title>
        <published>2012-09-26T00:00:00+00:00</published>
        <updated>2012-09-26T00:00:00+00:00</updated>
        
        <author>
          <name>Sergey Polovko</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://polovko.me/blog/about-sizes-in-java/"/>
        <id>https://polovko.me/blog/about-sizes-in-java/</id>
        
        <summary type="html">&lt;p&gt;Recently, I&#39;ve been pondering the question: &quot;How can one accurately assess the amount of memory allocated for objects in Java?&quot; There are several articles on Habrahabr &lt;a rel=&quot;external&quot; href=&quot;http://habrahabr.ru/post/134102/&quot;&gt;[1]&lt;/a&gt;, &lt;a rel=&quot;external&quot; href=&quot;http://habrahabr.ru/post/134910/&quot;&gt;[2]&lt;/a&gt; dedicated to this topic. However, I wasn&#39;t quite satisfied with the approach used by the authors. Therefore, I decided to delve into the internals of the OpenJDK Hotspot VM (hereafter referred to as Hotspot) and try to understand how things really work.&lt;/p&gt;
&lt;h2 id=&quot;data-types-in-java&quot;&gt;Data Types in Java&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Primitives (byte, short, char, int, float, long, double, boolean).&lt;/li&gt;
&lt;li&gt;Objects. The size of an object depends on the specific VM implementation and the processor architecture, so a definitive answer is not possible. Nonetheless, it&#39;s interesting to explore (using a specific VM as an example) what size of memory is allocated for a Java object.&lt;/li&gt;
&lt;li&gt;Arrays. One-dimensional linear structures that can contain all listed types (including other arrays). Arrays are also objects but with a specific structure.&amp;hellip;
&lt;/li&gt;
&lt;/ul&gt;
</summary>
        
    </entry>
</feed>
