<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Saksham Kapoor</title>
    <description>The latest articles on DEV Community by Saksham Kapoor (@saksham_kapoor).</description>
    <link>https://dev.to/saksham_kapoor</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2834327%2F7ca9545c-31a0-4742-a111-d7e9974e4b4c.jpeg</url>
      <title>DEV Community: Saksham Kapoor</title>
      <link>https://dev.to/saksham_kapoor</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/saksham_kapoor"/>
    <language>en</language>
    <item>
      <title>I Built a Redis Server in Rust — and Found Where It Breaks</title>
      <dc:creator>Saksham Kapoor</dc:creator>
      <pubDate>Tue, 24 Mar 2026 11:56:58 +0000</pubDate>
      <link>https://dev.to/saksham_kapoor/i-built-a-redis-server-in-rust-and-found-where-it-breaks-3a2o</link>
      <guid>https://dev.to/saksham_kapoor/i-built-a-redis-server-in-rust-and-found-where-it-breaks-3a2o</guid>
      <description>&lt;p&gt;Most developers use Redis like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;SET key value
GET key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It feels instant. Effortless.&lt;/p&gt;

&lt;p&gt;But once you try building Redis yourself, you realize:&lt;br&gt;
    • concurrency is the real problem&lt;br&gt;
    • locks kill performance faster than logic&lt;br&gt;
    • observability itself can become a bottleneck&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;RustRedis&lt;/strong&gt; — a Redis-compatible server in Rust — to understand what actually happens under load.  ￼&lt;/p&gt;

&lt;p&gt;This wasn’t about features.&lt;/p&gt;

&lt;p&gt;It was about answering one question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Where does performance actually break under concurrency?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;🔗 Full Project&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Code + benchmarks: &lt;a href="https://github.com/Saksham932007/RustRedis" rel="noopener noreferrer"&gt;Github Link&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;1. System Design&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The server follows a task-per-connection model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client → TCP → Tokio Task → Command Execution → Shared DB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each connection:&lt;br&gt;
    • parses RESP protocol&lt;br&gt;
    • executes commands&lt;br&gt;
    • returns responses&lt;/p&gt;

&lt;p&gt;All tasks share a central database.&lt;/p&gt;

&lt;p&gt;Two implementations were tested:&lt;br&gt;
    • Mutex (global lock)&lt;br&gt;
    • DashMap (sharded locks)&lt;/p&gt;

&lt;p&gt;This allows direct comparison of locking strategies.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;2. The Real Problem: Concurrency&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;At low load, everything works fine.&lt;/p&gt;

&lt;p&gt;At high load, everything changes.&lt;/p&gt;

&lt;p&gt;The bottleneck is not:&lt;/p&gt;

&lt;p&gt;❌ parsing&lt;br&gt;
❌ networking&lt;/p&gt;

&lt;p&gt;It is:&lt;/p&gt;

&lt;p&gt;👉 shared state contention&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;3. Lock Contention (Where It Breaks)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;With a global Mutex:&lt;br&gt;
    • all writes serialize&lt;br&gt;
    • threads queue behind each other&lt;br&gt;
    • throughput collapses&lt;/p&gt;

&lt;p&gt;At high concurrency:&lt;br&gt;
    • p99 latency explodes&lt;br&gt;
    • throughput drops significantly&lt;/p&gt;

&lt;p&gt;This is called:&lt;/p&gt;

&lt;p&gt;👉 lock convoy effect&lt;/p&gt;

&lt;p&gt;Even short critical sections become slow under contention.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;4. DashMap vs Mutex&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Replacing the global lock with DashMap (sharded locking):&lt;br&gt;
    • reduces contention&lt;br&gt;
    • allows parallel writes&lt;br&gt;
    • improves throughput significantly&lt;/p&gt;

&lt;p&gt;At high concurrency:&lt;br&gt;
    • ~60% higher throughput&lt;br&gt;
    • ~40% lower latency&lt;/p&gt;

&lt;p&gt;But:&lt;/p&gt;

&lt;p&gt;👉 not free&lt;/p&gt;

&lt;p&gt;Trade-offs:&lt;br&gt;
    • more overhead per operation&lt;br&gt;
    • complexity for full-scan operations&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;5. Observability Became a Bottleneck&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This was unexpected.&lt;/p&gt;

&lt;p&gt;Tracking metrics per command introduced:&lt;/p&gt;

&lt;p&gt;👉 another shared structure&lt;/p&gt;

&lt;p&gt;Three approaches were tested:&lt;/p&gt;

