Tüm yazılara dön

Dais Technology: Postgres JSON'dan Elasticsearch Aramaya

Sigorta satış verisi Postgres JSON'da, arama AWS üzerinde Elasticsearch'te—1-50 ngram, her yazmada refresh ve sorgu başına get_mapping teşhisi.

Dais Technology: Postgres JSON'dan Elasticsearch Aramaya

Dais Technology bir insurtech. Platform, perakende iş ortakları için koruma planları satıyor ve servis ediyor; kayıt sistemi Postgres—üstelik workflow state'lerini iç içe JSON olarak tutan JSONB kolonlarla, bazen JSON içinde serialize edilmiş JSON'la. Arama ise AWS üzerinde Elasticsearch'te: transaction ID, müşteri adı ya da yarım SKU yazıldığında UI satış ve claim kayıtlarında anında cevap vermek zorunda.

İlk görüşmeden çıkan semptom listesi kısa ve dürüsttü: indexing latency, web sayfasında güncellemenin geç görünmesi ("muhtemelen cache"), aggregation soruları, full-text search davranışı, cluster AWS'te. Bu liste beş ayrı hikâye değil, tek hikâye çıktı.

Kısa cevap: Dais, Postgres satırlarını—JSONB'yi ->/->> operatörleriyle düzleştirerek—data + metadata şeklinde kurgulanmış entity bazlı Elasticsearch index'lerine sync ediyor; rol ve tenant güvenliği mapping'in _meta bloğunda duruyor ve sorgu anında filtre olarak uygulanıyor. Latency şikâyetleri üç kararın bileşkesiydi: her text alanının 1-50 karakterlik bütün parçalarını index'leyen ngram tokenizer, her doküman yazımından sonra explicit refresh ve her aramada bir get_mapping turu. Üçü de kullanıcı deneyimini bozmadan düzeltilebilir.

Sorun

Ortam, us-west-2'de Amazon OpenSearch Service: 7.1 ve 7.10 Elasticsearch-engine domain'leri, hepsi VPC-only, her environment (dev, integration, UAT, production) için ayrı domain. Ürünün kalbi insurance_sale ve insurance_claim index'leri: ekip ve iş ortakları sürekli arama yapıyor; Postgres'teki her satış ve claim güncellemesi hızla aranabilir olmalı.

Pipeline'ın iki sahibi var. Bir Java servisi alan alan index tanımlarını (sale.json, claim.json) tutuyor ve index'leri kodla oluşturuyor. Python tabanlı bir search mikroservisi frontend için Elasticsearch client'ını sarıyor: sorguları kuruyor, dokümanları doc_as_upsert update olarak yazıyor, yetkilendirmeyi uyguluyor. Database sync tarafında Logstash JDBC prototipi de vardı—Postgres driver jar, info ->> 'customer' tarzı JSON düzleştiren SELECT, cron schedule.

Şikâyetler kullanıcıya dokunuyordu: yazma yavaş hissettiriyor, yeni düzenlenen bir satış bazen sayfada hemen görünmüyordu.

Teşhis

1-50 ngram tokenizer

Indexing latency'nin adresi mapping'di. Aranabilir bütün text alanları—isimler, adresler, SKU'lar, ürün açıklamaları—custom ngram_analyzer ile analiz ediliyordu:

"tokenizer": {
  "ngram_tokenizer": {
    "type": "ngram",
    "min_gram": "1",
    "max_gram": "50",
    "token_chars": ["letter", "digit", "punctuation", "symbol", "custom"],
    "custom_token_chars": "_\\+/-"
  }
}

Elasticsearch'ün bunu kabul etmesi için max_ngram_diff: 49 da eklenmiş. Kazanç "her yerde her substring eşleşsin"; bedel ağır: 50 karakterlik bir ürün açıklaması alan başına, doküman başına bin mertebesinde token üretiyor. Index boyutu, segment merge baskısı ve doküman başına indexleme süresi bununla ölçekleniyor. Arama tarafı ayrı bir partial_text_search analyzer (whitespace + lowercase) kullandığı için sorgular ucuzdu—bütün acı yazma anında toplanmıştı. Elastic'in ngram tokenizer dokümanı max_ngram_diff default'unu 1 tutarak bunu zaten ima ediyor.

Her yazmada refresh

Python servisi her doküman yazımını—ve her bulk'ı—explicit indices.refresh() ile bitiriyordu. "Web sayfasında güncelleme gecikiyor" raporunun açıklaması bu: ekip yazmaları senkron görünür kılmak için refresh'i zorlamış, karşılığını indexing throughput ile ödemişti. Yazma başına refresh bir sürü minik segment üretir ve Elasticsearch'ün batching modelini boşa çıkarır; yük altında cluster'ı yavaş hissettirmenin en garantili yollarından biridir.

Arama başına mapping okuması

