System Design Problem

Design an Elasticsearch Search Cluster

Commonly Asked By:ElasticAmazonNetflixUberGitHub

Interview Setup

Interview Prompt

Design a managed Elasticsearch search cluster in an Elastic Cloud style deployment holding 50 TB of indexed documents, serving 20K peak search queries per second and ingesting 100K documents per second. Cover cluster node roles, sharding and replication, inverted index internals, bulk indexing with backpressure, query scatter and gather, ILM tiering, cluster state management, and JVM circuit breaker failure modes.

Clarifying Questions (ask before designing)

QuestionWhy it matters
Is this Elasticsearch as a product or building Google web search?Web Search Engine is a web scale crawler, PageRank, and indexing pipeline for the public internet. This problem focuses on operating Elasticsearch as a managed search and log analytics engine with inverted Lucene indices, shard routing, and ILM rather than designing a search algorithm from scratch.
How does this differ from Log Aggregation where Elasticsearch serves as the sink?Log Aggregation designs the ingestion pipeline from Kafka and log shippers to Elasticsearch. This problem owns the Elasticsearch cluster itself, including shard sizing, tiering, query performance, and cluster health. Log Aggregation is the upstream producer, while this problem is the search infrastructure product.
What document types and query patterns?Mixed workloads include structured logs with timestamp and keyword filters plus aggregations, full text product search with BM25 scoring, and tenant scoped queries. This drives mapping design (text vs keyword), routing strategy, and cache invalidation patterns.
Consistency vs near real time visibility?Elasticsearch search is near real time rather than transactionally consistent. In Elastic Stack deployments the refresh interval defaults to 1s for active indices, while backfills can use a longer interval. A staff level probe is search_after with point in time vs scroll for deep pagination.
Single cluster or multi tenant SaaS?A multi tenant SaaS deployment can use _routing by tenant_id as the default locality strategy. Enterprise customers may receive dedicated indices or clusters. Access control still comes from authenticated tenant identity, filtered aliases, or document level security rather than routing alone.

Scope

In scope

  • Cluster node roles: master eligible, data, ingest, coordinating
  • Primary shards, replicas, and custom _routing keys
  • Inverted index, doc values, refresh interval (NRT)
  • Bulk indexing API with backpressure and thread pool limits
  • Query scatter and gather on coordinating nodes
  • ILM: hot/warm/cold/frozen storage tiers
  • Cluster state management and split brain prevention
  • JVM heap limits, circuit breakers, fielddata vs doc_values

Out of scope (state explicitly)

  • Building a web crawler or PageRank (Web Search Engine)
  • Log shipper and Kafka pipeline design (Log Aggregation serves as the upstream producer)
  • Vector ANN search (Vector Database serves dense HNSW, while Elasticsearch serves the sparse BM25 leg)
  • Kibana UI implementation (consume cluster APIs only)

Functional Requirements

Start by asking your interviewer whether you are operating Elasticsearch as a managed product or building a Web Search Engine from scratch. Clarify ingest rate, search QPS, aggregation workloads, and whether a Vector Database & Semantic Search system uses Elasticsearch as the sparse BM25 leg.

  • Document indexing: Single document and bulk (_bulk) ingest with optional ingest pipelines for parsing, enrichment, and filtering.
  • Full text search: BM25 scoring over analyzed text fields with bool, match, and phrase queries.
  • Structured filtering: Term, range, and bool filters on keyword, numeric, and date fields via doc values.
  • Aggregations: Metrics (count, average, percentiles) and bucket aggregations (terms, date_histogram) for analytics dashboards.
  • Index management: Create and delete indices, mappings, aliases, index templates, and ILM policies.
  • Shard routing: Custom _routing key for colocating related documents on a single shard.
  • Replication: Configurable replica count per index for high availability and read scaling.
  • Snapshots: Point in time backup and restore to object storage (Amazon S3).
  • Multi tenancy: Tenant locality through routing and access control through filtered aliases, document level security, or dedicated indices and clusters.
  • Hybrid search integration: Serve the sparse BM25 leg for Vector Database & Semantic Search hybrid queries.

Non-Functional Requirements

