Skip to content
Back to skills

Elasticsearch And Opensearch Tuning

ASecurity

Production guidance and performance tuning strategies for Elasticsearch and OpenSearch clusters, covering indexing, query optimization, mapping design, memory management, and scaling.

  • 8 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 29, 2026
ai-agentspythongobashnodeapidatabaseperformance

Works with

  • cli
  • api

Security analysis

A100/100

Scanned September 29, 2026

npx -y skills add hamzabellouch/agent-skills --skill elasticsearch-and-opensearch-tuning --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Elasticsearch And Opensearch Tuning?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Elasticsearch And Opensearch Tuning
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/hamzabellouch-elasticsearch-and-opensearch-tuning/badge)](https://www.skillsdirectory.com/skills/hamzabellouch-elasticsearch-and-opensearch-tuning)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: elasticsearch-and-opensearch-tuning
description: Production guidance and performance tuning strategies for Elasticsearch and OpenSearch clusters, covering indexing, query optimization, mapping design, memory management, and scaling.
---

# Elasticsearch & OpenSearch Tuning Guide

This skill provides comprehensive instructions, architectural pattern guidelines, and production-ready code examples for optimizing Elasticsearch and OpenSearch for high-throughput indexing, ultra-low latency search, and cost-effective cluster scaling.

---

## 1. Indexing & Mapping Strategies

### 1.1 Field Mapping Optimization
- **Disable `_source` selectively** only when raw document retrieval is unnecessary and storage budget is tight; otherwise keep `_source` enabled for reindexing capabilities.
- **Set `index: false`** on fields used only for storage/retrieval (e.g., metadata blobs, raw HTML strings) to prevent inverted index overhead.
- **Explicit Mappings over Dynamic Mappings**: Never rely on dynamic mapping in production. Dynamic mapping introduces field explosion risks (`keyword` + `text` dual fields by default).
- **Use `doc_values: false`** for high-cardinality text or keyword fields that will **never** be used for aggregations, sorting, or scripting.
- **Optimize Text Analysis**:
  - Disable norm storage (`norms: false`) on text fields where relevance scoring is not required (e.g., exact matches, filter-only text).
  - Use `index_options: docs` for fields where positional or phrase queries are not needed (saves term frequency and position offset storage).

#### Optimized Mapping Definition (DSL)
```json
{
  "settings": {
    "index": {
      "number_of_shards": 3,
      "number_of_replicas": 1,
      "refresh_interval": "30s",
      "translog": {
        "durability": "async",
        "sync_interval": "5s",
        "flush_threshold_size": "1gb"
      },
      "codec": "best_compression"
    }
  },
  "mappings": {
    "dynamic": "strict",
    "properties": {
      "id": {
        "type": "keyword",
        "doc_values": true
      },
      "title": {
        "type": "text",
        "analyzer": "standard",
        "fields": {
          "raw": {
            "type": "keyword",
            "ignore_above": 256
          }
        }
      },
      "body": {
        "type": "text",
        "analyzer": "standard",
        "index_options": "positions",
        "norms": true
      },
      "category_id": {
        "type": "keyword",
        "doc_values": true,
        "norms": false
      },
      "status": {
        "type": "keyword",
        "doc_values": true
      },
      "created_at": {
        "type": "date",
        "format": "strict_date_optional_time||epoch_millis"
      },
      "payload_metadata": {
        "type": "keyword",
        "index": false,
        "doc_values": false
      }
    }
  }
}
```

---

### 1.2 Bulk Ingestion & Shard Sizing
- **Bulk Batch Size**: Ideal bulk request payload is typically between **5MB to 15MB** per request (or 2,000–5,000 documents depending on doc size). Tune based on CPU and throughput benchmarks rather than fixed doc counts.
- **Shard Sizing Standard**:
  - **Search-heavy indices**: Shard size should be **10GB to 30GB**.
  - **Logging / Time-series indices**: Shard size should be **30GB to 50GB**.
  - Target total shards per GB of JVM heap: Keep under **20 shards per GB of heap**.
- **Indexing Throughput Tuning**:
  - Increase `refresh_interval` from `1s` to `30s` or `60s` during heavy bulk ingestion.
  - Set `number_of_replicas: 0` during initial bulk loading, then restore to `1` or `2` after load completes.
  - Configure `translog.durability: async` for non-critical logging workloads to eliminate synchronous disk commits on every indexing request.

---

## 2. Query Tuning & Performance Optimization

### 2.1 Query Execution: Filter vs. Query Context
- **Filter Context (`filter`, `must_not`)**: Does not compute relevance scores (`_score`). Results are cached in the **Node Query Cache** (LRU bitsets). Always place non-scoring exact-match conditions inside `filter`.
- **Query Context (`must`, `should`)**: Computes relevance scores using BM25. Use exclusively for text relevance matching.

#### Optimized Multi-Clause Search Request
```json
{
  "query": {
    "bool": {
      "filter": [
        { "term": { "status": "active" } },
        { "range": { "created_at": { "gte": "now-7d/d" } } }
      ],
      "must": [
        {
          "match": {
            "title": {
              "query": "distributed graph database",
              "operator": "and",
              "fuzziness": "AUTO:4,8"
            }
          }
        }
      ],
      "should": [
        {
          "match_phrase": {
            "body": {
              "query": "distributed graph database",
              "slop": 2,
              "boost": 2.5
            }
          }
        }
      ]
    }
  },
  "timeout": "500ms",
  "terminate_after": 10000,
  "_source": ["id", "title", "created_at"]
}
```

---

### 2.2 Deep Pagination Mitigation
- **Avoid `from` + `size`** for deep page iteration (`from + size > 10,000` causes Memory Overflow and high JVM CPU utilization).
- **Use `search_after` with Point In Time (PIT)** for deterministic stateless pagination over large result sets:

#### Python Production Example: `search_after` with PIT
```python
import logging
from typing import List, Dict, Any, Generator
from opensearchpy import OpenSearch, NotFoundError

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

class DeepPaginationClient:
    def __init__(self, client: OpenSearch, index_name: str):
        self.client = client
        self.index_name = index_name

    def iterate_all_documents(
        self, query: Dict[str, Any], page_size: int = 1000
    ) -> Generator[Dict[str, Any], None, None]:
        """
        Executes deep pagination efficiently using Point In Time (PIT) / search_after.
        """
        # Create Point In Time (PIT) context (OpenSearch / ES 7.10+)
        try:
            pit_resp = self.client.create_point_in_time(
                index=self.index_name, keep_alive="2m"
            )
            pit_id = pit_resp["pit_id"]
        except Exception as e:
            logger.error("Failed to create Point In Time context: %s", str(e))
            raise

        search_after_mark = None
        total_fetched = 0

        try:
            while True:
                body: Dict[str, Any] = {
                    "size": page_size,
                    "query": query,
                    "sort": [
                        {"created_at": {"order": "desc"}},
                        {"_shard_doc": {"order": "asc"}} # Tie-breaker
                    ],
                    "pit": {
                        "id": pit_id,
                        "keep_alive": "2m"
                    }
                }

                if search_after_mark:
                    body["search_after"] = search_after_mark

                response = self.client.search(body=body)
                hits = response["hits"]["hits"]

                if not hits:
                    break

                for hit in hits:
                    yield hit["_source"]
                    total_fetched += 1

                search_after_mark = hits[-1]["sort"]
                logger.debug("Fetched batch of %d docs. Total: %d", len(hits), total_fetched)

        finally:
            # Always release PIT resources
            try:
                self.client.delete_point_in_time(body={"pit_id": pit_id})
            except Exception as e:
                logger.warning("Failed to delete PIT context: %s", str(e))
```

---

## 3. Memory & Infrastructure Management

### 3.1 JVM Heap Allocation
- Set `-Xms` and `-Xmx` to the **exact same value** to prevent dynamic heap resizing runtime pauses.
- **Never exceed 32GB heap**: Keep JVM heap under the Compressed OOPs threshold (typically **30GB–31GB** depending on JDK version) to ensure 32-bit pointer efficiency.
- Allocate **50% of total system RAM to JVM Heap** and leave the remaining 50% for **OS Filesystem Page Cache** (crucial for Doc Values, Lucene segment caching, and bloom filters).

### 3.2 Circuit Breakers & Thread Pools
- **Parent Circuit Breaker**: Set `indices.breaker.total.use_real_memory: true` with limit `70%` to prevent OutOfMemoryError (OOM).
- **Fielddata Breaker**: Set `indices.breaker.fielddata.limit: 40%`. Avoid using `fielddata: true` on `text` fields; use `keyword` fields with `doc_values`.
- **Search Thread Pool Queue**: `thread_pool.search.queue_size: 1000`. Do not set arbitrarily high; larger queues cause task latency accumulation and high garbage collection pressure.

---

## 4. Production Anti-Patterns & Solutions

| Anti-Pattern | Operational Impact | Recommended Solution |
| :--- | :--- | :--- |
| **Dynamic Mapping Explosion** | Unbounded field creation leads to cluster state degradation and OOM. | Set `"dynamic": "strict"` in index mappings. |
| **Using `wildcard` with leading asterisk (`*foo`)** | Forces full inverted index scanning; severe CPU consumption. | Use `wildcard` field type or n-gram tokenizer (`edge_ngram`). |
| **Over-Sharding** | Thousands of small shards (<1GB) exhaust JVM heap on metadata. | Reindex/Shrink indices to maintain 10GB–50GB per shard. |
| **Deep `from` + `size` Pagination** | Linear RAM and CPU scaling ($O(N)$ memory cost per request). | Use `search_after` with PIT or `scroll` API (for batch export). |
| **Heavy `script_score` Without Caching** | High CPU overhead per doc evaluation during query phase. | Pre-compute values at ingestion or use `painless` optimized scripts. |

---

## 5. Diagnostic & Monitoring Commands

### Checking Cluster Health & Shard Distribution
```bash
# Cluster health summary
GET /_cluster/health?v

# Shard count and size per index
GET /_cat/shards?v&s=index,shard&h=index,shard,prirep,state,docs,store,node

# Circuit breaker states and memory usage
GET /_nodes/stats/breaker

# Fielddata and Query Cache memory usage
GET /_nodes/stats/indices/fielddata,query_cache,request_cache?human=true
```

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…