← Kursa Dön
📄 Text · 35 min

Bulk İşlemler ve Reindex Stratejileri

Giriş — Bulk API Best Practices, _reindex ve Aliases

Taşınma günü düşün. Eşyalarını tek tek taşırsan — bir tabak al, arabaya koy, git, bırak, geri gel, bir bardak al... — bir hafta sürer. Ama kolilere paketleyip kamyonla taşırsan bir günde biter. Hatta profesyonel bir nakliye firması tutarsan, eşyaları sınıflandırır, paketler, yeni evde doğru odalara yerleştirir.

Elasticsearch'te Bulk API "kamyon", Reindex API "nakliye firması", Aliases ise "adres yönlendirmesi" (eski adrese gelen mektuplar yeni adrese yönlendirilsin). Bu üçü birlikte çalışarak veri taşıma, dönüştürme ve sıfır kesinti geçişi mümkün kılar.


1. Bulk API — Toplu İşlemler

Neden Bulk?

Her tekil index/update/delete işlemi bir HTTP request'tir. Her request:

  • TCP bağlantısı kurar

  • JSON parse eder

  • Shard'a yönlendirir

  • Response döner

1 milyon document için 1 milyon HTTP request = saatler. Bulk API ile bunları tek request'e paketlersiniz = dakikalar.

Bulk API Formatı

Bulk API özel bir format kullanır: NDJSON (Newline Delimited JSON). Her satır ya bir action ya da bir document body'dir.

POST _bulk
{"index": {"_index": "products", "_id": "1"}}
{"name": "Laptop", "price": 15000, "category": "electronics"}
{"index": {"_index": "products", "_id": "2"}}
{"name": "Mouse", "price": 250, "category": "electronics"}
{"create": {"_index": "products", "_id": "3"}}
{"name": "Keyboard", "price": 500, "category": "electronics"}
{"update": {"_index": "products", "_id": "1"}}
{"doc": {"price": 14500}}
{"delete": {"_index": "products", "_id": "2"}}

⚠️ Dikkat: Her satır \n ile bitmeli — son satır dahil! JSON'lar tek satırda olmalı, pretty-print yapılmamalı.

Bulk Action Tipleri

ActionAçıklamaBody Gerekir mi?
indexEkle veya üzerine yaz
createSadece ekle (varsa hata)
updatePartial update
deleteSil

Bulk Response

{
  "took": 125,
  "errors": true,
  "items": [
    {
      "index": {
        "_index": "products",
        "_id": "1",
        "status": 201,
        "result": "created"
      }
    },
    {
      "index": {
        "_index": "products",
        "_id": "2",
        "status": 201,
        "result": "created"
      }
    },
    {
      "update": {
        "_index": "products",
        "_id": "999",
        "status": 404,
        "error": {
          "type": "document_missing_exception",
          "reason": "[999]: document missing"
        }
      }
    }
  ]
}

Kritik nokta: Bulk işlem atomik değildir! Bir satır başarısız olursa diğerleri etkilenmez. Her zaman errors flag'ini kontrol edin:

// Hata kontrolü
// Response'ta "errors": true ise, items dizisini tarayın
// Her item'ın status'una bakın — 2xx = başarılı, diğer = hata

2. Bulk API Best Practices

2.1. Optimal Batch Boyutu

"Ne kadar büyük batch o kadar iyi" doğru değil! Çok büyük batch'ler:

  • Network buffer'ı patlatır

  • Node'da bellek sorununa yol açar

  • Timeout'a düşer

Önerilen başlangıç noktası: 5-15 MB per batch veya 1000-5000 document per batch.

// İyi: 1000 document per batch
POST _bulk
{"index": {"_index": "products"}}
{"name": "Product 1", "price": 100}
{"index": {"_index": "products"}}
{"name": "Product 2", "price": 200}
// ... 998 satır daha