&lt;p&gt;Global Mutex&lt;br&gt;
    • simple&lt;br&gt;
    • but severe contention&lt;/p&gt;

&lt;p&gt;Sharded Metrics&lt;br&gt;
    • better scalability&lt;br&gt;
    • reduced lock contention&lt;/p&gt;

&lt;p&gt;Thread-Local Batching&lt;br&gt;
    • no locks on hot path&lt;br&gt;
    • near-zero overhead&lt;/p&gt;

&lt;p&gt;Key insight:&lt;/p&gt;

&lt;p&gt;Observability can become a primary bottleneck under load.&lt;/p&gt;

&lt;p&gt;At high concurrency:&lt;br&gt;
    • telemetry alone caused ~30% performance drop&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;6. Persistence Trade-offs (AOF)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Three persistence modes:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;Behavior&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Always&lt;/td&gt;
&lt;td&gt;fsync every write&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EverySecond&lt;/td&gt;
&lt;td&gt;background flush&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;OS-managed.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Results:&lt;br&gt;
    • Always → ~80% throughput drop&lt;br&gt;
    • EverySecond → minimal overhead&lt;br&gt;
    • No → fastest but unsafe&lt;/p&gt;

&lt;p&gt;Insight:&lt;/p&gt;

&lt;p&gt;👉 durability always costs performance&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;7. Throughput Scaling&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Performance peaks early:&lt;br&gt;
    • ~10–100 clients → optimal&lt;br&gt;
    • 500+ clients → contention dominates&lt;br&gt;
    • 1000 clients → system becomes unstable&lt;/p&gt;

&lt;p&gt;Why?&lt;/p&gt;

&lt;p&gt;👉 lock contention grows non-linearly&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;8. Redis vs RustRedis&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Compared with Redis-compatible system:&lt;/p&gt;

&lt;p&gt;At low concurrency:&lt;br&gt;
    • Redis is faster (no locking overhead)&lt;/p&gt;

&lt;p&gt;At high concurrency:&lt;br&gt;
    • RustRedis shows more stable behavior&lt;br&gt;
    • lower tail latency&lt;/p&gt;

&lt;p&gt;Reason:&lt;/p&gt;

&lt;p&gt;👉 multi-threaded I/O vs single-threaded event loop&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;9. The Most Important Insight&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This project changed how I think about systems:&lt;/p&gt;

&lt;p&gt;👉 performance is not about code speed&lt;br&gt;
👉 it’s about contention management&lt;/p&gt;

&lt;p&gt;Key takeaways:&lt;br&gt;
    • shared state is the real bottleneck&lt;br&gt;
    • locks don’t scale linearly&lt;br&gt;
    • batching removes contention&lt;br&gt;
    • observability must be designed carefully&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;10. What I’d Improve Next&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;actor-based architecture (no shared state)&lt;/li&gt;
&lt;li&gt;lock-free structures&lt;/li&gt;
&lt;li&gt;better persistence batching&lt;/li&gt;
&lt;li&gt;distributed sharding&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Building a Redis-like system reveals something important:&lt;/p&gt;

&lt;p&gt;The hardest part of systems design is not correctness — it’s managing contention under load.&lt;/p&gt;

&lt;p&gt;Most systems don’t fail because they are wrong.&lt;/p&gt;

&lt;p&gt;They fail because they don’t scale under pressure.&lt;/p&gt;

</description>
      <category>systems</category>
      <category>ai</category>
      <category>programming</category>
      <category>opensource</category>
    </item>
    <item>
      <title>I Built a Key-Value Database from Scratch — Here’s What I Learned</title>
      <dc:creator>Saksham Kapoor</dc:creator>
      <pubDate>Thu, 19 Mar 2026 21:15:32 +0000</pubDate>
      <link>https://dev.to/saksham_kapoor/designing-a-log-structured-key-value-storage-engine-from-first-principles-kestrelcache-1f2m</link>
      <guid>https://dev.to/saksham_kapoor/designing-a-log-structured-key-value-storage-engine-from-first-principles-kestrelcache-1f2m</guid>
      <description>&lt;p&gt;Modern storage systems are built around a small set of fundamental principles: optimize for sequential I/O, minimize in-place mutation, and design for recoverability from failure.&lt;/p&gt;

&lt;p&gt;Systems such as Bitcask, Kafka, and LSM-based databases all embrace these ideas in different forms. To better understand these trade-offs at a systems level, I built KestrelCache — a minimal persistent key-value storage engine implemented in C# and .NET.&lt;/p&gt;

