Progress · 0/8 phases
- All 120 Days Reference — Storage Engineering Roa
- Phase 8 — System Design & Interview Prep (Days 1
- Phase 7 — Performance Engineering (Days 91–105)
- Phase 6 — C++ Systems Programming (Days 76–90)
- Phase 4 — Distributed Storage Systems (Days 46–6
- Phase 3 — Storage Fundamentals (Days 31–45)
- Phase 2 — Linux Internals & OS (Days 16–30)
- Phase 1 — Go for Infrastructure (Days 1–15)
💾 Phase 3 — Storage Fundamentals (Days 31–45)
11 min read · Days 31–45 · Notion
Core insight: Storage interviews at Pure Storage, Cohesity, Rubrik, NetApp, MinIO, and Portworx are fundamentally about answering one question: "Do you understand what happens between when an application calls write() and when that data is safe on persistent media?" This phase gives you a comprehensive answer.
Day 31-33 — Block vs File vs Object Storage
The three storage models
Block storage: raw, addressable blocks. No filesystem concept at this level.
- Examples: AWS EBS, iSCSI LUNs, NVMe namespaces, SAN volumes
- Access: read/write at block offsets. The OS or application manages a filesystem on top.
- Use cases: databases (PostgreSQL, MySQL), VMs (hypervisor stores VM disk as a block device)
- Performance: lowest latency. No metadata overhead. Direct hardware access.
- Limitation: not shareable between hosts (unless clustered block store like SANs with SCSI reservations)
File storage: hierarchical namespace with files and directories.
- Examples: NFS, SMB/CIFS, NFSv4, Samba, AWS EFS, Azure Files, Qumulo, VAST Data
- Access: POSIX API (open, read, write, stat, mkdir, readdir)
- Use cases: shared home directories, code repositories, media workflows, HPC scratch
- Performance: adds metadata overhead vs block. Network latency for remote filesystems.
- Advantage: shareable by many clients simultaneously. Human-readable namespace.
Object storage: flat namespace of objects with a unique key. No hierarchy (prefix tricks mimic it).
- Examples: AWS S3, GCS, Azure Blob, MinIO, Ceph RadosGW, Cloudflare R2
- Access: HTTP API (PUT, GET, DELETE, LIST). Not POSIX.
- Use cases: backup, archive, ML training data, media storage, data lakes
- Performance: high throughput for large objects. High latency for small random I/O.
- Advantage: virtually unlimited scale, built-in metadata, HTTP accessible, cheap at scale
Comparison table:
Block File Object
Protocol iSCSI/NVMe NFS/SMB S3/HTTP
Namespace flat (offset) hierarchical flat (key)
POSIX yes (raw) yes no
Shareable limited yes yes (per object)
Latency lowest medium higher
Scale limited medium unlimited
Use case DB/VM shared files backup/ML
Consistency strong strong eventual (S3)Day 34-36 — IOPS, Throughput, and Latency
The three performance dimensions
IOPS (Input/Output Operations Per Second)
- Number of I/O operations completed per second
- Critical for: random workloads, transactional databases, VMs
- Example: PostgreSQL doing 10,000 random 8KB reads/second = 10K IOPS
- NVMe SSD: 1M+ random 4K IOPS
- SATA SSD: 100K random 4K IOPS
- HDD: 150-200 random 4K IOPS (seek time dominated)
Throughput (Bandwidth) - MB/s or GB/s
- Amount of data transferred per second
- Critical for: sequential workloads, streaming, backup, bulk copy
- Example: copying a 10GB backup file at 500 MB/s
- NVMe SSD: 7 GB/s sequential read (PCIe 4.0 x4)
- 10GbE NFS: ~1.25 GB/s (network limited)
- SATA SSD: 500 MB/s sequential
Latency - microseconds (µs) or milliseconds (ms)
- Time from I/O request submission to completion
- Critical for: databases, real-time applications, low-latency trading
- NVMe SSD local: ~100 µs (0.1ms)
- NVMe/TCP over 25GbE: ~200-400 µs
- NVMe/RoCE (RDMA): ~50-100 µs
- iSCSI over 10GbE: ~500 µs - 2ms
- NFS over 10GbE: ~1-5ms
- HDD: 5-10ms (seek + rotational latency)
The relationship:
Throughput = IOPS × I/O size
IOPS = Throughput / I/O size
Example: 1M IOPS × 4KB = 4 GB/s throughput
Example: 10 GB/s / 128KB = ~78K IOPS
Queue depth (QD): how many I/Os are submitted concurrently
QD=1: one I/O at a time (latency sensitive)
QD=32: 32 I/Os in flight (throughput optimized)
NVMe: benefits from high QD (32-128)
HDD: benefits from QD 32+ (elevator algorithm merges seeks)fio: the industry standard storage benchmark
# Sequential read throughput (128KB blocks, single thread)
fio --name=seq-read --rw=read --bs=128k --size=4G \
--numjobs=1 --iodepth=32 \
--direct=1 --ioengine=libaio \
--filename=/dev/nvme0n1 \
--output-format=json
# Random 4K read IOPS (database simulation)
fio --name=rand-read-iops --rw=randread --bs=4k --size=4G \
--numjobs=4 --iodepth=32 \
--direct=1 --ioengine=libaio \
--filename=/dev/nvme0n1
# Mixed read/write workload (70% read, 30% write)
fio --name=mixed-rw --rw=randrw --rwmixread=70 --bs=4k \
--size=4G --numjobs=4 --iodepth=32 \
--direct=1 --ioengine=libaio \
--filename=/dev/nvme0n1
# Latency test (QD=1, small block)
fio --name=latency --rw=randread --bs=4k --size=4G \
--numjobs=1 --iodepth=1 \
--direct=1 --ioengine=libaio \
--filename=/dev/nvme0n1 \
--percentile_list=50,90,95,99,99.9,99.99
# Key output metrics to read:
# read: IOPS=xxx, BW=xxxMiB/s
# lat (nsec): min=xxx, max=xxx, avg=xxx, stdev=xxx
# lat (nsec): 50th=xxx, 99th=xxx, 99.9th=xxxDay 37-40 — Storage Protocols: NFS, iSCSI, NVMe/TCP, NVMe/RoCE, RDMA
NFS (Network File System)
# NFS v3 vs v4 vs v4.1 vs v4.2
# v3: stateless, UDP or TCP, no native security
# v4: stateful, compound operations, Kerberos auth, single TCP port (2049)
# v4.1: pNFS (parallel NFS - client accesses storage nodes directly)
# v4.2: server-side copy, sparse files, space_used attribute
# Server: export configuration
cat /etc/exports
# /data/nfs 192.168.1.0/24(rw,sync,no_subtree_check,no_root_squash)
# Options:
# rw: read-write
# sync: fsync before responding (data safe, but slower)
# async: respond before fsync (faster, risk of data loss on crash)
# no_root_squash: root on client = root on server (dangerous)
# root_squash: root on client = nobody on server (safer)
exportfs -ra # reload /etc/exports
showmount -e # show current exports
# Client: mount
mount -t nfs -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600 \
192.168.1.10:/data/nfs /mnt/nfs
# Performance tuning options:
# rsize/wsize: read/write buffer size (max 1MB for NFSv4)
# hard: retry forever on server failure (vs soft: fail after timeout)
# async mount option: NOT the same as server async!
# async on client = don't sync locally before sending
# Check NFS stats
nfsstat -c # client statistics
nfsstat -s # server statistics
nfsiostat 1 # per-mount I/O statisticsiSCSI (internet Small Computer Systems Interface)
# iSCSI: SCSI commands over TCP/IP
# Initiator (client) connects to Target (server) and sees a block device
# Target presents LUNs (Logical Units = block volumes)
# Key terms:
# IQN: iSCSI Qualified Name (unique identifier)
# Format: iqn.YYYY-MM.reverse-domain:unique-name
# Example: iqn.2024-01.com.minio:storage-node-1
# Server (target) setup with targetcli
targetcli
/backstores/block create name=lun1 dev=/dev/sdb
/iscsi create iqn.2024-01.com.example:target1
/iscsi/iqn.2024-01.com.example:target1/tpg1/luns create /backstores/block/lun1
/iscsi/iqn.2024-01.com.example:target1/tpg1/portals create 0.0.0.0 3260
# Client (initiator) setup
apt-get install open-iscsi
iscsiadm -m discovery -t st -p 192.168.1.10 # discover targets
iscsiadm -m node --login # connect to all targets
lsblk # new block device appears (e.g., /dev/sdb)
# Performance characteristics:
# Over 10GbE: ~800MB/s throughput, ~100K IOPS for 4K random
# Latency: 0.5-2ms (TCP overhead)
# Benefits from Jumbo frames (MTU 9000) for throughputNVMe/TCP and NVMe/RoCE (RDMA)
# NVMe/TCP: NVMe commands over TCP (kernel 5.0+)
# Low latency: ~200-400µs (better than iSCSI because optimized protocol)
# No special hardware needed (just fast Ethernet)
# Server: expose NVMe device via TCP
modprobe nvmet
modprobe nvmet-tcp
mkdir /sys/kernel/config/nvmet/subsystems/testnqn
echo 1 > /sys/kernel/config/nvmet/subsystems/testnqn/attr_allow_any_host
mkdir /sys/kernel/config/nvmet/subsystems/testnqn/namespaces/1
echo /dev/nvme0n1 > /sys/kernel/config/nvmet/subsystems/testnqn/namespaces/1/device_path
echo 1 > /sys/kernel/config/nvmet/subsystems/testnqn/namespaces/1/enable
mkdir /sys/kernel/config/nvmet/ports/1
echo 192.168.1.10 > /sys/kernel/config/nvmet/ports/1/addr_traddr
echo tcp > /sys/kernel/config/nvmet/ports/1/addr_trtype
echo 4420 > /sys/kernel/config/nvmet/ports/1/addr_trsvcid
ln -s /sys/kernel/config/nvmet/subsystems/testnqn /sys/kernel/config/nvmet/ports/1/subsystems/
# Client: connect
modprobe nvme-tcp
nvme discover -t tcp -a 192.168.1.10 -s 4420
nvme connect -t tcp -a 192.168.1.10 -s 4420 -n testnqn
nvme list # see the connected namespace
# RDMA (Remote Direct Memory Access):
# Key benefit: bypasses OS TCP/IP stack entirely
# Data moves from remote memory to local memory WITHOUT CPU involvement
# This is why NVMe/RoCE achieves 50-100µs latency (vs 500µs+ for iSCSI)
# RoCE (RDMA over Converged Ethernet):
# Layer 2: requires Priority Flow Control (PFC) to prevent packet drops
# RoCEv2: Layer 3 routable (uses UDP)
# Hardware: Mellanox/NVIDIA ConnectX, Chelsio, Broadcom bnxt
# RDMA verbs (programming model):
# QP (Queue Pair): a send queue + receive queue, the RDMA communication endpoint
# WR (Work Request): what you submit to a QP
# CQ (Completion Queue): where completions land
# MR (Memory Region): memory registered for RDMA (pinned, not swappable)
# Send/Recv: two-sided (both sides involved)
# RDMA Write/Read: one-sided (source can write/read remote memory directly)
cat /proc/net/dev # check NIC statistics
ibstat # check InfiniBand/RoCE adapter status
perfquery # InfiniBand performance counters
ethtool -S eth1 | grep -i rdma # RoCE stats per NICDay 41-43 — Replication vs Erasure Coding
Replication
Simple replication: store N full copies of the data
3x replication example:
Original data: [Block A] [Block B] [Block C]
Node 1: [A] [B] [C] (primary)
Node 2: [A] [B] [C] (replica 1)
Node 3: [A] [B] [C] (replica 2)
Fault tolerance: survive loss of (N-1) nodes
3x = survive loss of 2 nodes, still have 1 copy
Storage overhead: N times the original size
3x replication = 3x storage cost
Read performance: can serve reads from any replica (3x read IOPS)
Write performance: must write to all N replicas (slower than single write)
Recovery: after a node failure, copy the full data back (expensive for large data)Erasure Coding
Erasure coding: split data into k data chunks + m parity chunks (k+m = n total)
Can recover from loss of any m chunks out of k+m
RS(6,3) example -- 6 data + 3 parity:
Original 6GB file split into 6 x 1GB data chunks:
[D1][D2][D3][D4][D5][D6] -> math produces -> [P1][P2][P3]
Store across 9 nodes: D1 D2 D3 D4 D5 D6 P1 P2 P3
Any 6 of the 9 chunks can reconstruct the full file
Tolerates loss of any 3 nodes
Storage overhead: (k+m)/k times the original
RS(6,3): 9/6 = 1.5x (vs 3x for 3x replication)
RS(12,4): 16/12 = 1.33x
Massive storage savings at large scale!
Trade-offs vs replication:
Pro: lower storage overhead (1.3-1.5x vs 2-3x)
Con: higher CPU cost (encoding/decoding math)
Con: read amplification for reconstruction (must read k chunks to serve any chunk)
Con: higher latency (must gather k chunks even for a read)
Pro: better fault tolerance per storage unit at large scale
Use cases:
Replication: latency-sensitive workloads, small data sets, warm data
Erasure coding: large-scale object stores, cold/archive storage, backup
Examples: Ceph RBD uses replication for block, Ceph RADOS uses EC for object
S3 uses EC internally, HDFS moved from replication to EC for cold data
Reed-Solomon math (conceptual):
Treat each chunk as a polynomial coefficient
Compute parity as evaluations of the polynomial at extra points
Reconstruction = polynomial interpolation from any k pointsDay 44-45 — Phase 3 Capstone: fio Benchmark Suite
Project: Build a comprehensive storage benchmark harness
# scripts/benchmark.sh
#!/bin/bash
# Runs a full storage characterization suite
# Usage: ./benchmark.sh /dev/nvme0n1 results/
DEVICE=$1
OUTDIR=$2
mkdir -p $OUTDIR
run_fio() {
local name=$1; shift
echo "Running $name..."
fio --output-format=json "$@" > $OUTDIR/$name.json 2>&1
# Parse and print key metrics
python3 -c "
import json, sys
data = json.load(open('$OUTDIR/$name.json'))
job = data['jobs'][0]
read = job['read']
write = job['write']
print(f' Read: {read[\"iops\"]:.0f} IOPS {read[\"bw\"] / 1024:.1f} MB/s p99={read[\"lat_ns\"][\"percentile\"][\"99.000000\"] / 1000:.0f} µs')
if write['iops'] > 0:
print(f' Write: {write[\"iops\"]:.0f} IOPS {write[\"bw\"] / 1024:.1f} MB/s p99={write[\"lat_ns\"][\"percentile\"][\"99.000000\"] / 1000:.0f} µs')
"
}
# 1. Sequential read throughput
run_fio seq-read-throughput \
--name=seq-read --rw=read --bs=128k --size=8G \
--numjobs=1 --iodepth=32 --direct=1 --ioengine=libaio \
--filename=$DEVICE
# 2. Sequential write throughput
run_fio seq-write-throughput \
--name=seq-write --rw=write --bs=128k --size=8G \
--numjobs=1 --iodepth=32 --direct=1 --ioengine=libaio \
--filename=$DEVICE
# 3. Random 4K read IOPS (max)
run_fio rand-read-iops-max \
--name=rand-read --rw=randread --bs=4k --size=8G \
--numjobs=8 --iodepth=32 --direct=1 --ioengine=libaio \
--filename=$DEVICE
# 4. Random 4K write IOPS (max)
run_fio rand-write-iops-max \
--name=rand-write --rw=randwrite --bs=4k --size=8G \
--numjobs=8 --iodepth=32 --direct=1 --ioengine=libaio \
--filename=$DEVICE
# 5. Latency (QD=1, single-threaded)
run_fio latency-qd1 \
--name=latency --rw=randread --bs=4k --size=8G \
--numjobs=1 --iodepth=1 --direct=1 --ioengine=libaio \
--filename=$DEVICE \
--percentile_list=50,90,95,99,99.9,99.99
# 6. Mixed 70/30 read/write (database simulation)
run_fio mixed-70-30 \
--name=mixed --rw=randrw --rwmixread=70 --bs=8k --size=8G \
--numjobs=4 --iodepth=32 --direct=1 --ioengine=libaio \
--filename=$DEVICE
echo "Done. Results in $OUTDIR/"Also: answer in writing:
- You ran these benchmarks and got 500K IOPS at QD=32 but only 80K IOPS at QD=1. Why?
- Sequential throughput is 6 GB/s but random 4K throughput is 800 MB/s. Why?
- A customer reports their NVMe drive achieves 1M IOPS in benchmarks but only 50K IOPS in production with their database. What are the likely causes?
Interview questions from the guide
- "Explain the difference between block, file, and object storage with a real example of each."
- "What is IOPS? How is it related to throughput and block size?"
- "What is queue depth? How does it affect NVMe vs HDD performance?"
- "Compare NFS and iSCSI. When would you use each?"
- "What is RDMA and why does NVMe/RoCE have lower latency than iSCSI?"
- "Compare erasure coding and replication. When would you choose each?"
- "How would you benchmark a new storage system before production use?"
- "What does p99 latency mean and why is it more important than average latency?"