The future of data systems
Each client writes the database, then the search index
Each message takes a random network delay. Red = the index disagrees with the database and nothing in flight will ever fix it. Try: stop random writes, press "Race" a few times in dual-write mode until the two stores disagree for good; then switch to the log and crash the index consumer — it resumes from its offset and converges.
Reprocessing: migrating a view without a big bang
view-reprocessingkappa-architecturelambda-architectureOne log, two versions of the view side by side
v2 is a new consumer of the same log, starting at offset 0. v1 keeps running, so the switch is reversible at every step. Try: route 30% of reads to v2 before deploying it (wrong answers), deploy it, wait for the lag to hit zero, then ramp the router to 100%.
Unbundling the database: a dataflow you can rewire
unbundling-the-databasederivation-functionstream-triggersreads-as-eventsClick a derived view to wire or unwire it from the log
Each derived view is just a consumer with its own offset: creating an index means "replay the log from 0, then keep following". Try: wire the secondary index and watch it backfill; press "Ana moves city" and see the index and the revenue view update. Then unwire the trigger, switch to "offset 0" and rewire it: every old user gets a second welcome email.
Sequence of transfer req-003
same network luck in every modeTCP already removes duplicate packets, and the database already makes each transaction atomic — but the retry is a new connection and a new transaction, so neither layer can tell it is the same transfer. Only an id minted by the client and checked in the database (end to end) can. Try: with "retry on timeout", send 20 and count the red rows; switch modes — the same losses happen, with different results.
Constraints without coordination: timeliness vs integrity
uniqueness-via-logtimeliness-vs-integritycoordination-avoidanceapology-compensationRequests routed by hash(username) to a single-threaded partition
All requests for the same name hash to the same partition and are decided one at a time in log order: no locks, no distributed transaction, never two winners. Slowing the processor only delays the answer. Try: race three users for one name in each mode; in the replica mode raise the lag. In the log mode crank the processing delay — the answer gets later (timeliness), but it is never wrong (integrity).
Event log (hash-chained) → derived account balances (checksummed blocks)
C = checksum, H = hash chain, R = re-derivation. Each audit catches a different class of fault: checksums catch storage corruption but happily vouch for a bug's output; the hash chain catches history being rewritten; recomputing derived state from the log catches both the disk and the bug. Try: inject a buggy write, run the checksum audit (clean!), then re-derive.
What you collected for a feature vs what it can reveal
- → can infer home and work address
- → can infer visits to clinics or places of worship
- → can infer who you spend nights with
- → can infer pregnancy, before you tell family
- → can infer financial distress
- → can infer health conditions
Joined together, these datasets reveal more than any one of them — and a join is one query away.
Every stored record outlives the intent it was collected with: the company may be acquired, breached, or compelled. Try: switch on browsing and set retention to 10 years, then ask whether the feature really needed more than a week.
- takeawayDecide what is the system of record and derive everything else from an ordered log of its changes. Consumers of one log can lag, but never disagree — unlike dual writes, which race and partially fail.
- takeawayA derived view is a function of the log, so you can add one, rebuild one, or migrate gradually by running old and new side by side. One stream implementation can handle history and live data.
- takeawayCorrectness is end to end: carry request ids into the database, enforce constraints by ordering requests in a partitioned log, prefer integrity over timeliness, and keep auditing — checksums alone won't catch your own bugs.
Key concepts · 20
book chapter 12- System of record
The authoritative copy of the data, where each fact is written first and exactly once.
When two copies disagree, the system of record wins by definition. Being explicit about which dataset is authoritative and which are derived is the first step to integrating many tools sanely.
↑ see it in the visualization - Derived data
Caches, indexes, materialized views and models computed from the system of record by some transformation.
Derived data is redundant by design: if lost or wrong, you can recompute it. That property is what makes it safe to have many specialized stores for one dataset.
ElasticsearchRedisdata warehouse↑ see it in the visualization - Ordering writes through a single log
Every change is appended to one totally ordered log; each derived system consumes it in that order.
Ad-hoc dual writes from the application race and partially fail, leaving stores permanently inconsistent. With a log, all consumers apply the same writes in the same order, so they can only be behind, never contradictory — they converge.
KafkaDebezium CDCPulsar↑ see it in the visualization - Reprocessing to evolve a view
Building a new version of a derived view by replaying the full history alongside the old one.
It turns schema migrations into a gradual, reversible process: run both views, shift a fraction of reads to the new one, and roll back at any point because the old view never stopped.
↑ see it in the visualization - Lambda architecture
A batch layer periodically recomputes exact views from immutable events; a speed layer serves fast approximate updates in between.
It combined reliable batch reprocessing with low-latency streams, but you implement the logic twice in two frameworks and must merge their outputs, and the approximate path drifts between batch runs.
Hadoop + Storm↑ see it in the visualization - Unifying batch and stream (kappa)
One stream processor handles both live events and the replay of history from the beginning of the log.
With a log that retains history and engines that support event time and exactly-once semantics, the same code can rebuild a view from scratch or keep it up to date — one implementation instead of two.
Kafka StreamsFlinkBeam↑ see it in the visualization - Unbundling the database (“database inside-out”)
Treating the log as the database's core and its indexes, caches and views as separate derived consumers.
A database already does this internally: CREATE INDEX scans existing data then follows new writes. Unbundling exposes that mechanism so you can compose specialized storage and processing tools, loosely coupled through the log.
↑ see it in the visualization - Application code as a derivation function
Expressing derived datasets as functions from input streams to output state, run by stream processors.
Instead of application code reading and writing a shared mutable database, each view is a deterministic function of the log. Adding a view means wiring a new consumer, not changing the writers.
↑ see it in the visualization - Stream processors as triggers
Consumers that perform side effects (emails, notifications, calls to external services) in response to events.
Unlike pure views, side effects don't replay safely: rebuilding from offset 0 sends every email again. Side-effecting consumers need to start at the head or deduplicate on event ids.
↑ see it in the visualization - Reads are events too
Logging read requests into a stream and answering them with a stream–table join against the derived state.
It records exactly what each user saw and when, which helps tracking causality and auditing; it also lets reads be routed to the partition holding the data, like a distributed query.
↑ see it in the visualization - The end-to-end argument
Some guarantees can only be implemented correctly with the help of the application at both endpoints of the communication.
TCP removes duplicate packets and a transaction is atomic, but neither knows that a retried HTTP request is the same transfer. Lower layers' guarantees help performance but don't remove the need for an end-to-end check.
↑ see it in the visualization - Operation identifiers for duplicate suppression
A unique request id minted by the client and carried all the way to the database, enforced with a uniqueness constraint.
Retrying is the only way to survive a lost response, and only an end-to-end id makes a non-idempotent operation like a money transfer safe to retry: the second attempt finds the id and does nothing.
Stripe Idempotency-Keyrequests table with UNIQUE(request_id)↑ see it in the visualization - Uniqueness constraints via a partitioned log
Route all requests for the same value to one partition and decide them sequentially in log order.
A single-threaded processor per partition decides deterministically which request came first, with no locks or distributed transactions, and scales by adding partitions.
↑ see it in the visualization - Timeliness vs integrity
Timeliness: readers see an up-to-date state. Integrity: no corruption, no contradictory or lost data.
Violations of timeliness are temporary — wait and they heal. Violations of integrity are permanent until someone repairs them. Most applications need integrity strictly and can tolerate some lag.
↑ see it in the visualization - Coordination-avoiding data systems
Systems that preserve integrity using logs, deterministic derivation and end-to-end ids instead of synchronous cross-node coordination.
They keep working across datacenters and network faults, with better performance, while giving up only timeliness. Synchronous coordination can be reserved for the few places where it truly cannot be avoided.
↑ see it in the visualization - Compensating transactions (apologies)
Fixing a constraint violation after the fact: refund, reassign, or apologize to the customer.
Many businesses already tolerate brief violations (overbooked flights, oversold stock) because a cheap apology beats the cost of strict coordination. The question is how often it happens and how expensive each apology is.
↑ see it in the visualization - Trust, but verify (auditing)
Continuously checking the integrity of data — checksums, re-deriving views from the log, comparing replicas.
Disks rot, software has bugs, and checksums can vouch for a bug's output. Systems that are designed to be audited (immutable events, deterministic derivation) let you detect corruption early instead of trusting blindly.
HDFS block scannerZFS scrubCassandra repair↑ see it in the visualization - Cryptographic auditing
Chaining each log entry to the hash of the previous one (or using Merkle trees) so tampering is detectable.
Rewriting an old event breaks every hash after it. The same idea underlies certificate transparency and blockchains — a way to make the log's integrity verifiable by others.
Certificate TransparencyMerkle treesGit↑ see it in the visualization - Predictive analytics and responsibility
Automated decisions (loans, insurance, hiring, policing) driven by models trained on data.
Models can codify existing discrimination and create feedback loops, and the people affected rarely can appeal or even learn why. Engineers who build these systems share responsibility for their consequences.
↑ see it in the visualization - Privacy, tracking and data as liability
Behavioural data collected for one feature can reveal far more, and lives on beyond its purpose.
Every stored record can be breached, subpoenaed or repurposed after an acquisition. Collect less, keep it shorter, and treat data as a hazardous material rather than an asset.
↑ see it in the visualization