&lt;p&gt;This article focuses on the core mechanics of the storage engine, including its architecture, data layout, write/read paths, failure handling, and the practical trade-offs that emerge from a log-structured design.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;1. Problem Framing&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;At its core, a storage engine must answer a simple question:&lt;/p&gt;

&lt;p&gt;How do we store and retrieve data efficiently while preserving correctness under failure?&lt;/p&gt;

&lt;p&gt;Traditional designs rely on in-place updates, but these introduce complexity:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;random disk I/O&lt;/li&gt;
&lt;li&gt;fragmentation&lt;/li&gt;
&lt;li&gt;complicated concurrency control&lt;/li&gt;
&lt;li&gt;difficult crash recovery&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;KestrelCache instead adopts a log-structured approach, where all writes are appended to disk. This eliminates in-place mutation entirely.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;2. System Architecture&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The entire system is defined by two components:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;In-Memory Index (HashMap&amp;lt;Key, Offset&amp;gt;)
                ↓
Append-Only Log File (Disk)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The responsibilities are clearly separated:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Disk provides durability and write throughput&lt;/li&gt;
&lt;li&gt;Memory provides fast lookup&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are no secondary indexes or complex data structures. The append-only log is the source of truth.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;3. Write Path&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The write path (Put) is designed for simplicity and performance:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Serialize the key and value into bytes&lt;/li&gt;
&lt;li&gt;Compute a CRC32 checksum&lt;/li&gt;
&lt;li&gt;Append the record to the end of the log file&lt;/li&gt;
&lt;li&gt;Update the in-memory index with the new offset&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This design guarantees:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;strictly sequential disk writes&lt;/li&gt;
&lt;li&gt;no in-place updates&lt;/li&gt;
&lt;li&gt;inherent crash safety (append-only)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sequential writes are significantly more efficient than random writes, especially under sustained workloads.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;4. Read Path&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Reads (Get) avoid scanning the log entirely:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Lookup the key in the in-memory index&lt;/li&gt;
&lt;li&gt;Retrieve the corresponding file offset&lt;/li&gt;
&lt;li&gt;Seek directly to the location on disk&lt;/li&gt;
&lt;li&gt;Read and return the value&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This results in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;constant-time lookup in memory&lt;/li&gt;
&lt;li&gt;a single disk seek&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The performance of reads is therefore predictable and efficient.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;5. Delete Semantics (Tombstones)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Deletes are implemented using tombstones, rather than removing data from disk.&lt;/p&gt;

&lt;p&gt;Instead of deleting a record:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[previous value]
[tombstone record]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A tombstone is represented by a record where:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ValueSize = -1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The in-memory index removes the key, but the log retains the history.&lt;/p&gt;

&lt;p&gt;This approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;avoids rewriting files&lt;/li&gt;
&lt;li&gt;maintains write performance&lt;/li&gt;
&lt;li&gt;simplifies concurrency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, it introduces the need for periodic compaction.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;6. On-Disk Record Format&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Each record is stored in a compact binary format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[CRC32][KeySize][ValueSize][Key][Value]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CRC32 ensures data integrity&lt;/li&gt;
&lt;li&gt;KeySize and ValueSize define record boundaries&lt;/li&gt;
&lt;li&gt;ValueSize = -1 indicates a tombstone&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This format enables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;corruption detection&lt;/li&gt;
&lt;li&gt;safe parsing during recovery&lt;/li&gt;
&lt;li&gt;validation of partial writes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In storage systems, integrity checks are not optional — they are foundational.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;7. Memory Index Design&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The in-memory index is a simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;long&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It maps each key to its latest offset in the log file.&lt;/p&gt;

&lt;p&gt;This design provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;O(1) lookup time&lt;/li&gt;
&lt;li&gt;minimal overhead&lt;/li&gt;
&lt;li&gt;simplicity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, it introduces a key limitation:&lt;/p&gt;

&lt;p&gt;The index must fit entirely in memory.&lt;/p&gt;

&lt;p&gt;This makes the system memory-bound as the dataset grows.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;8. Startup and Recovery&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;On startup, the system reconstructs its state by scanning the log file:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Read records sequentially&lt;/li&gt;
&lt;li&gt;Validate checksums&lt;/li&gt;
&lt;li&gt;Update the index for each key&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The final state reflects the most recent record for each key.&lt;/p&gt;