Rol bazlı güvenlik akıllıca bir yerde duruyordu: mapping'in _meta bloğu searchable_fields (boost'larla, ör. data.transactionId^10) ve permission_metadata tutuyor—öncelik sıralı bir rol listesi; admin rolü serbest geçerken perakende-ortak rolü storeCode ile filtrelenmek zorunda. Search servisi bu _meta'yı her istekte get_mapping ile okuyor, sonra operator: and ile cross_fields multi_match, metadata.tenantId.keyword üzerinde term filtresi, rol filtreleri ve rol eşleşmezse geçilmez bir filtre kuruyordu. Tasarım doğru—ama neredeyse hiç değişmeyen veri için her sorguda ekstra get_mapping turu hem latency hem cluster-state trafiği ekliyor.

Alışkanlıktan sharding

Entity index'leri 5 primary shard + 1 replica taşıyordu—bir sizing kararı değil, 7.x öncesi default'un devamı. Tek entity'lik bir arama index'i için bu, küçük segmentleri ve shard başına overhead'i cluster geneline çarpan etkisiyle yayar.

Çözüm

Öneriler kullanıcıya dönük sözleşmeyi bozmadan kuruldu:

  1. Ngram'ı sınırla. min_gram/max_gram'i makul bir pencereye çek (prefix için edge-ngram, infix için 2-3'ten ~10'a), ya da prefix senaryolarını search_as_you_type/keyword wildcard'a taşı. Paketteki en büyük indexing-latency kolu bu.
  2. Refresh-per-write'ı kaldır. UI'nin beklediği spesifik yazmada refresh=wait_for, gerisinde index seviyesinde refresh_interval. Aynı read-your-own-write deneyimi, segment fırtınası yok.
  3. _meta'yı cache'le. Searchable field'lar ve permission metadata sorgu başına değil deploy başına değişir—serviste kısa TTL ile cache'le, get_mapping'i hot path'ten çıkar.
  4. Shard'ları doğru boyutlandır. Bu veri hacminde entity index başına bir primary (+ replica); büyüme bilinçli olsun, default'la değil.
  5. İyi parçaları koru. dynamic: false mapping, data/metadata doküman ayrımı, tenant term filtreleri ve boost'lu multi_match multi-tenant entity aramanın tam olması gereken hali. Postgres JSON operatörlü Logstash JDBC hattı da app seviyesindeki upsert'in yanında meşru bir sync seçeneği.

Ayrıca production domain'e karşı container tabanlı Metricbeat monitoring kurduk; indexing baskısı ve refresh davranışı anekdot olmaktan çıkıp görünür hale geldi—searchali.com monitoring tarafında savunduğum görünürlük argümanının aynısı.

Sonuçlar

Sadece evidence pack'in desteklediği kadar:

Madde Kanıt
Indexing/update latency kök nedenleri belirlendi 1-50 ngram mapping, her yazmada refresh, sorgu başına get_mapping (kod + mapping)
Multi-tenant arama güvenliği sorgu anı filtreleriyle doğrulandı _meta permission metadata + tenant term filtresi
Postgres JSON → Elasticsearch sync desenleri belgelendi ->> düzleştirmeli JDBC/Logstash prototipi; app seviyesinde doc_as_upsert
Cluster monitoring devreye alındı Production domain'e Metricbeat container, 10 sn pull aralığı

Öncesi/sonrası latency yüzdeleri, index boyutları veya throughput sayıları: kaynak pakette yok, burada da yok.

Öğrendiklerimiz

  1. Ngram, indexleme anından alınmış kredidir. min_gram: 1, max_gram: 50 her alanın ~O(uzunluk × 50) token ödemesi demek. Pencereyi sınırla ya da edge-ngram kullan.
  2. Asla yazma başına refresh yapma. Önemli tek yazmada refresh=wait_for; gerisi refresh_interval.
  3. "Sayfa eski veri gösteriyor" çoğu zaman refresh-mi-cache-mi sorusudur. Bir forced refresh daha eklemeden önce hangi katman olduğunu teşhis et.
  4. Mapping'ten gelen config'i cache'le. _meta searchable field ve permission için harika bir yer—sorgu başına okumak değil.
  5. Shard sayısı karardır, default değil. Mütevazı bir entity index'ine beş primary, 7.x ataletidir.
  6. Postgres JSON temiz düzleşir. JDBC statement içinde info -> 'items' ->> 'qty', JSON'u pipeline'da yeniden şekillendirmekten iyidir.

Kaynağı Postgres olan, AWS Elasticsearch/OpenSearch üzerinde koşan ve açıklayamadığınız latency üreten bir arama mı var? Tam olarak yaptığım iş bu → searchali.com

Arama altyapınızı sınırların ötesine taşıyalım.

Yüksek performanslı ve hatasız bir arama deneyimi için hemen iletişime geçin.