4 puan yazan GN⁺ 2023-09-24 | 1 yorum | WhatsApp'ta paylaş
  • Postgres değişikliklerini diğer sistemlere gerçek zamanlı aktarmak için CDC (Change Data Capture) gerekir; basit bildirimlerden WAL tabanlı replikasyona kadar seçeneklerin güvenilirliği ve operasyon yükü oldukça farklıdır
  • Listen/Notify en hafif başlangıç yoludur; ancak at-most-once teslimat, geçici bildirimler ve 8000 bayt payload sınırı nedeniyle temel CDC’den çok yardımcı bir sinyale benzer
  • Tablo polling’i ve denetim tablosu (outbox pattern) standart tablolar ve trigger’larla uygulanabilir; ancak silme algılama, diff, commit sırası, yazma çoğalması ve backpressure sorunlarını sizin çözmeniz gerekir
  • Mantıksal replikasyon (logical replication) WAL’dan insert/update/delete işlemlerini stream eden güçlü bir yöntemdir; ancak replication slot, ack, yeniden başlatma ve throughput yönetimini uygulamanın üstlenmesi gerekir
  • Sequin, Postgres mantıksal replikasyonunu temel alarak değişiklikleri SQS, Kafka, Elasticsearch, Redis, HTTP endpoint’leri gibi hedeflere iletir ve replication slot’ları doğrudan yönetme yükünü azaltır

Postgres CDC’ye Ne Zaman İhtiyaç Duyulur?

  • Postgres, saklanan verileri yönetmede güçlüdür; ancak tablo değişiklikleriyle workflow tetiklemek veya verileri başka veri depolarına, sistemlere ya da servislere gerçek zamanlı stream etmek için veri hareketini ayrıca tasarlamak gerekir
  • Change Data Capture (CDC), veritabanı değişikliklerini belirleyip yakaladıktan sonra downstream sistemlere gerçek zamanlı aktarma yöntemidir
  • Postgres’te değişiklik yakalamanın birçok yolu vardır; uygulama zorluğu, güvenilirlik ve operasyon yükü birbirinden farklıdır

Listen/Notify: En Basit pub-sub

  • Postgres’in Listen/Notify özelliği süreçler arası iletişim sağlar ve publish-subscribe paterniyle çalışır
  • Bir session belirli bir kanalı listen eder; veritabanı aktiviteleri veya başka session’lar bu kanala notify gönderebilir
  • Değişiklik yakalamak için trigger eklenerek kullanılabilir
    • Örnek bir trigger, after insert or update or delete anında değişen kaydın table, id, action bilgisini JSON’a çevirir ve pg_notify('table_changes', payload::text) çağırır
  • Sınırları belirgindir
    • at-most-once teslimat semantiğine sahiptir; listener, bildirimin yayınlandığı anda bağlı olmalıdır
    • listener yalnızca abone olduktan sonraki bildirimleri alır; bu nedenle kısa bir ağ kesintisinde bile bildirim kaçırabilir
    • Payload boyutu sınırı 8000 bayttır; aşılırsa notify komutu başarısız olur
    • Payload boyutuna kanal adı da dahildir; Postgres identifier’ları gibi kanal adı en fazla 64 bayt olabilir
  • Temel değişiklik algılama veya tablo polling optimizasyonu için kullanılabilir; ancak karmaşık CDC gereksinimlerine pek uygun olmayabilir