&lt;p&gt;Advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;simple and reliable recovery&lt;/li&gt;
&lt;li&gt;no need for separate metadata&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Trade-off:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;startup time increases with log size&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;9. Observability and Logging&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Unlike kernel-level systems, user-space storage engines can implement structured logging.&lt;/p&gt;

&lt;p&gt;Typical log events include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;write operations with offsets&lt;/li&gt;
&lt;li&gt;tombstone creation&lt;/li&gt;
&lt;li&gt;recovery progress&lt;/li&gt;
&lt;li&gt;checksum failures&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;[INFO] Appended key=user:1 at offset=0
[INFO] Tombstone written for key=user:1
[WARN] Checksum mismatch at offset=170
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Observability is critical for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;debugging&lt;/li&gt;
&lt;li&gt;detecting corruption&lt;/li&gt;
&lt;li&gt;understanding system behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without it, diagnosing issues becomes extremely difficult.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;10. Failure Scenarios&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This design handles several failure modes naturally:&lt;/p&gt;

&lt;p&gt;Crash During Write&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Partial writes can be detected via checksum mismatch&lt;/li&gt;
&lt;li&gt;Invalid records can be skipped during recovery&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Data Corruption&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CRC32 validation prevents silent data corruption&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Power Loss&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Since writes are append-only, previously written data remains intact&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, durability depends on the underlying filesystem and flush behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;11. Performance Characteristics&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Strengths&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;high write throughput (sequential I/O)&lt;/li&gt;
&lt;li&gt;simple and predictable behavior&lt;/li&gt;
&lt;li&gt;efficient point lookups&lt;/li&gt;
&lt;li&gt;strong crash recovery guarantees&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Limitations&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;unbounded file growth&lt;/li&gt;
&lt;li&gt;memory-bound index&lt;/li&gt;
&lt;li&gt;no range queries&lt;/li&gt;
&lt;li&gt;no transactions or isolation&lt;/li&gt;
&lt;li&gt;no concurrent write optimization&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These limitations reflect deliberate design simplicity.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;12. Compaction Strategy&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Over time, the log accumulates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;outdated values&lt;/li&gt;
&lt;li&gt;tombstones&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Compaction reclaims space:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;scan log → keep latest entries → rewrite new file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This reduces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;disk usage&lt;/li&gt;
&lt;li&gt;read amplification&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Compaction is a core requirement in all log-structured systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;13. Design Trade-offs&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This project highlights several key trade-offs:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Design Choice&lt;/th&gt;
&lt;th&gt;Benefit&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Append-only writes&lt;/td&gt;
&lt;td&gt;Fast, simple&lt;/td&gt;
&lt;td&gt;Requires compaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;In-memory index&lt;/td&gt;
&lt;td&gt;Fast reads&lt;/td&gt;
&lt;td&gt;Memory usage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tombstones&lt;/td&gt;
&lt;td&gt;Cheap deletes&lt;/td&gt;
&lt;td&gt;Disk growth&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Simple format&lt;/td&gt;
&lt;td&gt;Easy recovery&lt;/td&gt;
&lt;td&gt;Limited features&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Storage systems are ultimately about choosing the right trade-offs.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;14. Lessons Learned&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Building a storage engine from scratch reveals several important insights:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Performance is primarily driven by I/O patterns&lt;/li&gt;
&lt;li&gt;Sequential access is the most important optimization&lt;/li&gt;
&lt;li&gt;Simplicity reduces failure modes&lt;/li&gt;
&lt;li&gt;Observability must be designed early&lt;/li&gt;
&lt;li&gt;Data integrity must be enforced at the lowest level
Most importantly:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Reliable systems are not built by adding features, but by controlling complexity.&lt;/p&gt;

&lt;p&gt;Conclusion&lt;/p&gt;

&lt;p&gt;KestrelCache is intentionally minimal, but it captures the essence of modern storage engine design.&lt;/p&gt;

&lt;p&gt;Even a simple log-structured key-value store demonstrates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;how durability is achieved&lt;/li&gt;
&lt;li&gt;how performance is optimized&lt;/li&gt;
&lt;li&gt;how systems recover from failure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Understanding these fundamentals is essential for building scalable backend systems and infrastructure.&lt;/p&gt;