Interviewers frequently stress-test the operational tension between near real time search (~1s refresh) and bulk ingestion at 100K docs/sec. Tuning refresh_interval per index resolves this trade off. Index lifecycle management tiering controls storage costs at 50 TB, while JVM heap saturation and circuit breaker trips represent the primary failure points under 10x query load.

  • Scale: 50 TB indexed data, 20K peak search QPS, and 100K peak document ingest/sec.
  • Latency: p99 search latency < 200ms for simple queries and < 500ms for heavy aggregations.
  • Near real time: Documents become searchable after refresh, with ~1 to 5 seconds as the planning target and longer intervals during bulk backfills.
  • Availability: 99.9% uptime target supported by replica shards, dedicated masters, and multi-AZ allocation.
  • Durability: Replicated shards and Amazon S3 snapshots taken every 15 minutes to meet the stated recovery point objective when snapshots complete successfully.
  • Elasticity: Add data, ingest, and coordinating nodes without planned downtime, with background shard relocation and rebalancing managed by the cluster.
  • Cost efficiency: ILM tiering can materially reduce local storage cost by moving colder data to searchable snapshots. This design uses approximately 10x as a planning assumption rather than a universal savings guarantee.

Capacity Estimations

Shard count directly dictates query fan out, requiring teams to evaluate 50 TB against target shard sizes before provisioning hardware. Unpaginated list queries at high volume introduce severe out of memory risks, and coordinating node capacity directly mirrors peak query throughput.

MetricCalculationValue
Total indexed dataGiven50 TB
Peak search queries / secGiven20K
Peak document ingest / secGiven100K docs/sec
Avg logical document payloadLogs average ~2 KB and product docs average ~5 KB, using 3 KB as a planning assumption~3 KB
Logical document count (approx)50 TB ÷ 3 KB, treating the 50 TB planning figure as source equivalent payload~17B docs
Target shard sizeRecommended range 10-50 GB/shard, with this design targeting ~40 GB/shard~40 GB/shard
Primary shards needed50 TB ÷ 40 GB~1,250 primaries
Replica factor1 replica for HA + read scale2x total (~100 TB primary + replica store before other overhead)
Data nodes (hot tier)100 TB replicated data ÷ 8 TB usable/node ≈ 12.5, then add headroom~14 hot nodes
Ingest throughput (raw)100K x 3 KB~300 MB/sec
Query fan out per request1,250 shards x 20K QPS (worst case)25M shard queries/sec*
Coordinating nodes20K QPS ÷ 2K QPS per node planning assumption, then add headroom~10-15 coordinating nodes
JVM heap per data node≤ 50% RAM and below compressed ordinary object pointer threshold~26-30 GB heap on 64 GB nodes, verify compressed oops

*Worst-case fan out assumes queries execute without routing, whereas production clusters can use _routing to reduce the shard set for tenant scoped requests. Targeting ~40 GB per primary yields approximately 1,250 primary shards for a 50 TB dataset. With 1 replica per shard, that is roughly 2,500 shard copies. At 8 TB usable capacity per node, ~14 hot data nodes provide about 112 TB of usable capacity before operational headroom. Ingestion at 100K docs/sec and 3 KB average size produces ~300 MB/sec of raw payload. Actual shard level query QPS depends on routing, query mix, replica selection, and workload skew, so it should be benchmarked rather than inferred from a fixed per node number.

Architecture Diagram

Clarify scope early: this system designs Elasticsearch as a managed search product rather than a Web Search Engine or the upstream pipeline in Log Aggregation & Search. Clients index documents via _bulk and query via _search, while the platform maintains cluster health, shard sizing, and index lifecycle management.

Data partitions into Lucene shards hosted on data nodes, while a dedicated master tier manages cluster state through leader election and consensus. Coordinating nodes scatter queries across relevant shards and merge results. At 20K QPS, coordinating nodes scale horizontally without requiring additional shard storage.

The architecture separates the write path, where ingest nodes pass bulk batches to data shards, from the read path, where coordinating nodes execute scatter and gather queries and merge the results. A Vector Database & Semantic Search platform can also consume this cluster as the sparse BM25 leg of hybrid semantic search, preserving dense HNSW search inside the vector database.

