Chris Collins

Tools · search

Elasticsearch, simulated

An animated, step-by-step Elasticsearch (and OpenSearch) cluster in your browser: murmur3 routing, primaries and replicas, the indexing buffer, translog, refresh, flush and segment merging inside every shard, query-then-fetch search, node failures, master elections, split brain and shard sizing — with guided scenarios and a REST console.

Added

Works with

How to use it

An Elasticsearch cluster in miniature that runs entirely in this page: a client, a coordinating node, three master-eligible nodes and a few data nodes holding the shards of an index — and inside every shard copy, its Lucene engine: the in-memory indexing buffer, the translog and the segments. Every request, replication, refresh, flush, merge, cluster-state publication and vote is drawn as it happens, and the rules — where a document goes, when it becomes searchable, what survives a crash, who may be master, when health turns yellow or red — follow Elasticsearch 7.x–9.x, with OpenSearch’s differences noted. Nothing here talks to a real cluster.

How to use it

Pick a scenario and press Start. Each step sets something up — indexes documents, searches, kills a node, cuts the network — then plays for a few simulated seconds and pauses so you can read what happened and why, with links to the Elasticsearch documentation. Next step moves on, Back rewinds (the simulator is deterministic, so it replays the earlier steps), ⏭ jumps to the end of the step and Step ⇥ advances to the next single thing that happens. Space plays and pauses; → is the next step. The Playground has no script.

Speed scales everything. Hop time is how long a message takes to cross a wire at 1×: while a drawn message is on its way the simulated clock slows, so a shard changes only when the message lands. Sim clock turns that off. Hop time changes only how the run is shown, never what happens. With reduced motion set in your system, messages jump instead of travelling.

⛶ Full screen (or F) gives the diagram the whole window — the browser’s full screen where it is allowed, otherwise a full-window layer (iPhone). H hides the narration and leaves a compact bar with the step, play and next controls; Escape leaves. The diagram is scaled to fit the window and redrawn when it changes size.

  • The diagram. At the top, the client, the coordinating-only node (it routes writes and scatters and gathers searches) and the master-eligible nodes, with ★ on the elected master and the current term, cluster-state version and voting configuration. Below, one card per data node (tier, zone, and pills for down, slow, cut off and no master), with a box for every shard copy: P primary or R replica, its sequence number and local and global checkpoints, and three rows — the indexing buffer (one chip per document not yet in a segment), the translog (one cell per operation since the last flush; filled = fsynced, outlined = not yet) and the segments (width by document count; blue = live documents, red hatching = deleted documents, dashed outline = not committed by a flush, faded = written but not yet searchable). Packets are labelled: an index request and the shard it was routed to, seq N replication and its ack, query / top k / fetch for search, v12 / commit for cluster-state publication, votes between master nodes, recovery transfers. Inside a shard, refresh, fsync, flush and merge _1+_2+_3 show where they act. A promoted replica flashes. On a phone the diagram stacks top to bottom and busy nodes switch to one line per shard. Tap a node or a shard to inspect it and act on it (kill, power cut, restart, slow down, cut off the network; refresh, flush or force-merge one shard copy).

  • Counters. Cluster health, the master, the cluster-state version and term, writes acknowledged and failed, acked but lost (acknowledged to the client but no longer in the current primary), searchable and not-yet-searchable documents, segments, deleted documents, unassigned shards and average search time.

  • Do something. Index documents (with an id, a text, a count and refresh=false|wait_for|true), delete or GET by id, steady traffic; search with a query and from, take the next page with search_after, open a point in time; _refresh, _flush, _forcemerge, change the number of replicas; kill, power-cut, restart, slow down or cut off any node; add a data node; heal the network.

  • REST console. Type requests as you would with curl or Kibana’s Dev Tools; responses come back when the simulated cluster answers, in Elasticsearch’s format. help lists what it understands: PUT /idx (with number_of_shards, number_of_replicas, refresh_interval, index.translog.durability), POST /idx/_doc, PUT /idx/_doc/<id>?routing=&refresh=, GET /idx/_doc/<id>?realtime=false, DELETE, GET /idx/_search?q=&size=&from= or a match query body with search_after and pit, POST /idx/_refresh, POST /idx/_flush, POST /idx/_forcemerge?max_num_segments=1, PUT /idx/_settings, POST /idx/_pit, POST /alias/_rollover, GET _cluster/health, GET _cluster/state, GET _cluster/allocation/explain, and _cat/health, _cat/nodes, _cat/master, _cat/indices, _cat/shards, _cat/segments, _cat/allocation (add ?v for headers).

  • Last search. The query, its phase, the time each shard took and on which copy, how many entries the coordinating node merged and kept, the top hits, and the adaptive replica selection statistics the coordinating node keeps for each data node.

  • Shard copies (like _cat/shards with the engine inside) and the event log, with a filter for cluster-state publications and elections.

  • Settings rebuild the cluster: Elasticsearch or OpenSearch, master-eligible and data nodes, a coordinating-only node, data tiers, zones and awareness, shards, replicas, the routing function, routing_partition_size, starting documents, refresh interval, translog durability and sync interval, wait_for_active_shards, delayed allocation timeout, OpenSearch segment replication, segments_per_tier, adaptive replica selection, legacy minimum_master_nodes, latency and seed.