</description>
      <category>database</category>
      <category>googlecloud</category>
      <category>backend</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>Building a Minimal Operating System Kernel with Interrupts, Paging, and Device Drivers</title>
      <dc:creator>Saksham Kapoor</dc:creator>
      <pubDate>Mon, 16 Mar 2026 18:02:31 +0000</pubDate>
      <link>https://dev.to/saksham_kapoor/building-a-minimal-operating-system-kernel-with-interrupts-paging-and-device-drivers-dc</link>
      <guid>https://dev.to/saksham_kapoor/building-a-minimal-operating-system-kernel-with-interrupts-paging-and-device-drivers-dc</guid>
      <description>&lt;p&gt;Modern operating systems often feel like black boxes. We interact with processes, files, and network sockets without thinking about what actually happens underneath.&lt;/p&gt;

&lt;p&gt;To better understand how low-level systems work, I built a small experimental kernel that runs on x86 architecture, implementing several core subsystems including interrupt handling, paging, device drivers, and a simple shell.&lt;/p&gt;

&lt;p&gt;This article explains the architecture of that kernel and the main design decisions behind it.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Why Build a Kernel?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Operating systems are one of the most complex pieces of software ever created. But the fundamental ideas behind them are surprisingly understandable when explored step by step.&lt;/p&gt;

&lt;p&gt;The motivation behind this project was to understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How CPUs transition from bootloader to kernel&lt;/li&gt;
&lt;li&gt;How interrupts allow hardware to communicate with software&lt;/li&gt;
&lt;li&gt;How paging enables virtual memory&lt;/li&gt;
&lt;li&gt;How device drivers interact with hardware&lt;/li&gt;
&lt;li&gt;How kernel services can be exposed through system calls&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rather than studying these concepts in isolation, the goal was to implement them together inside a working kernel.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;System Overview&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The kernel runs on 32-bit x86 architecture and boots through a Multiboot2-compatible bootloader such as GRUB.&lt;/p&gt;

&lt;p&gt;At a high level the architecture looks like this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Bootloader (GRUB)&lt;br&gt;
        ↓&lt;br&gt;
Kernel Entry (Assembly)&lt;br&gt;
        ↓&lt;br&gt;
Interrupt Descriptor Table&lt;br&gt;
        ↓&lt;br&gt;
Memory Management (Paging)&lt;br&gt;
        ↓&lt;br&gt;
Hardware Drivers&lt;br&gt;
        ↓&lt;br&gt;
Kernel Shell&lt;br&gt;
&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Each subsystem interacts with the others to form a minimal operating system environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Boot Process&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The boot process begins when the bootloader loads the kernel into memory and transfers control to its entry point.&lt;/p&gt;

&lt;p&gt;The kernel performs several early initialization steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Initialize descriptor tables&lt;/li&gt;
&lt;li&gt;Set up memory paging&lt;/li&gt;
&lt;li&gt;Configure interrupt handling&lt;/li&gt;
&lt;li&gt;Initialize hardware drivers&lt;/li&gt;
&lt;li&gt;Start the kernel shell&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This early boot stage is critical because the system is still running without many safety guarantees.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Interrupt Handling&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Hardware interrupts are one of the most fundamental mechanisms in operating systems.&lt;/p&gt;

&lt;p&gt;Devices such as keyboards, timers, and disks signal events to the CPU using interrupts.&lt;/p&gt;

&lt;p&gt;The kernel implements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Interrupt Descriptor Table (IDT)&lt;/li&gt;
&lt;li&gt;Exception handlers&lt;/li&gt;
&lt;li&gt;IRQ handlers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The IDT maps interrupt numbers to handler functions, allowing the CPU to transfer control to the correct kernel routine.&lt;/p&gt;

&lt;p&gt;Example flow:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Keyboard Key Press&lt;br&gt;
        ↓&lt;br&gt;
Keyboard Controller Interrupt&lt;br&gt;
        ↓&lt;br&gt;
CPU triggers interrupt handler&lt;br&gt;
        ↓&lt;br&gt;
Kernel keyboard driver processes input&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Without interrupts, the CPU would have to constantly poll devices, which would waste enormous amounts of processing time.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Memory Management&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Memory management is implemented using paging.&lt;/p&gt;

&lt;p&gt;Paging allows the operating system to map virtual memory addresses to physical memory addresses.&lt;/p&gt;

&lt;p&gt;The kernel initializes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Page directory&lt;/li&gt;
&lt;li&gt;Page tables&lt;/li&gt;
&lt;li&gt;Paging control registers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once paging is enabled, memory can be organized into virtual address spaces.&lt;/p&gt;

