CAP in Practice: What AP Actually Buys You
Network partitions are not rare events, and the interesting systems never pick one side cleanly.
Archive
103 long-form notes, newest first. No newsletter, no tracking — just the archive and an RSS feed.
Network partitions are not rare events, and the interesting systems never pick one side cleanly.
One key to share, two keys to manage. A decision guide with the protocols that mix both.
Avalanche, collision resistance, preimage resistance — the three properties that make SHA-256 boring on purpose.
The hash tree is not a blockchain primitive — it is a general proof-of-membership machine, and most systems use it badly.
SHA-256 and HMAC both output 32 bytes, and using the wrong one in the wrong place is a real vulnerability class.
Same word, opposite requirements: one optimizes for speed and spread, the other for unpredictability.
Why 128-bit hashes are enough for most things and 64-bit hashes are not enough for anything adversarial.
A salt defeats precomputation; a pepper defeats a stolen database. Neither replaces a slow hash.
Memory, iterations, and parallelism are a budget, not a magic tuple — here is how to spend it.
Hashing bytes to name them gives you free deduplication, immutable caching, and a verification step — if you keep the name honest.
A bloom filter never lies about absence, but its false-positive rate is a tunable bill you pay in bits and hashes.
Virtual nodes, and why the ring is really a weighted random assignment that happens to be cheap to update.
If an attacker can choose your map keys, a deterministic hash function is a denial-of-service primitive.
Reusing a nonce under the same key leaks plaintext XOR and, for GCM, the authentication key itself.
Both are safe AEADs. The choice is about hardware, side channels, and nonce size — not raw strength.
Extract-then-expand is the standard way to derive purpose-bound keys from shared secrets. Skip it and you invite cross-protocol attacks.
For every valid ECDSA signature there is a second valid one. Systems that assume uniqueness get burned.
Deterministic nonces, a cofactor-safe verification equation, and small keys — the modern default for signatures.
The raw Diffie-Hellman output is not a key. Feed it through a KDF and bind the transcript.
Textbook RSA is multiplicative; PKCS#1 v1.5 is fragile; OAEP is the floor for encryption.
A session stays secret after a server compromise only if the ephemeral private key is actually gone.
Branching on secret bytes leaks through timing. Here is what actually helps and what only looks like it does.
Weak RNGs do not throw errors. They produce keys that look fine and are shared with everyone.
What was removed from TLS 1.2 matters as much as what was added: fewer options, fewer downgrade paths.
CT does not stop a bad certificate; it guarantees someone can find it.
CRLs are too big, OCSP leaks browsing, stapling helps until it does not. Plan for revocation failure explicitly.
One RFC that packages KEM, KDF, and AEAD so you stop inventing your own sealed-box format.
Client certificates are a strong authentication mechanism and a lifecycle problem. Treat them like credentials, not config.
Encrypted Client Hello closes the last plaintext metadata hole in TLS — where it is deployed.
Pinning stops MITM proxies and breaks your app when you forget to rotate the pin.
One port, many protocols. ALPN is how HTTP/2, HTTP/3, and custom RPCs coexist without a second handshake.
TLS 1.3 runs inside QUIC, so the first flight already carries application data — and loses TCP head-of-line blocking.
DNSSEC authenticates DNS answers; DANE can bind certificates to them. Both are powerful and operationally demanding.
One is about real-time order of single operations, the other about equivalent serial execution of transactions.
Leader election, log replication, and safety — the three mechanisms that make Raft teachable.
Paxos is a family of protocols; Raft is a specification with an implementation manual. Choose by ecosystem, not theory.
R + W > N gives consistency; majority quorums give availability under f failures. The rest is arithmetic.
A time-bounded lease lets a leader serve reads without a round trip — as long as clocks behave.
Physical clocks are wrong by an unknown amount. Spanner turns that uncertainty into a wait.
HLC timestamps stay close to physical time while never violating happens-before.
If every replica applies operations commutatively, order stops mattering. The cost is metadata and semantics.
Random fanout spreads state in O(log n) rounds and never needs a membership oracle.
In an asynchronous network you cannot distinguish a slow node from a dead one. Every detector picks a trade-off.
Users notice when their own update disappears. Sticky sessions are a patch; version tokens are the fix.
Retries are inevitable. A key plus stored response turns an unsafe request into a safe replay.
Queues promise it, but the guarantee always comes from deduplication and atomic state changes downstream.
2PC guarantees atomicity across databases and blocks forever if the coordinator dies at the wrong moment.
A sequence of local transactions with compensating actions keeps availability and forces you to design for partial failure.
Writing to a database and a broker in one request is not atomic. Write the event to the database instead.
The log is easy. Schema evolution, snapshots, and replay semantics are the hard parts.
A vector clock tells you whether two events are ordered or concurrent. The application still has to choose.
A scalar logical clock preserves causality but cannot tell concurrency from causality.
Crash faults assume nodes stop. Byzantine faults assume they lie — and that changes the quorum math.
Buffer writes in memory, flush sorted files, and pay for it at read and compaction time.
One updates in place, the other appends. The choice shapes write amplification, read latency, and space usage.
Every byte you write may be rewritten several times before it settles. SSDs charge you for all of it.
A write-ahead log makes crash recovery possible — if the record actually reaches stable storage before you acknowledge.
Disks with volatile write caches, filesystems that ignore flushes, and virtualization layers that swallow them.
Never overwrite in place; write new blocks and flip pointers. Snapshots become free, and so do the problems.
k data shards plus m parity shards survive m losses for a fraction of the bytes — at the cost of reconstruction.
Leveled compaction keeps reads fast; tiered keeps writes cheap. Every LSM engine picks a point between.
Keep multiple versions and hand each transaction a snapshot. The cost is version cleanup and write conflicts.
Modern object stores are strongly consistent for new writes, but listings, caches, and multipart uploads have edges.
When producers outrun consumers, the only choices are drop, buffer, or slow down. Pick deliberately.
One lost packet can stall unrelated requests if they share an ordered stream. Multiplexing only helps if it is real.
Two reasonable optimizations combine into latency spikes for small writes.
TFO carries data in the SYN, but middleboxes, cookies, and replay concerns limit deployment.
A flexible priority tree was ignored by implementations; HTTP/3 replaced it with simple, explicit priorities.
Per-stream delivery means a lost packet costs one stream, not the connection — but congestion control still reacts globally.
The same IP announced from many places gets users to the nearest one — and makes failures global.
A pool per instance multiplies connections; a pool per cluster needs a proxy. Either way, size by concurrency, not by hope.
Synchronized retries amplify load exactly when the system is weakest. Jitter and budgets are not optional.
A per-call timeout without a deadline propagates work you no longer need. Deadlines are the fix.
If one request touches 100 services, the p99 of each becomes the p50 of the whole.
Concurrency equals throughput times latency. Every queue-sizing argument follows from it.
If your load generator waits for a response before scheduling the next request, it hides the latency spikes users actually see.
Percentiles are not additive. Aggregating p99s across shards or minutes produces a number that means nothing.
When a hot key expires, every request misses at once. Single-flight and jittered TTLs fix it.
Aligned timers, synchronized reconnects, and simultaneous retries all convert a small event into a spike.
They stop sending traffic to a failing dependency so it can recover, and so your own threads do not pile up.
Under overload, dropping work early beats serving everything badly — if you drop the right work.
Parallelism helps until the serial fraction dominates. Most systems have more of it than the diagram suggests.
The kernel already keeps recently read files in RAM. Fighting it with application caches usually loses.
Algorithm confusion, unchecked claims, and treating a signed token as authorization.
The authorization code flow is only safe if the code cannot be replayed by an interceptor.
If an attacker can plant a session identifier, the victim authenticates the attacker into their own account.
Any feature that fetches a user-supplied URL can be turned into a proxy into your private network.
SameSite cookies plus origin checks close the classic attack. Custom headers and token endpoints still need care.
Dependencies run with your privileges. Lockfiles, provenance, and least privilege are the controls that matter.
Containers isolate processes, not kernels. If the workload is hostile, that distinction is the whole threat model.
Most workloads need a few dozen syscalls. Filters can make the rest fail fast.
Linux splits root privileges into capabilities. Grant the few you need and drop the rest.
Authenticate and authorize every request based on identity and context — and assume the network is hostile.
A proof of a statement reveals nothing but its truth. The engineering question is what the statement costs to prove.
Trusted setup and small proofs versus transparency and larger proofs. The choice is about assumptions, not speed alone.
Kyber became ML-KEM. Here is what changes in a TLS handshake and why hybrid mode is the migration path.
Dilithium is now ML-DSA. Larger signatures, different performance, and a hybrid period ahead.
FHE is real and still expensive. Know which variant matches your operation before committing.
Multiple parties compute a joint function without revealing inputs. The costs are rounds and bandwidth.
A VDF takes a known number of sequential steps to compute and is fast to verify — a clock made of math.
t-of-n parties produce one signature without ever assembling the full private key.
A signature proves a member of a group signed, without revealing which one. Anonymity with a fixed cost.
Vector commitments replace per-level hashes, so proofs stay small as the tree widens.