Loading...

In the room

Say: "Elasticsearch as a managed service, not a Google web crawler." If the interviewer mentions oversharding, explain that 1,250 primaries at 20K QPS creates a dangerous fan out pattern when requests cannot be routed to a narrow shard set. Tenant queries should use _routing when the data model permits it.

Query Scatter and Gather

Loading...

Inverted Index Internals

Loading...

Component Deep Dives

1. Cluster Node Roles ⭐

Next we walk through each component. Node roles and shard routing represent the essential core mechanics, while cluster coordination and JVM fielddata traps demonstrate staff level depth.

At scale, dedicating master and coordinating roles reduces the chance that data node garbage collection pauses destabilize cluster coordination.

  • Master-eligible: Maintains cluster state including indices, mappings, and shard allocations. One active master coordinates cluster state changes through Elasticsearch cluster coordination. Three dedicated master eligible nodes provide a resilient voting configuration while keeping master duties separate from data workloads.
  • Data: Stores shard data on local disk, using fast storage for hot tiers and lower cost storage for colder tiers. Executes indexing and shard local search. Keep heap at no more than about 50% of RAM and below the compressed ordinary object pointer threshold, which commonly means roughly 26 to 30 GB on a 64 GB host.
  • Ingest: Runs pre-indexing ingestion pipelines before documents reach data shards. Offloads compute heavy grok parsing and GeoIP lookups from data nodes during 100K docs/sec ingestion bursts.
  • Coordinating: Stateless entry points that handle client requests, scatter queries to relevant shards, and merge and sort result sets. They scale horizontally for 20K QPS without adding shard storage.

2. Sharding, Replicas, and Routing

Primary shard counts are fixed at index creation. Using _routing co locates tenant documents so tenant scoped queries can touch one shard instead of thousands, as detailed in Sharding and Partitioning.

Index creation (shard count immutable without reindex):
  PUT /products
  { "settings": { "number_of_shards": 32, "number_of_replicas": 1 } }

Document routing:
  shard_id = hash(_routing ?? _id) % number_of_shards

  POST /products/_doc/123?routing=tenant_acme
  → always lands on same shard for tenant_acme

Query with routing (single-shard):
  GET /products/_search?routing=tenant_acme
  { "query": { "match": { "title": "laptop" } } }

Replica reads: coordinating nodes normally use adaptive replica selection to choose among eligible shard copies using observed response time, service time, and search queue load.
For this HA baseline, keep at least 1 replica per primary. A zero replica index can still be valid for explicitly disposable or fully rebuildable data, but it removes protection against a primary node failure.

3. Bulk Indexing and Backpressure

Receiving HTTP 429 during bulk ingestion indicates thread pool saturation, meaning clients must apply exponential backoff rather than increasing worker concurrency.

POST /logs-write/_bulk
Content-Type: application/x-ndjson

{"index":{"_id":"evt_1","routing":"tenant_acme"}}
{"@timestamp":"2026-06-30T10:00:00Z","tenant_id":"tenant_acme","message":"order placed"}
{"index":{"_id":"evt_2","routing":"tenant_acme"}}
{"@timestamp":"2026-06-30T10:00:01Z","tenant_id":"tenant_acme","message":"payment captured"}

Response: 200 OK
{"errors":false,"took":42,"items":[...]}

Client best practices (100K docs/sec target):
  1. Batch 3K to 5K docs or 5 MB to 15 MB per bulk request
  2. Start with 8 to 16 parallel bulk workers per ingest pipeline and tune from measured thread pool pressure
  3. Set refresh_interval: 30s during backfill, then restore 1s after
  4. Use _routing for colocated tenant docs
  5. On 429 / EsRejectedExecutionException: exponential backoff (1s, 2s, 4s and so on)
  6. Monitor thread pools: bulk, write, search queue depth

Ingest node pipeline (optional):
  PUT _ingest/pipeline/enrich-geo
  { "processors": [ { "geoip": { "field": "ip" } }, { "set": { "field": "env", "value": "prod" } } ] }
  Normalizes documents before they reach the data node shard