Tablo Polling’i: Basit, Ama Silme ve Diff Konusunda Zayıf

  • En basit sağlam değişiklik yakalama yöntemi, tabloyu doğrudan polling ile taramaktır
  • Her tabloda, satır her güncellendiğinde yenilenen updated_at benzeri bir kolon gerekir; gerekirse trigger ile oluşturulabilir
  • updated_at ve id kombinasyonu cursor olarak kullanılır; uygulama mantığı cursor’ı saklar ve yönetir
  • Notify aboneliği de birlikte kullanılırsa, kayıt ekleme veya güncelleme olduğunu uygulamaya bildirerek polling sıklığı azaltılabilir
    • Postgres bildirimleri geçici olduğundan, bunları yalnızca polling üzerinde bir optimizasyon olarak kullanmak daha uygundur
  • Başlıca üç dezavantajı vardır
    • Silinen satırlar tabloda kalmadığı için silme algılama mümkün değildir
    • Buna çözüm olarak bir delete trigger’ının id ve gerekli kolonları deleted_contacts gibi ayrı bir tabloya yazması, uygulamanın da bu tabloyu polling ile okuması sağlanabilir
    • Bir kaydın güncellendiği anlaşılabilir, ancak neyin değiştiği bilinemez
    • Postgres datetime ve sequence değerlerinde commit sırası sapabildiğinden, updated_at temelinde bir bloğu okurken hâlâ commit sürecindeki satırlar kaçırılabilir
  • Silme, diff ve ara sıra yaşanan kaçırmalar büyük sorun değilse, basit değişiklik takibi için makul bir seçenektir

Denetim Tablosu: outbox pattern ile Değişiklik Günlüğü Saklama

  • Denetim tablosu (audit table) yöntemi, değişiklikleri ayrı bir changelog tablosuna kaydeder; outbox pattern olarak da bilinir
  • changelog içinde değişiklikle ilgili kolonlar bulunabilir
    • action: insert, update, delete olup olmadığı
    • old: değişiklik öncesi kaydın jsonb değeri; insert için boştur
    • values: değişen alanların jsonb değeri; delete için boştur
    • inserted_at: değişikliğin gerçekleştiği zaman
  • Uygulamak için her değişiklikte changelog tablosuna insert yapan bir trigger fonksiyonu ve izlenecek her tablo için trigger gerekir
  • changelog’u kuyruk gibi tüketmek de mümkündür
    • Uygulama worker’ı tablodan değişiklikleri alır
    • Yaklaşık exactly-once işleme için Postgres’in for update skip locked özelliği kullanılabilir
    • Worker bir transaction açıp order by timestamp limit 100 for update skip locked ile bir batch’i kilitleyebilir, işleyebilir, işlenen kayıtları silebilir ve ardından commit edebilir
  • Operasyonel dezavantajları vardır
    • Tek bir tablo yazımı, denetim tablosunda birden fazla yazım üreten yazma çoğalması (write amplification) yaratır
    • Genellikle denetim tablosuna ilk insert, işleme sırasında update ve işlemden sonra delete olmak üzere en az üç yazma oluşur
    • Worker’lara fan-out etme biçimi, uygulamaya göre ayrıca tasarlanmalıdır
    • Prodüksiyon ölçeğinde dağıtımdan önce trigger fonksiyonunu ve tablo tasarımını ayarlamak gerekebilir
    • Bir worker’ın değişikliği checkout edilmiş halde ne kadar tutabileceğine ilişkin zaman sınırı gibi ayrıntılı politikalar da düşünülebilir
    • Worker başarılı şekilde işleyemese bile denetim tablosu dolmaya devam eder; bu nedenle backpressure yönetimi yetersizdir

