← Kursa Dön
📄 Text · 35 min

Troubleshooting — Yaygın Sorunlar ve Çözümler

Giriş — Yaygın Sorunlar ve Çözümleri

Bir arabayı düşün. Motor uyarı lambası yandığında panik yapmaz, kontrol paneline bakar, hata kodunu okur ve sorunu teşhis edersin. Elasticsearch da aynı: sorun olduğunda kırmızı ışıklar yanar (cluster RED), uyarılar gelir (YELLOW), loglar dolar. Önemli olan bu sinyalleri okumayı ve doğru müdahaleyi bilmek.

Production Elasticsearch cluster'ı yönetiyorsan, er ya da geç sorunlarla karşılaşacaksın. Bu ders, en yaygın sorunları, teşhis yöntemlerini ve çözümlerini sistematik olarak ele alıyor. Her sorun için "ne oluyor → neden oluyor → nasıl çözülür" üçlüsünü takip edeceğiz.


1. Cluster Health — RED ve YELLOW

Cluster Health Nedir?

GET _cluster/health

// Yanıt:
{
  "cluster_name": "production",
  "status": "yellow",
  "number_of_nodes": 5,
  "number_of_data_nodes": 3,
  "active_primary_shards": 150,
  "active_shards": 250,
  "relocating_shards": 0,
  "initializing_shards": 0,
  "unassigned_shards": 50,
  "delayed_unassigned_shards": 0,
  "number_of_pending_tasks": 0,
  "active_shards_percent_as_number": 83.33
}
DurumAnlamAciliyet
🟢 GREENTüm primary ve replica shard'lar atanmışSorun yok
🟡 YELLOWTüm primary shard'lar ok, bazı replica'lar atanmamışOrta — veri kaybı riski
🔴 REDBazı primary shard'lar atanmamışACİL — veri kaybı, eksik arama sonuçları

YELLOW Cluster — En Yaygın Senaryo

Belirti: Cluster yellow, unassigned_shards > 0

En sık neden: Tek node'lu cluster'da replica var.

// Sorun teşhisi
GET _cat/shards?v&h=index,shard,prirep,state,unassigned.reason&s=state

// Çıktı:
index          shard prirep state       unassigned.reason
logs-2024.01   0     p      STARTED
logs-2024.01   0     r      UNASSIGNED  INDEX_CREATED

Çözüm 1: Node ekle (doğru çözüm)

Çözüm 2: Replica'yı 0 yap (geçici, tek node'luk ortam):

PUT logs-2024.01/_settings
{
  "index": {
    "number_of_replicas": 0
  }
}

// Tüm index'ler için:
PUT _all/_settings
{
  "index": {
    "number_of_replicas": 0
  }
}

RED Cluster — Acil Müdahale

Belirti: Cluster red, bazı aramalar eksik sonuç dönüyor veya hata veriyor.

Teşhis adımları:

// 1. Hangi index'ler RED?
GET _cat/indices?v&health=red

// 2. Hangi shard'lar unassigned?
GET _cat/shards?v&h=index,shard,prirep,state,unassigned.reason&s=state:desc

// 3. Neden atanmadı?
GET _cluster/allocation/explain
{
  "index": "logs-2024.01.15",
  "shard": 0,
  "primary": true
}

_cluster/allocation/explain çok değerli bir API — tam olarak neden bir shard'ın atanamadığını söyler:

// Örnek yanıt:
{
  "index": "logs-2024.01.15",
  "shard": 0,
  "primary": true,
  "current_state": "unassigned",
  "unassigned_info": {
    "reason": "NODE_LEFT",
    "at": "2024-01-15T10:30:00.000Z",
    "last_allocation_status": "no_valid_shard_copy"
  },
  "can_allocate": "no",
  "allocate_explanation": "cannot allocate because all found copies of the shard are stale or corrupt",
  "node_allocation_decisions": [
    {
      "node_name": "data-node-2",
      "deciders": [
        {
          "decider": "disk_threshold",
          "decision": "NO",
          "explanation": "the node is above the high watermark [90%]"
        }
      ]
    }
  ]
}

2. Unassigned Shards — Detaylı Çözümler

Unassigned shard'ların birçok nedeni olabilir. Her neden için farklı çözüm:

Neden 1: INDEX_CREATED — Yeni Index, Atanacak Node Yok

// Çözüm: Node ekle veya replica azalt
PUT my_index/_settings
{
  "index.number_of_replicas": 0
}