Each data node has execution pools for indexing and search work. When capacity is exhausted, Elasticsearch can reject requests with HTTP 429. Clients should back off rather than simply adding more workers, because additional concurrency can deepen queue pressure and increase segment merge contention.

The write is acknowledged according to translog durability and active shard requirements, not according to search visibility. With the default translog durability, acknowledged operations are durably committed to the primary and allocated replicas before the request succeeds, while refresh separately controls when the document becomes searchable.

WRITE PATH VS SEARCH VISIBILITY:

1. Client sends _bulk request to the coordinating or ingest tier
2. Request is routed to the primary shard
3. Primary applies the operation and records it in the translog
4. Operation is replicated to allocated replica shards
5. With index.translog.durability = request (the default), the translog is fsynced and committed before the write request is acknowledged
6. A later refresh opens the new Lucene segment for search visibility

Key distinction:
  durability acknowledgement != search visibility
  translog durability protects acknowledged writes before the next Lucene commit
  refresh controls when those writes become visible to _search

Write availability:
  wait_for_active_shards controls how many shard copies must be active before the write proceeds
  default = 1, meaning the primary must be active

4. ILM Tiering (Hot, Warm, Cold, and Frozen)

Index Lifecycle Management can move aging indices through hot, warm, cold, and frozen phases. Searchable snapshots can materially reduce local storage requirements, and this policy introduces them in the frozen tier after cold retention. Actual savings depend on access patterns, replicas, snapshot storage, and licensing.

JSON
// ILM Policy: logs-hot-warm-cold-frozen

{
  "policy": {
    "phases": {
      "hot": {
        "actions": {
          "rollover": { "max_primary_shard_size": "50gb", "max_age": "1d", "min_primary_shard_size": "10gb" },
          "set_priority": { "priority": 100 }
        }
      },
      "warm": {
        "min_age": "7d",
        "actions": {
          "forcemerge": { "max_num_segments": 1 },
          "read_only": {},
          "migrate": {},
          "set_priority": { "priority": 50 }
        }
      },
      "cold": {
        "min_age": "30d",
        "actions": {
          "read_only": {},
          "migrate": {},
          "set_priority": { "priority": 10 }
        }
      },
      "frozen": {
        "min_age": "90d",
        "actions": {
          "searchable_snapshot": { "snapshot_repository": "s3-repo" }
        }
      },
      "delete": {
        "min_age": "365d",
        "actions": { "delete": {} }
      }
    }
  }
}
YAML

Hot: NVMe, full read/write, frequent refresh, replicas for query scale
Warm: lower cost storage for read mostly indices, with force merge as a measured optimization. This design does not shrink the index because the hot tier already targets about 40 GB per primary, while a 50 GB shrink target would usually leave the primary count unchanged.
Cold: lower-cost read-only data nodes retain regular shards and replicas in this policy
Frozen: partially mounted searchable snapshots provide the lowest-cost searchable retention
Searchable snapshots require a deployment and license tier that supports the feature, such as the Enterprise license in current Elastic documentation

5. Cluster State and Split Brain Prevention

Deploy a small dedicated master tier and let modern Elasticsearch maintain the voting configuration and quorum automatically. Data nodes should not carry master responsibilities at this scale. The important operational rule is to avoid obsolete quorum settings and to treat cluster.initial_master_nodes as a bootstrap only setting.

Cluster State (master tier responsibility):

  Contents: index definitions, mappings, aliases, shard routing table,
            ILM policies, ingest pipelines, persistent cluster settings

  Update flow:
    1. Master receives create-index / mapping change / shard started event
    2. Validates (shard limits, mapping conflicts)
    3. Publishes new cluster state version to all nodes
    4. Data nodes apply allocation by moving or creating shards

  Cluster coordination and bootstrap:
    - 3 dedicated master eligible nodes for this cluster
    - Modern Elasticsearch manages the voting configuration and quorum automatically
    - cluster.initial_master_nodes is used only for first bootstrap of a brand-new cluster, then removed
    - Voting-only masters participate in elections without storing document data

  Dangerous operations:
    - Deleting an index without a verified snapshot
    - Closing an index and forgetting that reads and writes are blocked until it is reopened
    - Changing the type or incompatible mapping of an existing field, which requires reindexing
    - Allowing uncontrolled dynamic field creation, which can cause mapping explosion