The other tabs: Routing computes the shard for any _id or routing value, step by step, with both the 7.0–9.3 and the 9.4 functions and the textbook formula; Fan-out shows how search latency’s tail grows with the number of shards a query touches, and what deep pagination costs; Shard sizing plans primaries, shards per node and shard size for time-series data against Elastic’s guidance.

Scenarios

  1. Indexing a document — the coordinating node routes with murmur3, the primary assigns a sequence number and replicates to every in-sync copy, wait_for_active_shards as a pre-flight check (and the 503 when it cannot be met), the response.

  2. Near-real-time search — the indexing buffer, a search that finds nothing, a realtime GET that finds the document through the live version map, the refresh that creates a searchable segment, refresh=wait_for.

  3. Durability — the translog fsynced before each acknowledgement, flush as a Lucene commit, a power cut, store recovery replaying the translog, and durability: async losing the last acknowledged writes.

  4. Segments and merging — a segment per refresh, the tiered merge policy, updates as delete plus add, deleted documents reclaimed by merges, force-merge.

  5. Search — query then fetch, per-shard top from + size, the coordinating node’s merge, deep pagination, search_after with a point in time, a slow node, adaptive replica selection, fan-out and tail latency.

  6. Node failure — follower checks, replica promotion with a new primary term, yellow, delayed allocation, re-allocation with peer recovery, a returning node recovering ops-based, red.

  7. Master election — publish, acknowledge, commit; leader checks, pre-vote and election in a new term; losing the quorum; recovering it.

  8. Shard sizing, tiers and rollover — an over-sharded index, the 10–50 GB guidance, rollover through an alias, hot to warm relocation by _tier_preference, force-merging read-only data.

  9. Split brain — a partitioned master steps down without a quorum, the minority refuses writes, the majority carries on, healing; replayed with legacy minimum_master_nodes: 1, two masters and a lost acknowledged write.

  10. Playground — your own cluster.

