Here is a puzzle. Elasticsearch's inverted index is heavily compressed and interleaved on disk; inserting one document into the middle of it would mean rewriting enormous files. Yet the machine accepts hundreds of thousands of writes per second. This essay is about the trick that makes it possible, and the live simulation below lets you run it yourself.
The answer, inherited from the Lucene library at its core, is one of the great tricks of systems engineering: never modify files, only create new ones. Incoming documents accumulate in a memory buffer. On every refresh, the buffer is written out as a small, complete, immutable mini-index called a segment. A search simply asks every segment and combines answers. Deletion doesn't touch the segment either; the document is marked dead in a side file and filtered from results. And a background process constantly merges small segments into bigger ones. Below, the whole machinery is running live; you control its dials:
Every oddity of Elasticsearch falls out of this design. Why "near real-time" rather than instant? A document is searchable only after the next refresh. Why doesn't a crash lose the buffer? Every write is also appended to a plain journal, the translog, replayed on restart. Why does the machine spend idle time merging segments? Because searching a thousand tiny indexes is slower than ten large ones, and merging is when deleted documents are finally, physically, gone.
A detail the simulation glosses: merges aren't random. The policy is tiered: it looks for segments of similar size to combine, so bytes get rewritten roughly logarithmically many times over their life rather than again and again. And once an index stops receiving writes forever (yesterday's log index, say), a force-merge down to a single segment is the final kindness: one segment, maximum compression, minimum per-search overhead.
The simulation above refreshed once per second, the default, but that number is a choice, and it prices a real trade. (Elasticsearch already bends it: unless you set the interval yourself, a shard that has not been searched for 30 seconds stops refreshing until the next search arrives, so write-only indices skip the cost.) Every refresh cuts a segment, and small segments cost merges later; refresh rarely and indexing gets cheaper while new documents stay invisible longer:
Immutability hands out one more gift: a consistent view of an index is just a list of segments. Hold that list, and you hold a frozen moment, no matter what happens next. That is a point-in-time, and it is how a paginating client avoids seeing the data shift under its feet:
One worry should be nagging you: documents sit in a memory buffer until the next refresh, and even a fresh segment isn't guaranteed on disk until a periodic flush commits it. What if the machine dies in between? The answer is the translog: before anything else happens, every write is appended to a plain sequential journal and fsynced. Replaying a journal is trivial; that's the point. Pull the plug yourself, at any moment:
Immutability pays dividends everywhere: it is why replication and snapshots are cheap, and why the query engine can parallelize by segment.