💡 İpucu: Optimal batch boyutunu bulmak için benchmark yapın. 1000, 3000, 5000, 10000 document ile test edin. Throughput artmayı bıraktığı veya error almaya başladığınız noktanın bir altı optimal boyuttur.

2.2. Refresh Interval Ayarı

Bulk yazma sırasında her 1 saniyede bir index refresh yapmak gereksiz:

// Bulk öncesi: Refresh kapat
PUT products/_settings
{
  "index.refresh_interval": "-1"
}

// Bulk işlemi yap...
POST _bulk
{"index": {"_index": "products"}}
{"name": "Product 1", "price": 100}
// ... binlerce satır

// Bulk sonrası: Refresh'i geri aç
PUT products/_settings
{
  "index.refresh_interval": "1s"
}

// Manuel refresh tetikle
POST products/_refresh

2.3. Replica Sayısını Azaltma

Bulk yazma sırasında her document hem primary hem replica shard'lara yazılır. Geçici olarak replica'yı kapatmak yazma hızını 2x artırabilir:

// Bulk öncesi
PUT products/_settings
{
  "index.number_of_replicas": 0,
  "index.refresh_interval": "-1"
}

// Bulk işlemi...

// Bulk sonrası
PUT products/_settings
{
  "index.number_of_replicas": 1,
  "index.refresh_interval": "1s"
}

⚠️ Dikkat: Replica 0 iken node düşerse veri kaybı olur! Sadece ilk yükleme (initial load) senaryolarında yapın. Production'da aktif veri alan index'lerde yapmayın.

2.4. Pipeline ile Bulk

Bulk işlemlerinde ingest pipeline kullanabilirsiniz:

// Önce pipeline oluştur
PUT _ingest/pipeline/product-pipeline
{
  "description": "Product enrichment",
  "processors": [
    {
      "set": {
        "field": "indexed_at",
        "value": "{{_ingest.timestamp}}"
      }
    },
    {
      "lowercase": {
        "field": "category"
      }
    }
  ]
}

// Pipeline ile bulk
POST _bulk?pipeline=product-pipeline
{"index": {"_index": "products"}}
{"name": "Laptop", "price": 15000, "category": "ELECTRONICS"}
{"index": {"_index": "products"}}
{"name": "Mouse", "price": 250, "category": "PERIPHERALS"}

2.5. Routing ile Bulk

POST _bulk?routing=tenant_abc
{"index": {"_index": "orders"}}
{"tenant": "tenant_abc", "product": "Laptop", "amount": 15000}
{"index": {"_index": "orders"}}
{"tenant": "tenant_abc", "product": "Mouse", "amount": 250}

// Veya her document'a ayrı routing
POST _bulk
{"index": {"_index": "orders", "_routing": "tenant_abc"}}
{"tenant": "tenant_abc", "product": "Laptop", "amount": 15000}
{"index": {"_index": "orders", "_routing": "tenant_xyz"}}
{"tenant": "tenant_xyz", "product": "Keyboard", "amount": 500}

3. Java ile Bulk İşlemler

Temel Bulk

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch.core.BulkRequest;
import co.elastic.clients.elasticsearch.core.BulkResponse;
import co.elastic.clients.elasticsearch.core.bulk.BulkResponseItem;

// Bulk request oluştur
BulkRequest.Builder builder = new BulkRequest.Builder();

for (Product product : products) {
    builder.operations(op -> op
        .index(idx -> idx
            .index("products")
            .id(product.getId())
            .document(product)
        )
    );
}

BulkResponse response = client.bulk(builder.build());

// Hata kontrolü
if (response.errors()) {
    for (BulkResponseItem item : response.items()) {
        if (item.error() != null) {
            System.err.println("Error: " + item.id() +
                " — " + item.error().reason());
        }
    }
}

Batch İşleme ile Büyük Veri Seti

import java.util.List;
import java.util.ArrayList;

public class BulkIndexer {
    private final ElasticsearchClient client;
    private static final int BATCH_SIZE = 3000;

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