6. JVM, Circuit Breakers, and fielddata vs doc_values

Enforce doc_values on keyword fields for aggregations and sorting, and strictly disallow fielddata on analyzed text fields at production scale.

  • doc_values: Columnar on disk storage used for sorting, aggregations, and field value access. It is enabled by default for most field types, but not for text.
  • fielddata: On heap field values used for operations such as aggregations on fields that do not have suitable doc_values. It is disabled by default on text fields. Enabling it for large analyzed text fields can create severe heap pressure, so use keyword subfields with doc_values where exact aggregation or sorting is required.
  • Circuit breakers: Parent and child breakers prevent requests from reserving excessive memory. Circuit breaker rejections return HTTP 429. Keep modern defaults unless measurements justify a change, and fix sustained memory pressure by reducing query demand or scaling the cluster rather than raising limits blindly.
  • Query cache: Caches eligible filter context query results at the node level using an LRU policy. Segment changes can invalidate cached results. A gateway or application cache can add short time to live caching for safe repeated tenant queries.

7. Relationship to Adjacent Systems

Understanding how Elasticsearch integrates with surrounding systems clarifies why it functions as a managed inverted index engine rather than a general crawler or vector database.

  • Web Search Engine: Focuses on crawling, HTML document extraction, link graph indexing, and PageRank scoring to construct a search engine from scratch. In contrast, this design operates Elasticsearch and Lucene as a distributed search product.
  • Log Aggregation & Search: Focuses on the end to end ingestion pipeline across Kafka and shippers into Elasticsearch. Log aggregation owns the ingestion topology, while this design governs cluster sizing, ILM tiering, and query performance for that sink.
  • Vector Database & Semantic Search: Focuses on dense HNSW approximate nearest neighbor search. Elasticsearch supplies the complementary sparse BM25 retrieval leg for Reciprocal Rank Fusion, functioning alongside rather than replacing vector databases.

API Design

Bulk Index Documents

Elasticsearch APIs are index centric. Demonstrate bulk ingestion with _routing for tenant locality, followed by search requests that use bool filters on keyword fields. Always paginate list operations, and use search_after instead of from/size for deep pages.

HTTP
POST /logs-write/_bulk
Content-Type: application/x-ndjson

{"index":{"_id":"evt_1001","routing":"tenant_acme"}}
{"@timestamp":"2026-06-30T14:00:00Z","tenant_id":"tenant_acme","level":"ERROR","message":"payment timeout"}
{"index":{"_id":"evt_1002","routing":"tenant_acme"}}
{"@timestamp":"2026-06-30T14:00:01Z","tenant_id":"tenant_acme","level":"INFO","message":"retry succeeded"}

Response: 200 OK
{
  "took": 38,
  "errors": false,
  "items": [
    { "index": { "_index": "logs-000001", "_id": "evt_1001", "status": 201, "result": "created" } },
    { "index": { "_index": "logs-000001", "_id": "evt_1002", "status": 201, "result": "created" } }
  ]
}

Search with Filters and Aggregations

HTTP
GET /logs-read/_search?routing=tenant_acme
Content-Type: application/json

{
  "size": 20,
  "query": {
    "bool": {
      "must": [{ "match": { "message": "payment timeout" } }],
      "filter": [
        { "term": { "tenant_id": "tenant_acme" } },
        { "term": { "level": "ERROR" } },
        { "range": { "@timestamp": { "gte": "now-1h" } } }
      ]
    }
  },
  "aggs": {
    "errors_per_minute": {
      "date_histogram": { "field": "@timestamp", "fixed_interval": "1m" }
    }
  },
  "sort": [{ "@timestamp": "desc" }],
  "track_total_hits": 10000
}

Response: 200 OK
{
  "took": 47,
  "hits": {
    "total": { "value": 3, "relation": "eq" },
    "hits": [{ "_id": "evt_1001", "_score": 4.2, "_source": { ... } }]
  },
  "aggregations": { "errors_per_minute": { "buckets": [{ "key_as_string": "...", "doc_count": 3 }] } }
}

Index Template and ILM Policy