What it models

  • Routing. shard = hash(_routing) % number_of_primary_shards is how the documentation explains it. Exactly: the hash is Elasticsearch’s Murmur3HashFunction — Lucene’s murmurhash3_x86_32 with seed 0 over the routing string’s UTF-16 code units, low byte first — and indices created by 7.0–9.3 compute floorMod(hash, routing_num_shards) / routing_factor, where routing_num_shards is the largest number_of_shards × 2^x ≤ 1024 so the index can be split; indices created by 9.4 use floorMod(hash, number_of_shards). routing_partition_size adds floorMod(hash(_id), size). The tests check these against Elasticsearch’s own Murmur3HashFunctionTests and IndexRoutingTests (all 99 values of testBWC).

  • Writes. The coordinating node resolves the shard and forwards to the primary; the primary checks wait_for_active_shards, assigns the next sequence number under its primary term, applies the operation, replicates it to every copy in its routing table, waits for all of them, fsyncs the translog when index.translog.durability: request, and answers with _shards, _seq_no, _primary_term and _version. A replica that does not answer is reported to the master and removed from the in-sync set before the write is acknowledged; a primary that cannot reach a master does not acknowledge (data replication model). Local and global checkpoints are tracked per copy. Updates are a delete of the old version plus an add; optimistic concurrency with if_seq_no returns 409 on a conflict. A write to an index that does not exist creates it (1 shard, 1 replica).

  • Inside a shard. New documents go to the indexing buffer and the translog, and their id into the live version map. A refresh (every index.refresh_interval, 1 s by default) writes the buffer as a new segment, applies pending deletes and opens it for search; a realtime GET of an id still in the version map refreshes only the internal reader. A flush is a Lucene commit: the buffer becomes a segment, every segment is fsynced, the commit point records the local checkpoint and the translog rolls to a new generation; it happens when the translog grows past a threshold or ages past flush_threshold_age, or on _flush. Merges follow Lucene’s TieredMergePolicy: the allowed segment count per tier from segments_per_tier, candidate merges of up to max_merge_at_once segments scored by skew, size and reclaimed deletes, too-large segments left alone unless they are mostly deletes; force-merge rewrites to max_num_segments. Search uses BM25 (k1 = 1.2, b = 0.75) with shard-local statistics, in which deleted documents still count until merged.

  • Crashes. Kill is a process crash: what reached the operating system survives (the whole translog). Power cut keeps only what was fsynced: the last commit and the fsynced part of the translog. On restart the master allocates a primary only to a node holding an in-sync copy on disk (store recovery: open the last commit, replay the translog above its checkpoint); replicas recover from the primary — ops-based (replaying only the missing operations, as retention leases allow) when the node had the shard, file-based (copying segments, then the operations since) otherwise.

  • Allocation. The master assigns primaries, then replicas once their primary is active, with the deciders that matter here: never two copies of a shard on one node (same_shard), _tier_preference (data_hot, data_warm, data_cold), zone awareness, and at most two incoming recoveries per node. Among allowed nodes it picks the lowest weight of the BalancedShardsAllocator: 0.45 × (shards − average) + 0.55 × (index shards − average); it rebalances (relocating) when that improves the weight by more than 1, and moves shards off a tier they no longer belong to. When a node leaves, its primaries are replaced by promoting an in-sync replica (primary term + 1) and its replicas wait index.unassigned.node_left.delayed_timeout (delayed allocation). Health is red with an unassigned primary, yellow with an unassigned replica, green otherwise.

  • Cluster coordination (the 7.0+ "Zen2" design, discovery and cluster formation). Master-eligible nodes elect a master by a quorum of the voting configuration, after a pre-vote, in ever-increasing terms. The master publishes every cluster-state change to all nodes, commits it once a quorum of master-eligible nodes has acknowledged and steps down if it cannot. Leader and follower checks detect failures (interval, retry count); a restarted node is a new node to the master. The voting configuration is kept odd-sized and never shrinks below three nodes on its own (as Reconfigurator does). With minMasters set the simulator instead behaves like legacy Zen discovery with discovery.zen.minimum_master_nodes: any that many visible master-eligible nodes may elect a master, and when two masters meet the one with the older cluster state yields.

  • Search. The coordinating node sends the query to one copy of each shard — chosen by adaptive replica selection (on by default since 7.0, C3: rank = R − 1/µ + q̂³/µ over EWMAs with α = 0.3, as ResponseCollectorService computes it) or round-robin — each shard returns its top from + size ids and scores, the coordinating node merges them and fetches the page’s documents from the shards that hold them. from + size > 10000 is refused (index.max_result_window). search_after and points in time are supported; a point in time keeps its segment readers. Each data node has two search threads; a slow node multiplies its service time.

OpenSearch

Set Flavour to OpenSearch (or flavor=os). OpenSearch forked from Elasticsearch 7.10, so the model above holds, with these differences:

  • The master is called the cluster manager; the coordination protocol is the same.

  • Routing keeps routing_num_shards (there is no 9.4-style modulo routing).

  • index.replication.type: SEGMENT (segment replication) is available: replicas write the translog but do not index; the primary copies its segments to them after each refresh, so replicas use less CPU but see new documents a little later. Realtime GETs go to the primary (segment replication).

  • There is no _tier_preference or ILM: nodes are tagged with an attribute and indices move with Index State Management (ISM) policies.