    public void indexAll(List<Product> products) throws Exception {
        List<Product> batch = new ArrayList<>();
        int totalSuccess = 0;
        int totalFailed = 0;

        for (int i = 0; i < products.size(); i++) {
            batch.add(products.get(i));

            if (batch.size() >= BATCH_SIZE || i == products.size() - 1) {
                BulkResponse response = sendBatch(batch);
                if (response.errors()) {
                    for (BulkResponseItem item : response.items()) {
                        if (item.error() != null) {
                            totalFailed++;
                        } else {
                            totalSuccess++;
                        }
                    }
                } else {
                    totalSuccess += batch.size();
                }
                batch.clear();

                System.out.printf("Progress: %d/%d (success: %d, failed: %d)%n",
                    i + 1, products.size(), totalSuccess, totalFailed);
            }
        }
    }

    private BulkResponse sendBatch(List<Product> batch) throws Exception {
        BulkRequest.Builder builder = new BulkRequest.Builder();
        for (Product p : batch) {
            builder.operations(op -> op
                .index(idx -> idx
                    .index("products")
                    .id(p.getId())
                    .document(p)
                )
            );
        }
        return client.bulk(builder.build());
    }
}

Retry Mekanizması

public BulkResponse bulkWithRetry(BulkRequest request, int maxRetries) 
        throws Exception {

    BulkResponse response = client.bulk(request);

    if (response.errors() && maxRetries > 0) {
        // Başarısız olanları topla
        BulkRequest.Builder retryBuilder = new BulkRequest.Builder();
        List<BulkResponseItem> items = response.items();

        for (int i = 0; i < items.size(); i++) {
            BulkResponseItem item = items.get(i);
            if (item.error() != null && isRetryable(item.status())) {
                // Orijinal request'ten ilgili operation'ı tekrar ekle
                retryBuilder.operations(
                    request.operations().get(i)
                );
            }
        }

        // Kısa bekleme
        Thread.sleep(1000 * (4 - maxRetries));

        return bulkWithRetry(retryBuilder.build(), maxRetries - 1);
    }

    return response;
}

private boolean isRetryable(int status) {
    return status == 429 || status >= 500;
}

4. Reindex API — Veri Taşıma ve Dönüştürme

Neden Reindex?

  • Mapping değişikliği (field tipi değiştirme)

  • Shard sayısını değiştirme

  • Analyzer değiştirme

  • Veri dönüştürme (enrich, filter)

  • Index birleştirme veya bölme

Temel Reindex

// Basit reindex — kaynak → hedef
POST _reindex
{
  "source": {
    "index": "products-v1"
  },
  "dest": {
    "index": "products-v2"
  }
}

Reindex with Query (Filtreleyerek Taşıma)

// Sadece aktif ürünleri taşı
POST _reindex
{
  "source": {
    "index": "products-v1",
    "query": {
      "bool": {
        "filter": [
          { "term": { "status": "active" } },
          { "range": { "created_at": { "gte": "2023-01-01" } } }
        ]
      }
    }
  },
  "dest": {
    "index": "products-v2"
  }
}

Reindex with Script (Dönüştürerek Taşıma)

// Field adlarını değiştir, veri dönüştür
POST _reindex
{
  "source": {
    "index": "products-v1"
  },
  "dest": {
    "index": "products-v2"
  },
  "script": {
    "source": """
      // Field adı değişikliği
      ctx._source.product_name = ctx._source.remove('name');

      // Yeni field ekle
      ctx._source.migrated_at = '2024-01-15';

      // Fiyatı KDV dahil yap
      if (ctx._source.price != null) {
        ctx._source.price_with_tax = ctx._source.price * 1.20;
      }

      // Belirli document'ları atla
      if (ctx._source.status == 'deleted') {
        ctx.op = 'noop';
      }
    """
  }
}

Reindex with Pipeline