Foreign Data Wrapper: Belirli Postgres’ler Arası Senkronizasyona Daha Yakın Bir Seçenek

  • Foreign Data Wrapper (FDW), Postgres veritabanının harici veri kaynaklarını okuyup yazmasını sağlayan bir özelliktir
  • En yaygın desteklenen FDW tabanlı eklenti postgres_fdw’dir
    • İki Postgres veritabanını bağlayabilir ve bir veritabanında diğer veritabanının tablolarına referans veren view benzeri bir yapı oluşturabilirsiniz
    • İçeride bir Postgres veritabanı client, diğeri server olur
    • Foreign table’a sorgu atıldığında client veritabanı, server veritabanına Postgres wire protocol üzerinden sorgu gönderir
  • FDW, değişiklik yakalama yöntemi olarak yaygın değildir ve çok spesifik durumlar dışında önerilmesi zordur
  • Bir Postgres veritabanındaki değişiklikleri başka bir Postgres veritabanına yazmak istiyorsanız FDW uygun olabilir
    • Örnek olarak muhasebe veritabanı ile uygulama veritabanının ayrı kullanıldığı bir durum verilebilir
    • Ara değişiklik yakalama adımını atlayıp postgres_fdw ile veritabanları arasında doğrudan yansıtma yapılabilir
  • Dahili API’ye değişiklikleri POST eden özel bir FDW yazmak da mümkündür
    • API’ye commit içinde yazdığı için API değişikliği reddedebilir ve commit’i rollback edebilir
  • FDW güçlüdür; ancak CDC amacıyla en iyi seçenek olduğu durumlar nadirdir ve özel FDW yazmak değişiklik yakalama yöntemleri arasındaki en büyük işlerden birine yakındır
    • Özel FDW yazmak Supabase wrappers gibi araçlarla kolaylaşmıştır; yine de büyük bir iştir

Doğrudan Mantıksal Replikasyon: WAL Tabanlı Güçlü CDC

  • Postgres’in veritabanı replikasyonu için bir protokolü vardır; bunlardan biri mantıksal replikasyon (logical replication)’dır
  • Mantıksal replikasyon, Postgres’in WAL (write-ahead log) altyapısı üzerine kuruludur
    • Veritabanındaki tüm insert, update ve delete işlemleri izlenir
    • Değişiklikler subscriber’a stream edilir
  • Kullanıcı önce primary üzerinde bir replication slot oluşturur
    • pg_create_logical_replication_slot('<your_slot_name>', '<output_plugin>') biçimi kullanılır
  • output_plugin, WAL değişikliklerini decode edecek eklentiyi belirtir
    • pgoutput varsayılan eklentidir ve client server’ın beklediği binary formatta çıktı üretir
    • test_decoding, WAL değişikliklerini insan tarafından okunabilir biçimde sunan basit bir output plugin’idir
    • Postgres’in yerleşik eklentisi olmasa da popüler bir eklenti olan wal2json vardır; JSON, uygulamaya başlamak için Postgres binary formatına göre daha kolay işlenir
  • Replication slot oluşturulduktan sonra başlatılabilir ve tüketilebilir
    • Replication slot, standart sorgulardan farklı bir Postgres protokol alanını kullanır
    • Birçok client kütüphanesi replication slot işlemlerine yardımcı fonksiyonlar sunar
    • psycopg2 örneği, WAL mesajlarını tüketmek için cursor.start_replication(...) ve cursor.consume_stream(...), ack göndermek için cursor.send_feedback(flush_lsn=msg.wal_end) kullanır
  • Client, aldığı WAL mesajlarına ack göndermelidir; replication slot, offset’i olan Kafka’ya benzer şekilde çalışır
  • Mantıksal replikasyon CDC için tasarlanmış sağlam bir yöntemdir, ancak karmaşıktır
    • Replication slot ve replication protocol, geliştiricilere sıradan tablolar ve sorgular kadar tanıdık değildir
    • Yeniden başlatma sırasında mesaj kaçırmamak için bir strateji gerekir
    • Postgres’ten gelen yüksek hacimli mesajları işleyebilecek şekilde tasarlanmalıdır