Neden 2: NODE_LEFT — Node Cluster'dan Ayrıldı

// Önce delayed allocation bekle (varsayılan 1 dakika)
// Node geri gelirse shard'lar otomatik atanır

// Bekleme süresini kontrol et:
GET my_index/_settings?filter_path=**.unassigned.node_left.delayed_timeout

// Acil müdahale: Reroute ile boş shard'ı yeniden ata
POST _cluster/reroute
{
  "commands": [
    {
      "allocate_stale_primary": {
        "index": "logs-2024.01.15",
        "shard": 0,
        "node": "data-node-2",
        "accept_data_loss": true
      }
    }
  ]
}

⚠️ Dikkat: allocate_stale_primary ile accept_data_loss: true kullanmak, eski (stale) bir shard kopyasını primary yapmanız demektir. Bu, en son yazılan bazı verilerin kaybolmasına neden olabilir. Başka çare kalmadığında son çare olarak kullanın.

Neden 3: ALLOCATION_FAILED — Atama Denemesi Başarısız

// Retry sınırına ulaşmış olabilir
// Retry sayısını sıfırla:
POST _cluster/reroute?retry_failed=true

Neden 4: Disk Watermark Aşıldı

// Disk kullanımını kontrol et:
GET _cat/allocation?v&h=node,disk.percent,disk.used,disk.avail,disk.total

// Çıktı:
node         disk.percent disk.used disk.avail disk.total
data-node-1  87           174gb     26gb       200gb
data-node-2  92           184gb     16gb       200gb
data-node-3  78           156gb     44gb       200gb

Disk watermark'ları (bkz. Bölüm 3 bu dersin ilerisi).

Neden 5: Shard Allocation Filter

// Index'te allocation filter var mı kontrol et:
GET my_index/_settings?filter_path=**.routing

// Çözüm: Yanlış filter'ı kaldır
PUT my_index/_settings
{
  "index.routing.allocation.require._name": null,
  "index.routing.allocation.include._name": null,
  "index.routing.allocation.exclude._name": null
}

Toplu Unassigned Shard Çözümü — Script

#!/bin/bash
# fix_unassigned.sh — Unassigned shard'ları teşhis et

echo "=== Unassigned Shard Raporu ==="

# Toplam sayı
TOTAL=$(curl -s 'localhost:9200/_cluster/health' | jq '.unassigned_shards')
echo "Toplam unassigned shard: $TOTAL"

# Neden dağılımı
echo -e "\n--- Sebep Dağılımı ---"
curl -s 'localhost:9200/_cat/shards?h=index,shard,prirep,state,unassigned.reason' | \
  grep UNASSIGNED | awk '{print $5}' | sort | uniq -c | sort -rn

# İlk 5 index
echo -e "\n--- En çok unassigned shard olan index'ler ---"
curl -s 'localhost:9200/_cat/shards?h=index,state' | \
  grep UNASSIGNED | awk '{print $1}' | sort | uniq -c | sort -rn | head -5

3. Disk Watermark Sorunları

Watermark Seviyeleri

Elasticsearch, disk dolduğunda otomatik önlem alır:

SeviyeVarsayılanDavranış
Low watermark%85Yeni shard'lar bu node'a atanmaz
High watermark%90Shard'lar bu node'dan başka node'lara taşınır
Flood stage%95Index'ler read-only yapılır!

Flood Stage — En Tehlikeli Durum

Disk %95'i aştığında Elasticsearch index'leri read_only_allow_delete moduna alır. Yazma işlemleri durur!

// Belirti: Yazma hatası
{
  "error": {
    "type": "cluster_block_exception",
    "reason": "index [logs-2024.01.15] blocked by: [FORBIDDEN/12/index read-only / allow delete (api)]"
  }
}

Çözüm:

// 1. Disk alanı aç (eski index'leri sil, log temizle)
DELETE logs-2024.01.01

// 2. Read-only bloğu kaldır
PUT _all/_settings
{
  "index.blocks.read_only_allow_delete": null
}

// Veya belirli index için:
PUT logs-2024.01.15/_settings
{
  "index.blocks.read_only_allow_delete": null
}

Watermark Ayarlarını Özelleştirme

PUT _cluster/settings
{
  "persistent": {
    "cluster.routing.allocation.disk.watermark.low": "85%",
    "cluster.routing.allocation.disk.watermark.high": "90%",
    "cluster.routing.allocation.disk.watermark.flood_stage": "95%",
    "cluster.info.update.interval": "30s"
  }
}