HTTP
PUT _index_template/logs-template
{
  "index_patterns": ["logs-*"],
  "template": {
    "settings": {
      "number_of_shards": 10,
      "number_of_replicas": 1,
      "index.lifecycle.name": "logs-hot-warm-cold-frozen",
      "index.lifecycle.rollover_alias": "logs-write",
      "refresh_interval": "1s"
    },
    "mappings": {
      "properties": {
        "@timestamp": { "type": "date" },
        "tenant_id":    { "type": "keyword" },
        "message":      { "type": "text", "fields": { "keyword": { "type": "keyword" } } },
        "level":        { "type": "keyword" }
      }
    }
  },
  "aliases": {
    "logs-read": {}
  }
}

The first concrete index must exist before the rollover alias can accept writes. The template attaches logs-read to each generated index, while ILM owns creation of subsequent indices and advances logs-write atomically.

The example template uses 10 primary shards to keep the API example concrete. The 50 TB capacity model is separate: it targets approximately 1,250 primary shards across the retained index set, so production shard counts per rolled index must be selected from target shard size, rollover cadence, and query workload.

HTTP
PUT logs-000001
{
  "aliases": {
    "logs-write": { "is_write_index": true }
  }
}

Cluster Health and Shard Allocation

HTTP
GET /_cluster/health
Response: { "status": "green", "number_of_nodes": 27, "active_shards": 2500, ... }

GET /_cat/shards/logs-000001?v&h=index,shard,prirep,state,docs,store,node
→ verify even distribution across AZs

PUT /_cluster/settings
{ "persistent": { "cluster.routing.allocation.enable": "all" } }
# During tightly controlled rolling maintenance, use "none" only when shard recovery and failover are deliberately managed

Common Error Responses

400 Bad Request: invalid input, missing required fields, or malformed JSON payload
401 Unauthorized: missing or invalid authentication token or API key
403 Forbidden: authenticated caller lacks required permissions for this resource
404 Not Found: requested resource ID does not exist
409 Conflict: duplicate write or version conflict, retry with a unique idempotency key
422 Unprocessable Entity: syntactically valid request failed semantic business validation
429 Too Many Requests: rate limit quota exceeded, client should honor Retry-After header
500 Internal Error: unexpected server failure, retry safely with an idempotency key
503 Service Unavailable: downstream dependency is unavailable or overloaded, retry with exponential backoff
504 Gateway Timeout: search index shard responded slowly, narrow query parameters or retry

Data Model

Index Mapping (Logs)

{
  "mappings": {
    "properties": {
      "@timestamp": { "type": "date" },
      "tenant_id":    { "type": "keyword" },
      "service":      { "type": "keyword" },
      "level":        { "type": "keyword" },
      "trace_id":     { "type": "keyword" },
      "message": {
        "type": "text",
        "analyzer": "standard",
        "fields": {
          "keyword": { "type": "keyword", "ignore_above": 256 }
        }
      },
      "duration_ms":  { "type": "long" },
      "geo":          { "type": "geo_point" }
    }
  }
}

Design rules:
  - text for full text search (inverted index)
  - keyword for filters, aggregations, and routing
  - Avoid dynamic mapping in production: explicit templates prevent mapping explosions

Index Naming and Aliases

logs-000001         ← concrete index (ILM rollover target)
logs-write           ← write alias → current write index
logs-read            ← read alias → all retained search indices
tenant_acme-*        ← filtered alias for an enterprise tenant

Rollover trigger: largest primary shard reaches 50 GB OR age > 1 day
ILM creates the next write index and moves logs-write

Cluster State (Master Managed Metadata)

Cluster state includes:
  - indices: { name, settings, mappings, aliases }
  - routing_table: shard_id → [node_ids] (primaries + replicas)
  - nodes: { id, roles, attributes (tier: hot/warm/cold) }
  - blocks: read only flags (disk watermark flood stage)

Persisted by the cluster coordination layer and published to participating nodes on each version change.
Large cluster state (> 10K shards) increases master processing and publication cost, reinforcing why oversharding must be avoided.

Snapshot Repository (Amazon S3)