Sequin: Mantıksal Replikasyonu Saran Bir CDC Aracı

  • Sequin, Postgres değişikliklerini ve satırlarını kuyruklara, stream’lere, arama indekslerine, cache’lere, HTTP endpoint’lerine vb. ileten bir CDC aracıdır
  • Hedefler arasında SQS, Kafka, Elasticsearch, Redis, HTTP endpoint’leri gibi seçenekler bulunur
  • Sequin içeride Postgres mantıksal replikasyonunu kullanır, ancak low-level protokol karmaşıklığını soyutlar
  • insert, update ve delete işlemlerinin tamamını yakalayabilir; update ve delete sırasında satırın hem new hem de old değerlerini yakalar
  • Sequin’i düşünmek için koşullar şunlardır
    • Gerçek zamanlı CDC’ye ihtiyacınız vardır
    • SQS veya webhook gibi hedeflere aracı sistem olmadan doğrudan stream etmek istersiniz
    • Geçmiş veri backfill’i ve SQL where koşuluna dayalı değişiklik filtreleme gibi özelliklere ihtiyacınız vardır
    • Replication slot’ları doğrudan yönetmekten daha basit bir alternatif gerekir
    • exactly-once işleme garantisine ihtiyacınız vardır
  • Dezavantajları da vardır
    • Sequin, Postgres içinde çalışan bir eklenti değil, veritabanının yanında çalışan üçüncü taraf bir araçtır
    • Eklenti olmadığı için herhangi bir Postgres veritabanıyla geniş uyumluluk sunar; ancak Sequin Cloud kullanmıyorsanız ek altyapıyı kendiniz kurmanız gerekir

Seçim Kriterleri

  • Başlangıç aşamasında Listen/Notify ve tablo polling’i uygundur
    • Listen/Notify, kritik olmayan event yakalama, prototipleme ve polling optimizasyonu için iyidir
    • Polling, basit kullanım senaryoları için makul ve doğrudan bir çözümdür
  • Biraz daha ciddi aşamada denetim tablosu ara seçenek olabilir
    • Satırın new ve old payload’larını yakalayabilir
    • Doğru kurulursa exactly-once işleme sistemi elde edilebilir
    • Ölçeklenirken yazma çoğalması ve backpressure eksikliği sorun olur; manuel yapılandırmada hata yapılırsa mesaj kaybedilebilir
  • Ölçeklenme aşamasında mantıksal replikasyon sağlam çözüme en yakın yoldur
    • Ancak slot’tan doğrudan okumak yerine Sequin gibi bir araç kullanmak önerilir
  • FDW ilginç bir özelliktir; ancak genel CDC gereksinimlerini çözme olasılığı düşüktür