// Veya byte cinsinden:
PUT _cluster/settings
{
  "persistent": {
    "cluster.routing.allocation.disk.watermark.low": "50gb",
    "cluster.routing.allocation.disk.watermark.high": "20gb",
    "cluster.routing.allocation.disk.watermark.flood_stage": "10gb"
  }
}

💡 İpucu: Yüzde yerine mutlak değer (GB) kullanmak genellikle daha öngörülebilir. 1TB diskle %85 = 150GB boş, ama 200GB diskle %85 = 30GB boş — aynı yüzde, çok farklı sonuçlar.


4. Circuit Breaker — Bellek Koruma Mekanizması

Circuit Breaker Nedir?

Elasticsearch, bir işlem çok fazla bellek tükettiğinde OutOfMemoryError yerine o işlemi reddeder. Bunu elektrik sigortası gibi düşün: aşırı akım geldiğinde sigorta atar, ev yangınını önler.

// Belirti:
{
  "error": {
    "type": "circuit_breaking_exception",
    "reason": "[parent] Data too large, data for [<http_request>] would be [31.2gb/32gb], which is larger than the limit of [30.4gb/32gb]"
  }
}

Circuit Breaker Tipleri

BreakerVarsayılan LimitNe Korur?
ParentJVM heap'in %95'iTüm breaker'ların toplamı
Field dataJVM heap'in %40'ıField data cache (aggregation, sort)
RequestJVM heap'in %60'ıTek bir request'in bellek kullanımı
In-flight requestsJVM heap'in %100'üGelen HTTP request'lerin toplam boyutu
AccountingJVM heap'in %100'üLucene segment metadata

Parent Circuit Breaker

En sık karşılaşılan breaker. Tüm bellek kullanımının toplamını kontrol eder:

// Mevcut breaker durumlarını gör
GET _nodes/stats/breaker

// Yanıt:
{
  "nodes": {
    "node-1": {
      "breakers": {
        "parent": {
          "limit_size_in_bytes": 31138512896,
          "estimated_size_in_bytes": 28024661606,
          "overhead": 1.0,
          "tripped": 5
        },
        "fielddata": {
          "limit_size_in_bytes": 12455405158,
          "estimated_size_in_bytes": 8589934592,
          "overhead": 1.03,
          "tripped": 2
        },
        "request": {
          "limit_size_in_bytes": 18683107737,
          "estimated_size_in_bytes": 0,
          "overhead": 1.0,
          "tripped": 0
        }
      }
    }
  }
}

tripped sayısı sıfırdan büyükse, o breaker çalışmış demektir.

Field Data Circuit Breaker

Text field üzerinde aggregation veya sort yapmak field data'yı belleğe yükler:

// Kötü sorgu — text field üzerinde sort
GET logs-*/_search
{
  "sort": [{ "message": "asc" }]
}

// Bu, message field'ının tüm unique değerlerini belleğe yükler!
// Çözüm: Keyword alt-field kullan
GET logs-*/_search
{
  "sort": [{ "message.keyword": "asc" }]
}
// Field data cache'i temizle (acil durum)
POST _cache/clear?fielddata=true

// Belirli index için:
POST logs-*/_cache/clear?fielddata=true

Circuit Breaker Ayarlarını Özelleştirme

PUT _cluster/settings
{
  "persistent": {
    "indices.breaker.total.limit": "95%",
    "indices.breaker.fielddata.limit": "40%",
    "indices.breaker.request.limit": "60%"
  }
}

⚠️ Dikkat: Breaker limitlerini yükseltmek sorunu çözmez, sadece maskeler. Asıl çözüm: sorguyu optimize et, heap'i artır veya node ekle.


5. GC Pressure — Garbage Collection Sorunları

GC Pressure Nedir?

Java'nın çöp toplayıcısı (GC) kullanılmayan bellek alanlarını geri kazanır. Ama heap çok doluysa, GC sürekli çalışır ve Elasticsearch'ü durdurur (stop-the-world pause).

Bunu şöyle düşün: odanı toparlamaya çalışıyorsun ama sürekli yeni eşya geliyor. Bir noktada her şeyi bırakıp sadece toparlama yapman gerekiyor — bu sırada hiçbir iş yapamıyorsun.