HTTP
PUT _snapshot/s3-repo/daily-2026.06.30
{
  "indices": "logs-*",
  "ignore_unavailable": true,
  "include_global_state": false
}

Restore (DR):
  POST _snapshot/s3-repo/daily-2026.06.30/_restore
  { "indices": "logs-000001", "rename_pattern": "(.+)", "rename_replacement": "restored-$1" }

Because this example sets include_global_state to false, cluster level configuration such as templates, aliases, and other recovery metadata must be managed separately. The backup plan should therefore cover both indexed data and the configuration required to recreate the write and read paths.

Fault Tolerance

Fault Tolerance Scenarios

ConcernSolution
Split brain where two masters appear to elect simultaneouslyUse at least 3 dedicated master eligible nodes. For this design, 3 is the baseline and 5 is appropriate when an additional master failure margin is required. Three masters tolerate one master failure, while five tolerate two, assuming the remaining voting configuration can still form quorum. For an HA cluster, at least 2 master eligible nodes should not be voting-only. Modern Elasticsearch manages the voting configuration automatically. Set cluster.initial_master_nodes only during the first bootstrap of a brand new cluster, then remove it. Avoid assigning data node workloads to master eligible nodes at this scale.
Yellow cluster with unassigned replica shardsAdd data nodes or reduce replica count temporarily. Check disk watermark thresholds because flood stage blocks allocation. Enforce shard allocation awareness to spread replicas across availability zones following replication best practices. Use cluster reroute API for manual rebalances.
Red cluster when a primary shard is lostRestore from S3 snapshots. If a replica exists on another node, promote replica to primary automatically. Never delete an index to resolve a red cluster because that causes permanent data loss. Run snapshots every 15 minutes for critical indices.
Circuit breaker trips due to query out of memory risksCircuit breakers track estimated memory use and reject requests before heap exhaustion. Reduce aggregation cardinality with terms size limits, avoid fielddata on analyzed text by using keyword sub-fields and doc_values, and cancel pathological requests through the tasks API. Treat breaker limits as safety boundaries rather than the primary capacity control, and scale data nodes or reduce query fan out when sustained memory pressure remains.
Bulk indexing overwhelms clusterApply backpressure when Elasticsearch returns HTTP 429 because an execution queue or other capacity limit is saturated. Clients use exponential backoff. Tune bulk batches to 5-15 MB or 1K-5K documents. Increase refresh_interval to 30s during bulk operations, and deploy dedicated ingest nodes with index lifecycle rollover.
Hot node disk full causing blocked indexingTrigger ILM rollover when the largest primary shard reaches the configured target, such as 50 GB, or the index reaches the age threshold of 1 day. Migrate older indices through warm, cold, and frozen phases. Configure low, high, and flood stage disk watermark alerts. Force-merge read only warm indices when measured segment reduction justifies the I/O cost.
Cross tenant query scans all shardsUse the _routing key (tenant_id) at index time so tenant queries can target one shard per targeted index. Provision dedicated indices or clusters for enterprise isolation where required. Use filtered aliases or document level security for access control on shared indices, and cache frequent safe queries at the coordinating layer using caching best practices. Routing improves locality but is not an authorization boundary.

Additional Considerations

Pagination: search_after vs scroll vs from/size

The default from/size result window is 10,000 hits, and deep pagination becomes expensive because the coordinating node must collect and sort skipped hits. The scroll API is intended for batch style extraction rather than interactive user pagination. For user facing deep pagination, use search_after, often with a point in time snapshot when a stable view across refreshes is required, plus an explicit unique sort order.

Mapping Explosion

Mapping explosion comes from an excessive number of mapped fields, not simply from high cardinality values inside one field. Dynamic object keys can create thousands of distinct fields and inflate cluster state and mapping metadata. Configure index.mapping.total_fields.limit, prefer explicit mappings and templates, and reject unexpected fields in production ingestion paths. High cardinality values such as millions of distinct user_agent strings are a separate aggregation and memory concern.

Security and Multi Tenancy

Use document level security, filtered aliases, or dedicated indices and clusters for regulated workloads. Enforce tenant authorization from authenticated identity at the gateway and search layer rather than trusting a client supplied tenant_id. Custom _routing co locates tenant data for performance and query locality, but it is not an authorization boundary.