// Ingest pipeline ile dönüştürme
POST _reindex
{
  "source": {
    "index": "logs-old"
  },
  "dest": {
    "index": "logs-new",
    "pipeline": "log-enrichment-pipeline"
  }
}

Remote Reindex (Farklı Cluster'dan Taşıma)

POST _reindex
{
  "source": {
    "remote": {
      "host": "https://old-cluster:9200",
      "username": "elastic",
      "password": "changeme",
      "socket_timeout": "1m",
      "connect_timeout": "10s"
    },
    "index": "products",
    "query": {
      "match_all": {}
    }
  },
  "dest": {
    "index": "products"
  }
}

Remote reindex için elasticsearch.yml'de whitelist gerekir:

reindex.remote.whitelist: "old-cluster:9200"

Reindex Performans Ayarları

// Hızlı reindex için optimizasyon
POST _reindex?wait_for_completion=false&slices=auto
{
  "source": {
    "index": "logs-2024.01",
    "size": 5000
  },
  "dest": {
    "index": "logs-2024.01-v2"
  },
  "conflicts": "proceed"
}

Parametreler:

  • wait_for_completion=false: Async çalıştır, task ID döner

  • slices=auto: Shard sayısı kadar paralel slice

  • size: Batch boyutu (scroll size)

  • conflicts: proceed: Conflict'lerde durma, devam et

Async Reindex İzleme

# Task durumunu kontrol et
GET _tasks/node-1:12345

# Tüm reindex task'lerini listele
GET _tasks?detailed=true&actions=*reindex

# Task iptal et
POST _tasks/node-1:12345/_cancel

Reindex with Throttle

// Saniyede max 500 document (production load'u etkilemesin)
POST _reindex?requests_per_second=500
{
  "source": {
    "index": "products-v1"
  },
  "dest": {
    "index": "products-v2"
  }
}

// Throttle'ı runtime'da değiştir
POST _reindex/node-1:12345/_rethrottle?requests_per_second=1000

5. Index Aliases — Sıfır Kesinti Geçişi

Alias Nedir?

Alias, bir index'e verilen takma ad. Uygulamanız gerçek index adını bilmek zorunda kalmaz — alias kullanır. Index değiştiğinde alias'ı yeni index'e yönlendirirsiniz. Uygulama hiçbir şeyden habersiz çalışmaya devam eder.

Temel Alias İşlemleri

// Alias oluştur
POST _aliases
{
  "actions": [
    {
      "add": {
        "index": "products-v1",
        "alias": "products"
      }
    }
  ]
}

// Artık "products" alias'ı üzerinden arama yapılabilir
GET products/_search
{
  "query": { "match_all": {} }
}

Alias Swap — Atomik Geçiş

Bu, reindex sonrası en kritik adım. Eski index'ten alias'ı kaldır, yeni index'e ekle — tek atomik operasyonda:

POST _aliases
{
  "actions": [
    { "remove": { "index": "products-v1", "alias": "products" } },
    { "add": { "index": "products-v2", "alias": "products" } }
  ]
}

Bu operasyon atomik — iki action aynı anda uygulanır. Arada boşluk yok, sorgu kaybı yok.

Write Alias

Hem okuma hem yazma alias'ı ayrı tutabilirsiniz:

POST _aliases
{
  "actions": [
    {
      "add": {
        "index": "products-v2",
        "alias": "products-read"
      }
    },
    {
      "add": {
        "index": "products-v2",
        "alias": "products-write",
        "is_write_index": true
      }
    }
  ]
}

Filtered Alias

Alias'a filtre ekleyerek sanal view oluşturabilirsiniz:

POST _aliases
{
  "actions": [
    {
      "add": {
        "index": "products-v2",
        "alias": "electronics",
        "filter": {
          "term": { "category": "electronics" }
        }
      }
    },
    {
      "add": {
        "index": "products-v2",
        "alias": "clothing",
        "filter": {
          "term": { "category": "clothing" }
        }
      }
    }
  ]
}

// Sadece elektronik ürünleri arar
GET electronics/_search
{
  "query": { "match": { "name": "laptop" } }
}

Multi-Index Alias

Birden fazla index'i tek alias altında birleştirebilirsiniz:

POST _aliases
{
  "actions": [
    { "add": { "index": "logs-2024.01", "alias": "logs-recent" } },
    { "add": { "index": "logs-2024.02", "alias": "logs-recent" } },
    { "add": { "index": "logs-2024.03", "alias": "logs-recent" } }
  ]
}

// Son 3 ayın loglarını tek seferde ara
GET logs-recent/_search
{
  "query": {
    "range": {
      "@timestamp": { "gte": "now-7d" }
    }
  }
}

Alias Bilgilerini Görüntüleme

# Tüm alias'lar
GET _cat/aliases?v

# Belirli bir alias
GET _alias/products

# Belirli bir index'in alias'ları
GET products-v2/_alias

6. Bütünleşik Örnek: Zero-Downtime Migration

Senaryo: products-v1 index'inde mapping hatası var. description field'ı keyword olarak tanımlanmış, text olmalıydı. Analyzer da eklenmeli. Sıfır kesinti ile geçiş yapacağız.

Adım 1: Mevcut Durumu Kontrol Et

# Mevcut alias yapısı
GET _alias/products
# Sonuç: products → products-v1

# Mevcut mapping
GET products-v1/_mapping

Adım 2: Yeni Index'i Doğru Mapping ile Oluştur

PUT products-v2
{
  "settings": {
    "number_of_shards": 3,
    "number_of_replicas": 1,
    "analysis": {
      "analyzer": {
        "turkish_custom": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": ["lowercase", "turkish_stop", "turkish_stem"]
        }
      },
      "filter": {
        "turkish_stop": {
          "type": "stop",
          "stopwords": "_turkish_"
        },
        "turkish_stem": {
          "type": "stemmer",
          "language": "turkish"
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "name": {
        "type": "text",
        "analyzer": "turkish_custom",
        "fields": {
          "keyword": { "type": "keyword" }
        }
      },
      "description": {
        "type": "text",
        "analyzer": "turkish_custom"
      },
      "price": { "type": "float" },
      "category": { "type": "keyword" },
      "status": { "type": "keyword" },
      "created_at": { "type": "date" }
    }
  }
}

Adım 3: Reindex (Dönüştürerek Taşıma)

// Yeni index'in yazma performansını artır
PUT products-v2/_settings
{
  "index.refresh_interval": "-1",
  "index.number_of_replicas": 0
}

// Reindex başlat
POST _reindex?wait_for_completion=false
{
  "source": {
    "index": "products-v1",
    "size": 5000
  },
  "dest": {
    "index": "products-v2"
  },
  "script": {
    "source": """
      // Status field'ı standartlaştır
      if (ctx._source.status == 'aktif') {
        ctx._source.status = 'active';
      }
      // Silinen ürünleri atla
      if (ctx._source.status == 'deleted') {
        ctx.op = 'noop';
      }
    """
  }
}

// Task ID al ve izle
GET _tasks?detailed=true&actions=*reindex

Adım 4: Reindex Tamamlandıktan Sonra

// Ayarları geri al
PUT products-v2/_settings
{
  "index.refresh_interval": "1s",
  "index.number_of_replicas": 1
}

// Refresh zorla
POST products-v2/_refresh

// Document sayısını karşılaştır
GET _cat/count/products-v1?v
GET _cat/count/products-v2?v

Adım 5: Atomik Alias Swap

POST _aliases
{
  "actions": [
    { "remove": { "index": "products-v1", "alias": "products" } },
    { "add": { "index": "products-v2", "alias": "products" } }
  ]
}

Adım 6: Doğrulama ve Temizlik

// Yeni index'ten arama test
GET products/_search
{
  "query": {
    "match": {
      "description": "harika bir ürün"
    }
  }
}

// Her şey OK ise eski index'i sil
DELETE products-v1

Bu Sürecin Java Otomasyonu

public class ZeroDowntimeMigration {
    private final ElasticsearchClient client;

    public void migrate(String sourceIndex, String destIndex, 
                       String aliasName) throws Exception {

        // 1. Yeni index'i oluştur (mapping ayrıca tanımlanmış olmalı)
        // 2. Performans ayarları
        client.indices().putSettings(s -> s
            .index(destIndex)
            .settings(st -> st
                .refreshInterval(t -> t.time("-1"))
                .numberOfReplicas("0")
            )
        );

        // 3. Reindex
        var reindexResponse = client.reindex(r -> r
            .source(src -> src.index(sourceIndex).size(5000))
            .dest(dst -> dst.index(destIndex))
            .waitForCompletion(false)
        );

        String taskId = reindexResponse.task();
        System.out.println("Reindex task started: " + taskId);

        // 4. Task'i izle
        waitForTask(taskId);

        // 5. Ayarları geri al
        client.indices().putSettings(s -> s
            .index(destIndex)
            .settings(st -> st
                .refreshInterval(t -> t.time("1s"))
                .numberOfReplicas("1")
            )
        );

        client.indices().refresh(r -> r.index(destIndex));

        // 6. Atomik alias swap
        client.indices().updateAliases(a -> a
            .actions(act -> act.remove(rm -> rm
                .index(sourceIndex)
                .alias(aliasName)
            ))
            .actions(act -> act.add(ad -> ad
                .index(destIndex)
                .alias(aliasName)
            ))
        );

        System.out.println("Migration complete! Alias '" +
            aliasName + "' now points to '" + destIndex + "'");
    }

    private void waitForTask(String taskId) throws Exception {
        while (true) {
            var task = client.tasks().get(g -> g.taskId(taskId));
            if (task.completed()) {
                System.out.println("Reindex completed!");
                break;
            }
            var status = task.task().status();
            System.out.println("Progress: " + status);
            Thread.sleep(5000);
        }
    }
}

7. Update by Query — Toplu Güncelleme

Reindex gibi ama aynı index üzerinde çalışır:

// Tüm "draft" status'lu ürünleri "pending_review" yap
POST products/_update_by_query
{
  "query": {
    "term": { "status": "draft" }
  },
  "script": {
    "source": "ctx._source.status = 'pending_review'; ctx._source.updated_at = params.now",
    "params": {
      "now": "2024-01-15T14:00:00Z"
    }
  }
}

// Async ve throttled
POST products/_update_by_query?wait_for_completion=false&requests_per_second=500&conflicts=proceed
{
  "query": {
    "range": {
      "created_at": { "lt": "2023-01-01" }
    }
  },
  "script": {
    "source": "ctx._source.archived = true"
  }
}

Delete by Query

// Belirli koşula uyan document'ları toplu silme
POST products/_delete_by_query
{
  "query": {
    "bool": {
      "filter": [
        { "term": { "status": "deleted" } },
        { "range": { "deleted_at": { "lt": "now-90d" } } }
      ]
    }
  }
}

// Async ve throttled
POST logs/_delete_by_query?wait_for_completion=false&requests_per_second=1000
{
  "query": {
    "range": {
      "@timestamp": { "lt": "now-180d" }
    }
  }
}

8. Best Practices

✅ Yap

KonuÖneri
Batch boyutu5-15MB veya 1000-5000 doc/batch — benchmark ile belirle
Bulk öncesiRefresh interval -1, replica 0 (initial load)
Reindexwait_for_completion=false ile async çalıştır
AliasHer zaman alias üzerinden oku/yaz — doğrudan index adı kullanma
Alias swapTek atomik operasyonda yap — ayrı remove + add yapmayın
ThrottleProduction'da requests_per_second ile throttle kullan
Hata kontrolüBulk response'ta errors flag'ini mutlaka kontrol et

❌ Yapma

KonuNeden
Tek tek HTTP requestHer request overhead yaratır, bulk kullan
100MB batchMemory patlatır, timeout alırsın
Reindex sırasında replica 2Her document 3 kopyaya yazılır, çok yavaş
Alias'sız doğrudan index adıMigration'da uygulama kodunu değiştirmek zorunda kalırsın
Sync reindex (büyük data)HTTP timeout'a düşer, async kullan

9. Yaygın Hatalar ve Çözümleri

Hata 1: Bulk Request Parse Error

// Sorun: "Malformed action/metadata line"
// Neden: JSON pretty-print veya eksik newline

// ❌ YANLIŞ — Pretty-printed JSON
POST _bulk
{
  "index": {
    "_index": "products",
    "_id": "1"
  }
}
{
  "name": "Laptop"
}

// ✅ DOĞRU — Tek satırda
POST _bulk
{"index": {"_index": "products", "_id": "1"}}
{"name": "Laptop"}

Hata 2: Version Conflict (Reindex)

// Sorun: Reindex sırasında kaynak index'e yazma oluyor → conflict
// Çözüm: conflicts: proceed
POST _reindex
{
  "conflicts": "proceed",
  "source": { "index": "products-v1" },
  "dest": { "index": "products-v2" }
}

Hata 3: Reindex Çok Yavaş

// Sorun: Reindex saatler sürüyor
// Çözüm 1: Slicing ile paralelize et
POST _reindex?slices=auto
{
  "source": { "index": "large-index" },
  "dest": { "index": "large-index-v2" }
}

// Çözüm 2: Hedef index'i optimize et
PUT large-index-v2/_settings
{
  "index.refresh_interval": "-1",
  "index.number_of_replicas": 0,
  "index.translog.durability": "async",
  "index.translog.flush_threshold_size": "1gb"
}

Hata 4: Alias Çakışması

// Sorun: Yeni index adı mevcut bir alias ile aynı
// ES hata verir: "index and alias have the same name"
// Çözüm: Index ve alias adlarını farklı tut

// Konvansiyon:
// Index: products-v1, products-v2, products-v3
// Alias: products (read), products-write (write)

Hata 5: Bulk Timeout

// Sorun: Bulk işlem timeout'a düşüyor
// Çözüm 1: Batch boyutunu küçült
// Çözüm 2: Timeout'u artır

POST _bulk?timeout=5m
{"index": {"_index": "products"}}
{"name": "Product 1"}
// ...

10. Reindex Alternatifleri: Ne Zaman Hangisi?

SenaryoÇözüm
Mapping değişikliği_reindex + alias swap
Aynı index'te toplu güncelleme_update_by_query
Toplu silme_delete_by_query
Farklı cluster'a taşımaRemote _reindex
Shard sayısı azaltma_shrink API
Shard sayısı artırma_split API
Harici veri kaynağından yüklemeLogstash veya Bulk API
Günlük index rotasyonuILM + Rollover

Özet

  1. Bulk API toplu işlemler için zorunlu — tek tek HTTP request göndermek kabul edilemez performans kaybı yaratır.

  2. Optimal batch boyutu 5-15MB — benchmark ile kendi ortamınız için ince ayar yapın. Çok büyük batch timeout, çok küçük batch overhead yaratır.

  3. Reindex API mapping değişikliği, veri dönüşümü ve cluster arası taşıma için kullanılır — slices=auto ile paralelize edin, wait_for_completion=false ile async çalıştırın.

  4. Aliases production'da olmazsa olmaz — uygulama kodunuz asla doğrudan index adını bilmemeli. Alias swap ile sıfır kesinti geçişi yapılır.

  5. Atomik alias swap ile eski index'ten yenisine geçiş tek operasyonda yapılır — arada boşluk yok, sorgu kaybı yok.

  6. Bulk öncesi optimizasyon (refresh kapatma, replica azaltma) yazma hızını dramatik artırır — ama sadece ilk yükleme senaryolarında güvenlidir.