Belirtiler

  • Yavaş response'lar (normalde ms olan sorgular saniyelerce sürüyor)

  • Node'un cluster'dan düşmesi (master timeout)

  • Log'larda uzun GC pause'ları

# Elasticsearch log'larında GC uyarıları:
[gc][old][123456][4] duration [15.2s], collections [1]/[15.3s], ...

Teşhis

// JVM bellek kullanımı
GET _nodes/stats/jvm

// Yanıt:
{
  "nodes": {
    "node-1": {
      "jvm": {
        "mem": {
          "heap_used_in_bytes": 28991029248,
          "heap_used_percent": 90,
          "heap_max_in_bytes": 32212254720
        },
        "gc": {
          "collectors": {
            "old": {
              "collection_count": 150,
              "collection_time_in_millis": 45000
            },
            "young": {
              "collection_count": 50000,
              "collection_time_in_millis": 120000
            }
          }
        }
      }
    }
  }
}

Tehlike işaretleri:

  • heap_used_percent sürekli > 75%

  • Old GC collection_time sürekli artıyor

  • Old GC collection_count hızla artıyor

Çözümler

1. Heap boyutunu kontrol et:

# jvm.options — Heap boyutu
-Xms16g
-Xmx16g

# Kural: Fiziksel RAM'in yarısı, max 31GB
# 64 GB RAM → -Xmx31g (compressed oops sınırı)
# 32 GB RAM → -Xmx16g
# 16 GB RAM → -Xmx8g

⚠️ Dikkat: Heap'i 32GB'ın üstüne çıkarmayın! Java'nın compressed oops optimizasyonu 32GB sınırında devre dışı kalır ve bellek kullanımı paradoks olarak artar.

2. Bellek tüketen sorguları bul:

// Hot threads — hangi thread'ler CPU/memory yiyor?
GET _nodes/hot_threads

// Hangi index'ler çok bellek kullanıyor?
GET _cat/segments?v&h=index,docs.count,size,memory&s=memory:desc

3. Field data temizle:

POST _cache/clear?fielddata=true

4. Gereksiz index'leri kapat veya sil:

// Eski index'leri kapat (bellek serbest kalır)
POST logs-2023.*/_close

// Veya sil
DELETE logs-2023.01.*

6. Slow Query — Yavaş Sorgular

Slow Log Etkinleştirme

// Index seviyesinde slow log
PUT logs-*/_settings
{
  "index.search.slowlog.threshold.query.warn": "10s",
  "index.search.slowlog.threshold.query.info": "5s",
  "index.search.slowlog.threshold.query.debug": "2s",
  "index.search.slowlog.threshold.query.trace": "500ms",
  "index.search.slowlog.threshold.fetch.warn": "1s",
  "index.search.slowlog.threshold.fetch.info": "800ms",
  "index.search.slowlog.level": "info",
  "index.indexing.slowlog.threshold.index.warn": "10s",
  "index.indexing.slowlog.threshold.index.info": "5s"
}

Log çıktısı (/var/log/elasticsearch/<cluster>_index_search_slowlog.json):

{
  "type": "index_search_slowlog",
  "timestamp": "2024-01-15T14:30:00.000Z",
  "level": "WARN",
  "message": "[logs-2024.01.15][0] took[12.5s], took_millis[12500], total_hits[1500000], source[{\"query\":{\"wildcard\":{\"message\":{\"value\":\"*error*\"}}}}]"
}

Query Profiling

// Sorgunun nerede zaman harcadığını gör
GET logs-2024.01.15/_search
{
  "profile": true,
  "query": {
    "bool": {
      "must": [
        { "match": { "message": "error" } },
        { "range": { "@timestamp": { "gte": "now-1h" } } }
      ]
    }
  }
}

Profile çıktısı hangi query'nin, hangi shard'da, kaç ms sürdüğünü gösterir.

Yaygın Yavaş Sorgu Nedenleri ve Çözümleri

SorunNedenÇözüm
wildcard("*error*")Leading wildcard tüm terimleri tararmatch veya ngram analyzer kullan
script_score her doc'taHer document'ta script çalışırfunction_score ile sadece match'lerde çalıştır
Büyük from/sizeDeep paginationsearch_after veya scroll kullan
Çok fazla shardHer shard'a istek giderShard sayısını azalt, routing kullan
text field'da sortField data belleğe yüklenirkeyword alt-field kullan
Regex queryPahalı — tüm terimleri kontrol ederDaha spesifik query kullan
// KÖTÜ — leading wildcard
GET logs-*/_search
{
  "query": {
    "wildcard": { "url": { "value": "*login*" } }
  }
}