&lt;p&gt;Benefits of paging include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;memory protection&lt;/li&gt;
&lt;li&gt;flexible address mapping&lt;/li&gt;
&lt;li&gt;foundation for user processes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this kernel the paging system establishes a simple flat memory model for kernel execution.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Kernel Heap and Dynamic Memory&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Operating systems cannot rely on standard libraries like malloc.&lt;/p&gt;

&lt;p&gt;Instead, the kernel implements its own heap allocator.&lt;/p&gt;

&lt;p&gt;This allows kernel subsystems to dynamically allocate memory for structures such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;device buffers&lt;/li&gt;
&lt;li&gt;process data&lt;/li&gt;
&lt;li&gt;driver state&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Although simple, this allocator demonstrates the fundamental idea behind dynamic memory management in kernel space.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Device Drivers&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Hardware interaction happens through device drivers.&lt;/p&gt;

&lt;p&gt;Two drivers are implemented in the kernel:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;VGA Display Driver&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The kernel writes directly to VGA memory at:&lt;/p&gt;

&lt;p&gt;0xB8000&lt;/p&gt;

&lt;p&gt;This allows text to be displayed on screen without relying on BIOS services.&lt;/p&gt;

&lt;p&gt;The VGA driver implements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;character output&lt;/li&gt;
&lt;li&gt;cursor control&lt;/li&gt;
&lt;li&gt;color support&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;PS/2 Keyboard Driver&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The keyboard driver handles hardware interrupts generated by the PS/2 controller.&lt;/p&gt;

&lt;p&gt;The driver:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;reads scancodes from the keyboard port&lt;/li&gt;
&lt;li&gt;translates them into characters&lt;/li&gt;
&lt;li&gt;forwards input to the kernel shell&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This demonstrates how hardware input flows through the interrupt system into software.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Timer Support&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The kernel configures the Programmable Interval Timer (PIT) to generate periodic interrupts.&lt;/p&gt;

&lt;p&gt;Timers are essential for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;scheduling&lt;/li&gt;
&lt;li&gt;time measurement&lt;/li&gt;
&lt;li&gt;system responsiveness&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even in a simple kernel, timer interrupts provide the foundation for multitasking systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;System Call Interface&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A minimal system call interface is implemented to expose kernel services.&lt;/p&gt;

&lt;p&gt;System calls act as the boundary between:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;user programs&lt;br&gt;
        ↕&lt;br&gt;
kernel services&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Although this kernel does not yet implement full user-mode processes, the syscall interface lays the groundwork for future expansion.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Interactive Kernel Shell&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;To make the system usable, the kernel includes a small command-line shell.&lt;/p&gt;

&lt;p&gt;The shell allows interaction with kernel services and debugging of internal behavior.&lt;/p&gt;

&lt;p&gt;Typical commands might include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;memory inspection&lt;/li&gt;
&lt;li&gt;system information&lt;/li&gt;
&lt;li&gt;device testing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This interface helps verify that the kernel subsystems are functioning correctly.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Limitations&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This kernel is intentionally minimal and designed primarily as a learning platform.&lt;/p&gt;

&lt;p&gt;Some important limitations include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;single-core execution&lt;/li&gt;
&lt;li&gt;no filesystem&lt;/li&gt;
&lt;li&gt;no process isolation&lt;/li&gt;
&lt;li&gt;no SMP support&lt;/li&gt;
&lt;li&gt;no preemptive multitasking&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Despite these limitations, the project demonstrates how core operating system components interact in a real execution environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What This Project Taught Me&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Building even a small kernel provides deep insights into how computers work.&lt;/p&gt;

&lt;p&gt;Key lessons from this project include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;hardware interrupts are fundamental to system responsiveness&lt;/li&gt;
&lt;li&gt;memory management is the foundation of modern OS design&lt;/li&gt;
&lt;li&gt;driver design requires careful interaction with hardware&lt;/li&gt;
&lt;li&gt;observability and debugging are essential for low-level development&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most importantly, operating systems are not magic, they are carefully structured layers of software interacting directly with hardware.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Building an operating system kernel is one of the most educational experiences for understanding systems engineering.&lt;/p&gt;

&lt;p&gt;This project demonstrates how several essential subsystems, interrupts, memory management, device drivers, and system calls, combine to form the foundation of an operating system.&lt;/p&gt;

&lt;p&gt;Future work could include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;implementing user processes&lt;/li&gt;
&lt;li&gt;adding a filesystem&lt;/li&gt;
&lt;li&gt;supporting multitasking&lt;/li&gt;
&lt;li&gt;expanding hardware driver support&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even a small kernel reveals just how fascinating low-level systems development can be.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>computerscience</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