1 yorum

 
GN⁺ 2023-09-24
Hacker News yorumları
  • Trigger + geçmiş tablosu (denetim tablosu) vakaların %98’inde doğru çözümdür. Zaten kullanmıyorsanız bugünden itibaren kullanabilirsiniz. 30 yıldan uzun süredir kendini kanıtlamış bir tekniktir.
    Genel biçimde uygulamanın basit bir örneği https://gist.github.com/slotrans/353952c4f383596e6fe8777db5d... adresinde var. Alan verimliliğinden vazgeçip “kolay uygulamayı” seçen bir yaklaşım.
    Değişmez verileri saklayabiliyorsanız gerçekten harika; ancak veritabanınızda muhtemelen çok fazla değişebilir veri vardır ve her gün pek çok şeyi unutuyor olabilirsiniz. Unutmayın, geçmiş tablosu kullanın.
    Referans: https://github.com/matthiasn/talk-transcripts/blob/master/Hi...
    Papertrail gibi uygulama katmanındaki geçmiş izleme kütüphanelerini veya tekniklerini kullanmamak daha iyi. Yavaştırlar, hataya açıktırlar ve uygulama stack’ini baypas eden DB değişikliklerini yakalayamazlar. updated zaman damgasını uygulamadan basmaya çalışmak da temelde yanlıştır; çünkü her web sunucusunun saati farklıdır. DB saatini kullanmalısınız; doğru olan tek saat odur.

    • Tutarlılık için zamanı istemcide üretmek yerine sorgunun içine now() gibi bir çağrı koyup DB saatini kullanmak doğru yaklaşımdır.
      Ancak yalnızca bu zaman damgasıyla senkronizasyon yapmak yeterli değildir. Çünkü zaman damgası, transaction commit anında değil transaction başlangıç anında oluşturulur.
      Tabloyu poll ederek son zaman damgasına göre filtrelerseniz, commit sırası karışmış transaction’ların bir kısmını kaçırabilirsiniz. Birkaç dakika daha geriye bakan ve tekrarları eleyen bir tampon aralık koyabilirsiniz; ancak PostgreSQL’de transaction süresi sınırsızdır ve çok geriye bakmak ciddi israf yaratır. Doğruluk ve verimlilik önemliyse bu yöntem uygun değildir.
    • Estuary (https://estuary.dev, CTO’suyum), operasyonel DB’de ek yapılandırma gerektirmeden, bulut depolama üzerinde tüm veritabanı değişikliklerinin gerçek zamanlı data lake değişiklik günlüğünü oluşturur.
      Log sıra numarası, DB zamanı ve REPLICA IDENTITY FULL kullanıldığında değişiklik öncesi/sonrası durumlar da dahil edilir. Sonrasında koleksiyonu Snowflake gibi bir yerde materialize ederseniz, kaynak DB güncellemelerini takip eden senkronize bir tabloyu varsayılan olarak elde edebilirsiniz.
      Aynı temel data lake üzerinden denetim amaçlı tam tablo geçmişini dönüştürmek veya materialize etmek de mümkündür; böylece kaynak DB’ye tekrar capture ya da WAL reader bağlamaya gerek kalmaz.
    • Trigger içinde oturum değişkenlerine başvurarak, değişikliğin nedeni hakkında yorum gibi ek bilgileri geçmişe eklemek mümkün oldu. Bunu yalnızca küçük bir kişisel projede denedim ama şimdiye kadar iyi çalıştı.
    • Örneği SQLite’a port edip çalışmasını gösterdim: https://chat.openai.com/share/b5113cb1-10df-4a38-adde-5ec0e7...
      Benzer deseni JSON yerine sütun tabanlı olarak uygulayan SQLite yöntemini de ayrıca anlattım: https://simonwillison.net/2023/Apr/15/sqlite-history/
    • Bu yaklaşım iyi; gerçekten de uygulamanın aktivite feed’ini bu şekilde oluşturuyoruz. Ancak “değiklikleri dışarı push etme” problemini kendi başına çözmüyor. Elbette denetim tablosunun WAL değişikliklerini dinlerseniz iki avantajı birden elde edebilirsiniz.
  • Bu yazı, Postgres’in yerleşik özellikleriyle mümkün olan çeşitli yaklaşımları kısa ve iyi bir şekilde özetliyor.
    “Değişiklikleri denetim tablosunda yakalama” bölümünde, önceki şirketimde Temporal Tables desenini faydalı şekilde kullanmıştık. Diğer başlıca ilişkisel DBMS’lerden farklı olarak Postgres’in kendisinde yerleşik değil; ancak SQL fonksiyonuyla kullanılabilen basit bir desen var: https://github.com/nearform/temporal_tables
    Belirli bir andaki tablo durumunu görebildiğiniz için “12 Ağustos’ta bu kullanıcının ayarları neydi?”, “Dün gece 23:55’te kaç bekleyen kayıt vardı?”, “Şu an ile bir hafta önceki feature flag farklarını göster” gibi sorulara yanıt verebilirsiniz.

  • Daha önce çok büyük bir monolitik SQL Server’ı olan bir şirkete danışmanlık yapmıştım. Postgres değildi ama Postgres olduğunu varsaysak da durum benzer olurdu.
    Onlarca yıl boyunca işletimde kalmış, şirket içindeki türlü türlü amaçlar için kullanılmıştı; fiilen şirket genelindeki tüm uygulamalar ve iş süreçleri verilerini bu veritabanına kaydediyordu.
    Sorun şuydu: Bu DB’yi sorgulayan çok sayıda uygulama vardı; veri ekleyen ve değiştiren süreçler ile prosedürler de inanılmaz fazlaydı. Üst taraftaki ekleme/değiştirme süreçleri değiştikçe veya yenileri eklendikçe uygulama düzeyindeki değişmez koşulların bozulduğu durumlar ortaya çıkıyordu. Normal süreçler bile kötü veri olduğunda farklı davranıyordu.
    Kök nedeni izlemek çok zordu; çünkü incelenen şeyler çoğunlukla 10 yıl önce yazılmıştı ve o çalışanlar çoktan şirketten ayrılmıştı.
    Postgres veritabanındaki değişiklikleri bir tür DAG biçiminde yakalayıp hangi sürecin veriyi eklediğini, değiştirdiğini, sildiğini ve tarihsel olarak nasıl davrandığını; farklı uygulamaların bu veriyi nasıl sorguladığını ve sorgu istatistiklerinin zaman içinde nasıl değiştiğini görebilir miyiz diye merak ediyorum.
    Böyle bir öncül örnek var mı, böyle bir aracı hangi yaklaşımla geliştirmek mümkün olur, pek emin değilim. Geçmişte benzer bir şey yapmayı düşünmüştüm ama doğru tercihler yapabilmek için Postgres çekirdek mühendisi düzeyinde anlayış gerektiren bir alan gibi geliyor.

    • Postgres’in mantıksal replikasyonunda, başka bir veritabanında aynı durumu mantıksal olarak yeniden oluşturmak için gereken değişiklik ifadeleri, yani ekleme/değiştirme/silme bilgileri tamamen yer alır.
      Her değişiklik için istemci düzeyinde kaynak verisini alamazsınız.
      Yine de etrafından dolaşmak mümkün. Mantıksal replikasyon akışı pg_logical_emit_message fonksiyonunun bilgi mesajlarını da içerebildiğinden, istemci doğrudan metadata ekleyebilir. Her transaction başlangıcında istemci tanımlayıcısını yayacak şekilde ayarlamak da mümkün olabilir.
    • Sorguları nasıl ele almak gerekir bilmiyorum ama ekleme/değiştirmeler için olay kaynağını (last updated by) izleyen bir kolon koyarım. Bir anti-pattern olabilir; daha sağlam bir çözüm varsa iyi olurdu.
    • Teknik olarak log replikasyonunda tüm aktörlerin yaptığı tüm işlemler bulunur; trigger’lar dikkatli kullanılırsa DDL/DML yakalama tabloları ile de her şey izlenebilir. DCL konusunda endişe varsa o da dahil edilebilir.
      Bu yaklaşım WAL veya trigger kullanan neredeyse tüm SQL ailesi çözümlerinde çalışır.
      SQL Server’da trigger yaklaşımını birkaç kez kullandım ama tüm sorguları loglayınca yavaşlama eğilimi oluyor. İşletimi engellemeyen bir insert mekanizması tasarlamak kusursuz değil; sampling gerekebilir.
    • Her uygulamanın kendi DB kullanıcısına sahip olması bile epey bilgi sağlar.
    • DB’ye sorgu gönderen tüm script ve programları tarayıp, her sorguya ilgili script/programa bağlanan benzersiz ID yorumu ekleme fikri vardı. Sorgu logunda bu yorum ve ID kalırsa kaynağı izlemek mümkün olur gibi.
  • “Audit table” yoluna gidecekseniz doğrudan pgaudit kullanın. Sahada kendini kanıtlamış bir extension ve AWS kullanıyorsanız RDS’de de mevcut.
    https://github.com/pgaudit/pgaudit/blob/master/README.md
    https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Appen...

  • Bunu illa yapmanız gerekmiyor. Bunu istemek, Postgres’teki ilişkileri sözleşmeye dönüştürmek demek. Hiçbir servis iç durumunu kalıcı hale getiremez olur.
    Domain-driven design’a gerçekten kendinizi adarsanız mümkün olabilir; ama hafif ve pratik bir event-driven system kullanmak daha iyi.

    • Veritabanındaki ilişkiler, hoşunuza gitsin gitmesin, zaten bir sözleşme.
      Event-driven herhangi bir şey 1000 kat daha karmaşık.
  • updated_at kolonunu polling ile kontrol etme yöntemi, en basit biçimiyle sağlam değildir. Çünkü transaction’ların o sırayla commit edileceğinin garantisi yoktur.

    • Yazarıyım. İyi nokta. Örneğin transaction A başlar, before trigger çalışır ve Row 1’in updated_at değeri 2023-09-22 12:00:01 olarak ayarlanır.
      Kısa süre sonra transaction B başlar, Row 2’nin updated_at değeri 2023-09-22 12:00:02 olarak ayarlanır ve B önce commit eder.
      Polling sorgusu çalışır, Row 2’yi en son değişiklik olarak görüp cursor’ı 2023-09-22 12:00:02 olarak günceller; ardından A daha sonra commit ederse Row 1 kaçırılır.
      Bu sorundan kaçınmanın basit yolu neredeyse gerçek zamanlı polling yapmamaktır. Sıra eninde sonunda tutarlı hale gelir.
      Daha sağlam öneri sequence kullanmak olabilir. Örneğin satır her değiştiğinde artan bir updated_at_idx kolonu tutmak gibi.
    • Bunu ilk kez öğrendim. Kolonu güncellemek için trigger kullansak da böyle mi?
      now() koyan bir before trigger kullanıldığında da iki satırın updated_at timestamp’leri transaction commit sırasından farklı olabilir mi merak ediyorum. updated_at ile commit timestamp’inin aynı olması gerekmez ama updated_at, milisaniye/mikrosaniye düzeyinde commit sırasını doğru göstermeli.
    • Polling için updated_at yerine, trigger’ın mevcut transaction ID ile ayarladığı _txid kolonunu kullanıyoruz. Sonraki polling’de txid_current() ile hangi transaction’ların commit edildiğini, hangilerinin henüz edilmediğini kontrol ediyoruz.
      Biraz riskli ve sınır değeri hatası üretmeye çok açık ama yıllardır production’da sorunsuz çalışıyor.
  • Yazı harika.
    Elixir ve Postgres kullanıyorsanız, benzer bir yaklaşımla WAL değişikliklerini dinleyen küçük bir kütüphane hazırlamıştım: https://github.com/cpursley/walex

  • Bu yöntemlerin hepsi biraz kötü; kişisel olarak polling’in en pratik seçenek olduğunu düşünüyorum
    Postgres’in bu alanda yenilik yapması iyi olurdu

    • SQL standardına çeşitli zamansallık türlerini birinci sınıf özellik olarak eklemeye yönelik girişimler oldu
      SQL standardına girmeden, ilişkisel DBMS’lerin çekirdek alanında ivme kazanmasının zor olduğunu düşünüyorum. Seçenekler çok ve karmaşık; kullanıcı alanındaki başarılı çözümler de performans açısından aşırı yük getiren türden değil
      Bu arada, bu alanı araştıranlar genelde denetim tabloları yaklaşımına meylediyor. Çünkü veritabanı içinde tutarlı ACID özellikleri korunuyor ve proxy ya da polling işi eklemek yerine Postgres tek hata noktası olarak kalıyor
    • 1 saniyelik polling aralığı pratik mi?
  • Veri dünyasında büyük bir boşluk var. Veri deposuna sonuçları sormak yerine, sorgu sonuçlarının artımlı olarak push edilmesi iyi olurdu
    Gerçek zamanlı/streaming analizleri çok yapıyoruz; stream processing yapılabiliyor ve veri deposunun içinde materialized view’larla bunun bir kısmı ele alınabiliyor. Ama veri DB’ye ya da data lake’e girdikten sonra, aşağı akışta değişiklikleri görmek için pratikte yine polling’e dönülüyor
    Veride belirli bir durum oluştuğunda tepki vermek ya da sayfayı yenilemeden ekranı güncellemek için pek temiz çözüm yok. Bu yazıdaki çözümler de birinci sınıf özellik olmaktan çok geçici çözüm gibi görünüyor
    Sayfa yenilemeden gerçek zamanlı güncellenen bir rapor yapmak istiyorsanız, genelde veriyi DB’den yükleyip değişiklikleri Kafka ve WebSocket ile GUI’ye akıtma yoluna gidiliyor. Böylece bazı analizlerin kodda, bazılarının DB’de yapıldığı tuhaf bir lambda mimarisi işletmiş oluyorsunuz
    Bu alanda yenilikler var. KSQL ve Kafka Streams değişiklikleri dışarı verebiliyor, Materialize’da abonelikler var, ClickHouse’ta live view’lar var. Ama birçok özellik yeni ya da preview aşamasında ve tam oturmuyor. Hepsini denedim; geliştiriciye fazla iş yıktığını hissettim
    [select * from orders with suscribe] gibi bir seçenekle doğrudan değişiklik feed’i alabileceğiniz bir kütüphane olsa güzel olurdu. Yeterince önemli bir alan ama hak ettiği ilgiyi daha az gördü

  • Yazıda ele alınmayan büyük bir replikasyon tuzağı var; bu yüzden ben replikasyon kullanmıyorum
    Postgres, replikasyon slotu tüketicisinin veriyi kaçırmamasını çok güçlü biçimde garanti etmeye çalışıyor. Bu yüzden tüketici slottan veri tüketmezse Postgres kaçırılan veriyi nazikçe saklamaya devam ediyor ve sonunda disk dolup DB çökene kadar gidiyor. Prototipleme sırasında iki farklı SaaS DB’de bunu yaşadım; kurtarmak için destek bileti açmaktan başka çare yoktu
    Replikasyon slotu tüketicisi okumayı bırakırsa mutlaka alarm çalmalı
    Bir diğer neden de tablonun ilk snapshot’ını alan kod yolu ile değişiklikleri okuyan kod yolunun tamamen farklı olması. Hiçbir değişikliği kaçırmayacak şekilde replikasyon slotu okumasını başlatmak önemsiz bir iş değil
    Ne yazık ki değişiklik yakalama açısından replikasyon en az hacky çözüm
    Ben polling kullanıyorum ama updated_at yerine txid saklıyorum

    • Slotun alanı sürekli elde tutması yerine belirli bir boyutu aşınca geçersiz işaretlenmesi için boyut sınırı ayarlanabiliyor: https://www.postgresql.org/docs/current/runtime-config-repli...
      Hangi davranışı daha çok istediğinizi merak ediyorum
      Büyük veri hacimleriyle çalışıyorsanız ilk snapshot ile değişiklik okumayı farklı ele almak istersiniz. Çünkü paralel başlatma ya da fiziksel yedek tabanlı başlatma gibi işlemlerin mümkün olması gerekir. Yine de slot oluşturulduktan sonra mevcut verileri seçici olarak stream eden bir özelliğin faydalı olabileceğini anlıyorum
      Değişiklikleri kaçırmamak için replikasyon slotu okumasını başlatma kısmının zor olmaması gerekir gibi geliyor; nerede takıldığınızı merak ediyorum
    • İlk sorunu ele almanın püf noktalarından biri mantıksal decoding mesajlarını kendinize göndermektir. Böylece tutulan WAL düşük tutulabilir
      Tüm değişikliklere ihtiyaç olmadığında, bağlantı kesildiğinde kendini temizleyen geçici replikasyon slotları da kullanışlıdır. Sunucuyu öldürmemek için tutulan WAL’a maksimum sınır koyan bir yapılandırma da var
    • Bu tuzağa ben de düştüm. Gerçekten incelikli bir durum. Tüketiciyi kaldırmanın birincil DB üzerinde hiçbir etkisi olmaması gerekir gibi geliyor; ama gerçekte bir saatli bomba oluşuyor
      updated_at yerine txid’yi nasıl kullandığını biraz daha açıklarsan iyi olurdu