Limits

  • A handful of nodes and shards; documents are a short title field; only match-style queries (BM25 on that field), no aggregations, mappings, analyzers beyond lower-cased words, or scroll.

  • Timeouts are shorter than Elasticsearch’s so failures play out in seconds (each setting shows the real default). The flush threshold is a number of operations standing in for flush_threshold_size. Merge sizes are counted in documents, and the merge policy is Lucene’s algorithm reduced to those sizes. Network latency is fixed.

  • Not modelled: the write-load and disk-usage terms of the allocator, disk watermarks, shard request caching, search_idle (described, not simulated), translog retention for recovery (retained history is assumed), primary-replica resync after promotion, cross-cluster features, snapshots, stateless/serverless Elasticsearch, and the 8.x/9.x merge policy for time-series data (LogByteSizeMergePolicy).

  • The shard-sizing tab is guidance arithmetic, not a benchmark; the fan-out tab assumes independent lognormal shard latencies.

Linking to an example

Links can be written by hand. Add a tab after #: sim (the default), routing, fanout or sizing. A link’s settings replace this page’s saved settings (a button restores yours).

Parameter Meaning Example

example

A scenario (list below), with its own cluster; parameters given with it override the scenario’s

example=split-brain

scenario

The same, without the scenario’s own settings

scenario=search

flavor

es (Elasticsearch) or os (OpenSearch)

flavor=os

masters

Master-eligible nodes, 1–5

masters=5

dataNodes

Data nodes, 1–8

dataNodes=4

coord

A coordinating-only node (true/false)

coord=false

tiers

hot, hot-warm or hot-warm-cold

tiers=hot-warm

zones

Zones the data nodes are spread over, 1–3

zones=2

awareness

Shard allocation awareness by zone

awareness=true

shards

Primary shards of the first index, 1–12

shards=6

replicas

Replicas, 0–3

replicas=2

routing

legacy (routing_num_shards, 7.0–9.3 and OpenSearch) or modulo (9.4+)

routing=modulo

partition

routing_partition_size

partition=2

docs

Documents already in the index at the start, 0–200

docs=100

refreshMs

refresh_interval in ms (-1 turns refresh off)

refreshMs=5000

durability

index.translog.durability: request or async

durability=async

syncMs

index.translog.sync_interval in ms

syncMs=5000

waitFor

Default wait_for_active_shards: 1–4 or all

waitFor=all

delayedMs

index.unassigned.node_left.delayed_timeout in ms

delayedMs=10000

replication

OpenSearch index.replication.type: document or segment

replication=segment

segsPerTier

index.merge.policy.segments_per_tier

segsPerTier=10

ars

Adaptive replica selection

ars=false

minMasters

Legacy Zen discovery.zen.minimum_master_nodes (0 = modern quorums)

minMasters=1

latency

One-way network latency in ms

latency=60

seed

Random seed (document text, ids, timings)

seed=7

speed

Playback speed: 0.25, 0.5, 1, 2, 4 or 8

speed=2

hop

Hop time: seconds a message takes to cross a wire at 1× (0.25, 0.5, 1, 2, or 0.1–5), or clock

hop=1

Scenarios:

example= Shows

index-doc

Indexing a document: routing, primary then replicas, wait_for_active_shards, the response (open)

nrt

Near-real-time search: the buffer, refresh, realtime GET, refresh=wait_for (open)

durability

Translog fsync, flush, a power cut and translog replay; async durability losing writes (open)

merging

Segments, the tiered merge policy, deleted documents, force-merge (open)

search

Query then fetch, deep pagination, search_after and PIT, adaptive replica selection, fan-out (open)

node-failure

Replica promotion, yellow, delayed allocation, recovery, red (open)

master-election

Cluster-state publication, master election, losing and regaining the quorum (open)

sizing

Over-sharding, rollover, hot to warm, force-merge (open)

split-brain

A network partition with quorums — and with minMasters=1 (open)

playground

A cluster with 30 documents to experiment with (open)

Sources: the Elasticsearch reference (replication model, near-real-time search, translog, merge settings, cluster health, allocation, discovery and cluster formation, voting configurations, pagination, point in time, adaptive replica selection); Elasticsearch source (Murmur3HashFunction, IndexRouting, RoutingFunction, MetadataCreateIndexService, Reconfigurator, ResponseCollectorService, WeightFunction, MergePolicyConfig, IndexSettings); Lucene’s TieredMergePolicy and BM25Similarity; Size your shards; A new era for cluster coordination in Elasticsearch; Suresh et al., C3: Cutting Tail Latency in Cloud Data Stores via Adaptive Replica Selection (NSDI 2015); Dean and Barroso, The Tail at Scale (CACM 2013).

Runs entirely in your browser; nothing you type leaves this page.