Interview Walkthrough

  • 25-minute interview structure
    • Clarify scope: Elasticsearch as a managed service vs web search crawler vs log aggregation sink (5 min)
    • Cluster node roles: dedicated masters, data nodes, ingest pipelines, and coordinating nodes (6 min)
    • Shard sizing and capacity: 50 TB ÷ 40 GB yielding ~1,250 primaries, with warnings on oversharding fan out (5 min)
    • Inverted index and doc values: why fielddata on text is strictly avoided (5 min)
    • Bulk ingestion: batch sizes, thread pool limits, refresh interval tuning, and exponential backoff on HTTP 429 (4 min)
  • Scatter and gather query path: coordinating node heap merges and custom _routing for tenant locality.
  • ILM tiering: automated transitions across hot, warm, cold, and frozen storage to manage cost at 50 TB.
  • Split brain prevention: 3 dedicated masters, odd node counts, and quorum voting rules.
  • Circuit breakers and JVM heap: identifying bottlenecks and failure modes under 10x traffic spikes.
  • Hybrid search integration: positioning Elasticsearch as the sparse BM25 leg alongside vector search.

Engineering Trade-offs

More Shards vs Larger Shards

Configuring more primary shards improves parallel indexing per node but increases query fan out because every shard must execute the query. Conversely, fewer and larger shards (around 50 GB) reduce internal sub-query overhead but slow down shard relocations and node recovery. The planning target is 20 to 50 GB per shard, with this design using approximately 40 GB and therefore about 1,250 primaries for 50 TB. Because changing primary shard counts requires full reindexing, teams must plan this allocation upfront.

refresh_interval: 1s vs 30s vs -1

A 1-second refresh interval delivers near real time visibility for interactive search and log tailing in Log Aggregation & Search. A 30-second interval can materially increase indexing throughput during bulk ingest backfills, depending on workload and segment merge pressure. Setting the interval to -1 disables automatic refreshes entirely, maximizing ingestion speed at the expense of real-time searchability. Teams should toggle these values per index through ILM phases rather than applying cluster-wide overrides.

Replicas for Read Scale vs Storage Cost

Each replica doubles required disk storage and replication network traffic across nodes. Maintaining 1 replica provides a second shard copy for high availability and can add search capacity, subject to query mix, node resources, and replica allocation. Using 2 replicas triples storage costs for hot, heavily queried indices. The frozen storage tier uses searchable snapshots with minimal replicas, trading query latency for significant cost reductions.

Index per Tenant vs Shared Index with Routing

An index per tenant model provides clear logical isolation and simpler per tenant deletion, but does not create a physical hardware boundary unless tenants also receive separate nodes or clusters. Cluster state metadata can also grow substantially at 10,000 tenants. A shared index using _routing minimizes shard count and improves hardware density, but introduces noisy neighbor risk if one tenant floods ingestion. A common architecture provisions dedicated clusters for enterprise accounts and shared routed indices with strict rate quotas for SMB tenants.

Elasticsearch vs Dedicated Vector Databases for Hybrid Search

Elasticsearch excels at BM25 lexical keyword retrieval, while platforms like Vector Database & Semantic Search provide dense semantic retrieval. Modern Elasticsearch also supports vector search, but this architecture keeps dense ANN in a dedicated vector tier so ANN memory and tuning remain isolated from the 50 TB lexical workload. Hybrid retrieval uses Reciprocal Rank Fusion to combine the sparse BM25 and dense result sets in the application or query coordinator.

Combined vs Dedicated Node Roles

Combined node roles simplify small clusters of three nodes. At 50 TB and 20K QPS, dedicated master nodes reduce election instability when data node garbage collection pauses occur. Dedicated coordinating nodes isolate merge heap allocations from indexing work, and dedicated ingest nodes isolate compute heavy parsing pipelines. While dedicated roles require more instances, they provide more predictable failure domains across workloads.

💬Review

Help Us Improve

How helpful was this walkthrough?

Click a star to rate. We actively use this feedback to refine and update our system design content.

Placeholder
Optional but highly appreciated!

Discussion

Share your thoughts, ask questions, or help others.

Loading comments...