// İYİ — match query
GET logs-*/_search
{
  "query": {
    "match": { "url": "login" }
  }
}

7. Rejected Thread Pool — İş Parçacığı Reddedilmesi

Thread Pool Nedir?

Elasticsearch farklı iş tipleri için ayrı thread pool'lar kullanır:

GET _cat/thread_pool?v&h=node_name,name,active,rejected,queue,completed&s=rejected:desc

// Çıktı:
node_name   name     active rejected queue completed
data-node-1 search   10     1523     0     5000000
data-node-1 write    5      892      0     3000000
data-node-2 search   8      45       0     4500000

Thread Pool Tipleri

PoolKullanımSizeQueue
searchArama sorgularıCPU çekirdek * 3/2 + 11000
writeIndex/update/delete/bulkCPU çekirdek10000
getGet APICPU çekirdek1000
analyzeAnalyze API116
managementCluster yönetimi5-

Rejected — Ne Anlama Gelir?

Thread pool dolu ve queue da dolu — gelen istek reddedilir. Client tarafında 429 Too Many Requests veya EsRejectedExecutionException hatası alırsınız.

Çözümler

1. Search rejected yüksekse:

// Sorguları optimize et (slow log'a bak)
// Shard sayısını azalt (daha az parallel iş)
// Node ekle (load distribution)
// Coordinating-only node ekle (arama load'u dağıt)

2. Write rejected yüksekse:

// Bulk size'ı küçült (5-15 MB arası optimal)
// Yazma paralelliğini azalt
// Refresh interval'ı artır (yazma sırasında segment oluşturma azalır)
PUT my_index/_settings
{
  "index.refresh_interval": "30s"
}

// Replica'ları geçici olarak 0'a çek (yazma hızını artırır)
PUT my_index/_settings
{
  "index.number_of_replicas": 0
}

3. Queue boyutunu artır (geçici çözüm):

# elasticsearch.yml
thread_pool.search.queue_size: 2000
thread_pool.write.queue_size: 20000

⚠️ Dikkat: Queue boyutunu artırmak, reddedilme yerine bekletme sağlar ama bellek tüketir. Asıl çözüm: load'u azalt veya kapasite artır.


8. Mapping Explosion

Sorun

Dynamic mapping açıkken, her yeni field otomatik oluşturulur. Eğer verilerinizde binlerce farklı field geliyorsa (örneğin key-value logları), mapping devasa büyür:

// Belirti: Cluster yavaşlıyor, mapping güncelleme süresi artar
GET my_index/_mapping
// ... 50.000 field döner 😱

Neden Tehlikeli?

  • Her field cluster state'te saklanır ve tüm node'lara replike edilir

  • Cluster state büyüdükçe master node yavaşlar

  • Mapping güncelleme işlemi cluster-wide lock alır

Çözüm 1: Field Sayısı Limiti

PUT my_index/_settings
{
  "index.mapping.total_fields.limit": 2000
}
// Varsayılan: 1000. Çok yüksek yapma!

Çözüm 2: Dynamic Mapping Kontrolü

// Dynamic mapping'i kapat
PUT my_index/_mapping
{
  "dynamic": "strict"
}

// Veya bilinmeyen field'ları yoksay (index'leme)
PUT my_index/_mapping
{
  "dynamic": "false"
}

Çözüm 3: Flattened Field Type

Key-value veri için ideal — tüm alt field'ları tek field olarak saklar:

PUT my_index
{
  "mappings": {
    "properties": {
      "labels": {
        "type": "flattened"
      }
    }
  }
}

// Artık labels altında ne gelirse gelsin tek field:
POST my_index/_doc
{
  "labels": {
    "env": "production",
    "app": "api-gateway",
    "version": "2.1.0",
    "region": "eu-west-1"
  }
}

9. too_many_clauses — Bool Query Patlaması

Sorun

// Hata:
{
  "error": {
    "type": "too_many_clauses",
    "reason": "maxClauseCount is set to 4096"
  }
}

Bu genellikle wildcard/prefix query'lerin çok fazla terime genişlemesinden kaynaklanır:

// KÖTÜ — Binlerce terime genişler
GET logs-*/_search
{
  "query": {
    "bool": {
      "should": [
        { "prefix": { "url.keyword": "/api/v" } }
        // Bu prefix binlerce farklı URL'e match edebilir
      ]
    }
  }
}

Çözümler

1. Limiti artır (geçici):

PUT _cluster/settings
{
  "persistent": {
    "indices.query.bool.max_clause_count": 8192
  }
}

2. Sorguyu optimize et (kalıcı):

// prefix yerine match_phrase_prefix kullan
GET logs-*/_search
{
  "query": {
    "match_phrase_prefix": {
      "url": "/api/v"
    }
  }
}

// Veya terms query ile spesifik değerler ver
GET logs-*/_search
{
  "query": {
    "terms": {
      "status_code": [500, 502, 503, 504]
    }
  }
}

10. Shard Allocation Sorunları

Shard Allocation Neden Bekliyor?

// Detaylı açıklama al
GET _cluster/allocation/explain

// Belirli bir shard için:
GET _cluster/allocation/explain
{
  "index": "my_index",
  "shard": 0,
  "primary": true
}

Yaygın Allocation Sorunları

1. Awareness attribute uyuşmazlığı:

// Rack awareness aktifse ve yeterli rack yoksa
// replica, primary ile aynı rack'e atanamaz

// Geçici çözüm:
PUT _cluster/settings
{
  "transient": {
    "cluster.routing.allocation.awareness.attributes": ""
  }
}

2. Allocation filter engeli:

// Index seviyesinde filter var mı?
GET my_index/_settings?filter_path=**.routing.allocation

// Cluster seviyesinde?
GET _cluster/settings?filter_path=**.allocation

3. Maximum shards per node:

// Varsayılan: node başına 1000 shard
GET _cluster/settings?filter_path=**.total_shards_per_node

// Artır (dikkatli!):
PUT _cluster/settings
{
  "persistent": {
    "cluster.max_shards_per_node": 1500
  }
}

// Shard sayısını kontrol et:
GET _cat/allocation?v

4. Shard'ı manuel taşıma:

POST _cluster/reroute
{
  "commands": [
    {
      "move": {
        "index": "logs-2024.01.15",
        "shard": 2,
        "from_node": "data-node-1",
        "to_node": "data-node-3"
      }
    }
  ]
}

11. Teşhis Araç Kutusu

Hızlı Sağlık Kontrolü

// 1. Cluster genel durum
GET _cluster/health?pretty

// 2. Node'ların durumu
GET _cat/nodes?v&h=name,heap.percent,ram.percent,cpu,load_1m,disk.used_percent,node.role

// Çıktı:
name         heap.percent ram.percent cpu load_1m disk.used_percent node.role
data-node-1  72           85          45  3.20    78                d
data-node-2  68           80          38  2.80    82                d
master-1     35           60          10  0.50    25                m

// 3. Index sağlığı
GET _cat/indices?v&h=health,index,pri,rep,docs.count,store.size&s=health:desc

// 4. Shard dağılımı
GET _cat/shards?v&h=index,shard,prirep,state,node,docs,store&s=state

// 5. Pending tasks (master node queue)
GET _cat/pending_tasks?v

Detaylı Teşhis

// Hot threads — CPU'yu ne yiyor?
GET _nodes/hot_threads?threads=5

// Task management — uzun süren işlemler
GET _tasks?actions=*search*&detailed=true

// Belirli bir uzun task'i iptal et
POST _tasks/node-1:12345/_cancel

// Node istatistikleri
GET _nodes/stats/indices,jvm,os,process,thread_pool

// Cluster state boyutu (mapping explosion tespiti)
GET _cluster/state?filter_path=metadata.indices.*.mappings

Java ile Health Check

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch.cluster.HealthResponse;
import co.elastic.clients.elasticsearch.cat.*;

public class ClusterHealthChecker {

    private final ElasticsearchClient client;

    public ClusterHealthChecker(ElasticsearchClient client) {
        this.client = client;
    }

    public void fullHealthCheck() throws Exception {
        // 1. Cluster health
        HealthResponse health = client.cluster().health();
        System.out.printf("Cluster: %s | Status: %s | Nodes: %d%n",
            health.clusterName(), health.status(), health.numberOfNodes());
        System.out.printf("Shards - Active: %d | Unassigned: %d%n",
            health.activeShards(), health.unassignedShards());

        // 2. RED/YELLOW ise uyar
        if (health.status() != co.elastic.clients.elasticsearch
                ._types.HealthStatus.Green) {
            System.out.println("⚠️ UYARI: Cluster " + health.status());

            // Unassigned shard detayı
            if (health.unassignedShards() > 0) {
                System.out.println("Unassigned shard var — " +
                    "_cluster/allocation/explain kontrol et");
            }
        }

        // 3. Node durumları
        NodesResponse nodes = client.cat().nodes(n -> n
            .headers("name,heap.percent,disk.used_percent,cpu")
        );
        nodes.valueBody().forEach(node -> {
            System.out.printf("Node: %s | Heap: %s%% | Disk: %s%% | CPU: %s%%%n",
                node.name(), node.heapPercent(),
                node.diskUsedPercent(), node.cpu()
            );
        });
    }
}

12. Troubleshooting Flowchart

Sorunla karşılaştığında bu sırayı takip et:

Sorun var!
  │
  ├── Cluster RED/YELLOW mu?
  │     ├── GET _cluster/health
  │     ├── GET _cat/shards?v (unassigned shard'ları bul)
  │     └── GET _cluster/allocation/explain (neden atanmadı?)
  │
  ├── Yavaş sorgular mı?
  │     ├── Slow log etkinleştir
  │     ├── profile: true ile sorgu analizi
  │     └── _cat/thread_pool (search rejected?)
  │
  ├── Yazma hataları mı?
  │     ├── Disk watermark kontrol et
  │     ├── _cat/thread_pool (write rejected?)
  │     ├── Bulk size kontrol et (5-15 MB optimal)
  │     └── Circuit breaker kontrol et
  │
  ├── Node düşüyor mu?
  │     ├── JVM heap kontrol et (> 75% tehlike)
  │     ├── GC log'ları kontrol et
  │     ├── Disk dolu mu?
  │     └── Network sorunları? (master timeout)
  │
  └── Genel yavaşlık mı?
        ├── _nodes/hot_threads
        ├── _cat/nodes (CPU, heap, disk)
        ├── Mapping explosion? (_cluster/state boyutu)
        └── Çok fazla shard? (_cat/allocation)

13. Best Practices

✅ Yap

KonuÖneri
MonitoringPrometheus + Grafana veya Kibana Stack Monitoring ile sürekli izle
AlertingHeap > 75%, disk > 80%, RED cluster için alarm kur
Slow logProduction'da her zaman açık tut
Shard boyutu10-50 GB arası hedefle — çok küçük veya çok büyük yapma
Mapping kontrolüdynamic: strict veya false kullan
Düzenli bakımForce merge (read-only index'ler), cache temizleme

❌ Yapma

KonuNeden
Heap > 31 GBCompressed oops devre dışı kalır, bellek verimsizleşir
Text field sortField data belleğe yüklenir, circuit breaker tetiklenir
Leading wildcard*error* gibi sorgular çok pahalı
1000+ shard/nodeMaster node yavaşlar, cluster state şişer
Dynamic mapping kontrolsüzMapping explosion riski
Breaker limitlerini artırmakSorunu maskeler, çözmez

Özet

  1. Cluster RED/YELLOW durumunda ilk adım _cluster/allocation/explain — bu API tam olarak neden shard atanamadığını söyler. Unassigned shard'ların en yaygın nedenleri: disk dolu, node kaybı, allocation filter.

  2. Disk watermark üç seviyedir: low (%85), high (%90), flood stage (%95). Flood stage'de index'ler read-only olur — acil disk alanı açın ve read_only_allow_delete bloğunu kaldırın.

  3. Circuit breaker OOM'u önleyen koruma mekanizmasıdır — parent, fielddata, request ve in-flight tipleri vardır. Tetikleniyorsa sorguyu optimize edin veya heap'i artırın (max 31GB).

  4. GC pressure heap sürekli > %75 olduğunda ortaya çıkar — node yavaşlar, hatta cluster'dan düşer. Heap boyutunu RAM'in yarısı olarak ayarlayın, 32GB sınırını aşmayın.

  5. Slow log ve profile API yavaş sorguları teşhis eder — leading wildcard, deep pagination, text field sort en yaygın nedenlerdir.

  6. Sistematik yaklaşım her zaman kazanır: önce _cluster/health, sonra _cat/shards, ardından _cluster/allocation/explain. Sorunun kök nedenini bulun, semptomları değil nedeni çözün.