6 puan yazan GN⁺ 2023-09-25 | 1 yorum | WhatsApp'ta paylaş
  • Ayrı bir kuyruk sistemi getirmeden önce, zaten kullandığınız Postgres ile arka plan iş kuyruğu kurabiliyorsanız operasyonel basitlikten ve ekibin aşinalığından büyük ölçüde yararlanabilirsiniz
  • Postgres 9.5 sonrası NOTIFY/LISTEN ve FOR UPDATE SKIP LOCKED, yeni iş bildirimini ve çalışanlar arasında mükerrer işlemeyi önlemeyi birlikte çözer
  • Redis, Kafka, RabbitMQ ve Amazon SQS güçlü seçeneklerdir; ancak yeni bağımlılıklar geliştirme, test ve operasyon ortamlarına hata modları ve öğrenme maliyeti ekler
  • Postgres kuyruğu da her derde deva değildir: push/pop işlemleri Redis’ten yavaş olabilir; ayrı bir kuyruk veritabanı veya sunucu, daha sık VACUUM gibi operasyonel bedeller doğabilir
  • Arka plan iş kodunu kuyruk backend’ine bağlı kalmayacak şekilde soyutlamak ve yalnızca aşina olunan teknoloji gereksinimleri karşılamadığında başka kuyruk teknolojilerini değerlendirmek daha güvenlidir

Postgres kuyruğu nasıl çalışır

  • Postgres kuyruk teknolojisi iki unsurdan oluşur
    • Yeni işleri bildiren ve alan pub/sub
    • Birden fazla çalışanın aynı işi işlemesini engelleyen satır kilitleme
  • İki özellik de 2016’da yayımlanan Postgres 9.5’ten beri yerleşik olarak sunulur
  • NOTIFY ve LISTEN birlikte kullanıldığında uygulamaya pub/sub eklenebilir
  • FOR UPDATE SKIP LOCKED, koşullara uyan kayıtlarda satır kilidi alır ve zaten kilitli olan kayıtları atlar
    • İş kayıtlarına uygulandığında SELECT * FROM jobs ORDER BY created_at FOR UPDATE SKIP LOCKED LIMIT 1 gibi bir kuyruk işleme sorgusu oluşturulabilir
  • SKIP LOCKED, verinin “tutarsız” bir görünümünü sunar; ancak kuyruklarda bu özellik yararlıdır
    • Zaten işlenmekte olan işler, satır kilidi nedeniyle diğer çalışanlara görünmez
    • Bu davranış dağıtık karşılıklı dışlamayı mümkün kılar
  • NOTIFY ile LISTEN yapan çalışanlara yeni işler bildirildiğinde hem periyodik toplu işleme hem de gerçek zamanlı iş işleme kurulabilir

Redis’in arka plan işleri için varsayılan haline gelişi

  • Uzun süre çalışan işlerin nasıl ele alınacağı, birçok ödünleşim içeren bir teknoloji seçimidir
  • Yaygın kullanılan kuyruk ve mesaj aracısı seçenekleri şunlardır
    • Redis: Bellek içi veri deposu ve birçok arka plan iş kütüphanesinin backend’i
    • Apache Kafka: Apache Foundation tarafından yönetilen dağıtık olay akışı platformu
    • RabbitMQ: Yaygın biçimde dağıtılmış bir mesaj aracısı olarak tanıtılır
    • Amazon SQS: Yüksek ölçeklenebilir kuyruklar sağlayan Amazon SaaS’i
  • GitHub’daki background jobs topic içinde en popüler ilk 5 kütüphanenin tamamı Redis tabanlıdır
    1. Sidekiq — Ruby
    2. resque — Ruby
    3. rq — Python
    4. Hangfire — C#
    5. asynq — Go
  • Redis verileri bellekte sakladığı için ekleme ve okuma hızlıdır; pub/sub API’si ile list, set veri yapılarını sunduğundan kuyruk uygulamaları için iyi uyum sağlar
  • Redis’in ölçeklenebilirliği birçok geliştirici için varsayılan seçenek olmasını sağlar; varsayılanlar teknoloji seçiminde güçlü bir etkiye sahiptir

Ölçeklenebilirlikten önce bakılması gereken ölçütler

  • Teknoloji sektörünün “scale” takıntısı basitliği, bakım kolaylığını ve geliştiricilerin bilişsel yükünü azaltmayı geri plana itebilir
  • Birçok uygulama Google, Facebook veya Uber seviyesinde ölçeklenme gerektirmez
  • Bir projeye veya işe başlarken, en baştan yalnızca ölçeklenebilirliği optimize etmek yerine önce şu ölçütlere bakmak gerekir
    • Ekibin iyi bildiği teknoloji
    • Kullanıcı ihtiyaçlarını karşılayan yeterince iyi teknoloji
    • Ekip yetkinliklerine uygun ve en az zahmetli çözüm
  • Postgres pratikte iyi ölçeklenir; ancak kuyruk kullanım senaryosunda en çok ölçeklenen uzmanlaşmış kuyruk sistemi değildir
  • Genel amaçlı bir yazılım olan Postgres birçok işi oldukça iyi yapar; kuyruk da bunlardan biri olarak ele alınabilir

“Sıkıcı teknoloji” ile karar vermek

  • Kuyruk teknolojisi seçerken en önemli soru, şu anda hangi teknolojiyi kullandığınız ve iyi anladığınızdır
  • Zaten kullanılan ve iyi bilinen teknolojinin yazılım yığınına eklediği yük daha küçüktür
  • Halihazırda ilişkisel veritabanı kullanıyorsanız ve bu veritabanı Postgres ise, diğer yazılımlardan önce Postgres kuyruğunu değerlendirebilirsiniz
  • “Sıkıcı teknoloji” sabit bir liste değildir; şu anda kullanılan teknolojiye göre değişir
    • Mesaj iletimi merkezli uygulamalarda RabbitMQ sıkıcı teknoloji olabilir
    • Önbellekleme merkezli uygulamalarda Redis sıkıcı teknoloji olabilir
    • İlişkisel verisi yoğun uygulamalarda Postgres sıkıcı teknoloji olabilir
  • Redis, Kafka, RabbitMQ veya SQS’yi yalnızca arka plan işleri için yeni devreye almak maliyeti artırır
    • Geliştirme, test ve production ortamlarına yeni bir sistem bağımlılığı eklenir
    • Gelecekte Developer, DBA ve SRE’lerin yeni sistemin hata modlarını ve yapılandırmasını bilmesi gerekir
    • Felaket kurtarma, sorun tanılama ve performans izleme bilgisi gerekir
    • Ekibin henüz bilmediği unknown unknowns risk olarak kalır

Postgres kuyruğunun bedelleri ve seçim ölçütleri

  • Sıkıcı teknoloji her derde deva değildir; Postgres de istisna değildir
  • Postgres kuyruğunu seçtiğinizde aşinalık, bilinen hata modları ve maliyeti dağıtma avantajları elde ederken şu bedellerle karşılaşabilirsiniz
    • Postgres kuyruğunda push/pop, Redis’e göre kayda değer ölçüde yavaştır
    • Tek bir ilişkisel veritabanı yerine uygulama veritabanı ve kuyruk veritabanı gerekebilir
    • Arka plan işlerini bağımsız ölçeklendirmek için ayrı bir veritabanı sunucusu gerekebilir
    • Daha sık VACUUM gerekebilir ve bu da performans yükü oluşturabilir
  • Postgres, Redis, Kafka, RabbitMQ veya SQS’den hiçbiri otomatik varsayılan olmamalıdır
  • Teknoloji seçimi süregiden bir ödünleşimdir; ekip ve uygulama gereksinimlerine göre değerlendirilmelidir
  • Emin olunmadığında “alternatifleri yalnızca sıkıcı teknolojinin gereksinimleri karşılamadığının kanıtlandığı durumda değerlendirmek” ölçütü uygulanabilir

Kuyruğu değiştirebilen arka plan iş yapısı

  • Arka plan iş işleme kodu belirli bir kuyruk teknolojisine bağımlı olmamalıdır
  • Uygulama büyüdükçe ihtiyaca göre memcached veya Redis gibi teknolojiler eklenebilir; zamanla bu teknolojiler de ekip için aşina olunan “sıkıcı teknoloji” haline gelebilir
  • Kuyruk teknolojisi soyutlanırsa kullanıcılar işe uygun kuyruğu seçebilir
  • GitHub’da en popüler arka plan iş kütüphaneleri arasında Hangfire dışında hiçbiri Redis dışı kuyruk teknolojisine çıkış yolu sunmaz
    • Bu yapıda kuyruğu değiştirmek için uygulama kodunu yeniden yazmak gerekir
  • Neoq, bu felsefeyle geliştirilmiş Go kütüphanesidir
    • Kuyruk backend’i olarak bellek içi, Postgres ve Redis kullanılabilir
    • Kullanıcılar uygulama kodunu değiştirmeden farklı bir kuyruk backend’i ile başlatabilir
    • Bellek içi ve Postgres uygulamaları birinci elden sunulur; Redis uygulaması asynq kullanır
  • Redis, Kafka, RabbitMQ ve SQS harika teknolojiler olabilir; ancak her zaman ilgili iş için doğru kuyruk veya uygun karmaşıklık düzeyi olmayabilir

1 yorum

 
GN⁺ 2023-09-25
Hacker News yorumları
  • Şimdiye kadar üç kez dağıtık iş sistemi kurdum ve yıllardır önerdiğim pratik kural şu: “mevcut ölçeğin 10 katına göre tasarla.”
    Saniyede 70 istekse 700 isteği, 20 batch iş sunucusu gerekiyorsa 200 sunucuyu baz almak gibi. Yılda %100 büyüyen bir startup bile 3 yıl sonra 8 kat ölçeğe ulaşır; yani büyürken yeniden yazacak zamanınız olur.
    İlk yaptığım sistemde “ölçeklenebilirlik” yüzünden SQL’den kaçınmıştım; ama işlem bütünlüğü gerektiren çok sayıda istisnai durumla karşılaşınca epey zorlandım.
    Sonraki iki dağıtık iş sisteminde PostgreSQL’i koordinatör olarak kullandım ve kelimenin tam anlamıyla SELECT FOR UPDATE SKIP LOCKED etrafında kurguladım. Biri normalde 350 worker’ı yönetiyor, diğeri ise binlerce işe karmaşık önceliklendirme uyguluyor.
    İkisi de yıllık geliri milyonlarca dolar seviyesine çıkana kadar sorunsuz dayanacaktır; örneğin 350 worker’lık olan, az bir düzenlemeyle yaklaşık 2.000 CPU’ya kadar ölçeklenebilir.
    Ultra büyük ölçek teknolojileri, transaction gerektiğini fark edene kadar oldukça ucuz görünür. Ama o andan itibaren nihai tutarlılığa sahip bir depolama katmanının üstünde transaction semantiğini taklit etmek mühendislik kâbusuna dönüşür.
    Bu yüzden oturup hesap yapmak gerekir. Şirket yıllık 100 milyon dolar kazanırsa dağıtık sistemin ne kadar büyümesi gerekeceğini, bu yükü kaldıracak bir PostgreSQL instance’ının kolayca bulunup bulunamayacağını ya da istemci bazlı sharding’in yeterli olup olmayacağını değerlendirin; mümkünse PostgreSQL’i ciddi şekilde düşünmeye değer. Yüz tane küçük şey kolaylaşır.

    • Benzer bir deneyimim var. SQL tabanlı kuyrukları, insanların “SQL burada çöker, dağıtık çözüm şart” dediği ölçeklerin iki basamaklı katları kadar daha zorladığım birkaç durum oldu.
      SQL çözümleri genelde daha basit, daha az hesaplama kaynağı tüketen ve operasyonel olarak desteklemesi daha kolay çözümler oluyor.
      Ancak çalıştırmak için, SELECT FOR UPDATE SKIP LOCKED gibi özelliklerin baştan var olduğunu bilecek kadar veritabanını iyi bilmek gerekiyor. Günümüzde birçok mühendis, DBMS’in gerçek gücünü ağır ORM’lerin perdelediği ortamlarda yetiştiği için bu tür bilgi epey nadirleşiyor.
    • Mevcut ölçeğin 10 katına göre tasarlayıp test edin, ama uygulamayı yalnızca şu an gereken kadar yapın.
      Sistem tepe yükü kaldırmak zorunda; tepe yükü bilmiyorsanız güvenlik payı bırakmalı ya da gerektiğinde işleri atmanın veya ertelemenin bir yolunu koymalısınız.
      Her şey bir ödünleşimdir; yalnızca optimize edilmesi gerekenleri optimize etmek gerekir ve neyin buna dahil olduğunu ayırt edebilmek iyi bir mühendisin özelliğidir.
    • “Nihai tutarlılığa sahip bir depolama katmanının üstünde transaction semantiğini taklit etmek mühendislik kâbusudur” cümlesi gerçekten önemli.
      Bu sizin için yeniyse, mesai saatinde bir hafta HN okumanın karşılığını az önce almış oldunuz; bunu bir kâğıda yazıp ATM kartınıza yapıştırmaya değer.
    • PostgreSQL’de advisory lock tabanlı kuyruklar da oluşturabilirsiniz; çok daha yavaştır ama avantajları da vardır.
  • Birçok projede daha basit bir yaklaşımı seçtim: tüm dillerdeki ORM/sorgu DSL framework’lerinde doğrudan çalışan sıradan tablolar ve SELECT FOR UPDATE SKIP LOCKED kullanmak.
    https://www.pgcasts.com/episodes/the-skip-locked-feature-in-...
    “Web ölçeği” değil ama benim deneyimime göre binlerce arka plan işine kadar kolayca ölçekleniyor.

    • Transaction mantığı olmadığı için kilitsiz, daha basit yaptığım bir durum da oldu. Bir satır seçip “alındı” alanını güncellemeyi deniyorduk; etkilenen satır sayısı 1 ise iş bendeydi, 0 ise biri önce almış demekti, o zaman yeni satır seçiyorduk.
      Bu yöntemi büyük bir kuruluşun işlerinde de sorunsuz kullandık. Özel bir dağıtım ya da yeni altyapı gerekmiyor; uygulamanın içinde birkaç worker thread başlatmak yeterli. Terk edilmiş işleri sıfırlayan bir thread de koyabilirsiniz.
      Ancak 3 yıl boyunca pratikte böyle bir durum yaşanmadı; her şey try/catch içindeydi, başarısız olursa kuyruğa geri koyuyorduk ve Java uygulaması da çok kararlıydı.
    • Yakın zamanda PostgreSQL’de tam olarak bu yöntem için bir manifesto ve kod parçaları yayımladım.
      delete from task
      where task_id in
      ( select task_id
      from task
      order by random() -- use tablesample for better performance
      for update
      skip locked
      limit 1
      )
      returning task_id, task_type, params::jsonb as params
      [1] https://taylor.town/pg-task
    • Bir kuyruk sisteminin düzgün ölçeklenmediğini en son öğrenmeniz gereken problemlerden biridir. Çünkü kuyruk sisteminin yapısal olarak ölçeklenemez olduğunu öğrendikten sonra bunu veri kaybı olmadan düzeltmek zordur.
      “Binlerce arka plan işi” dediniz ama kuyruklara genelde Little yasası açısından bakmak gerekir; saniyedeki ortalama iş giriş oranı ve ortalama iş süresi gibi oranlardan söz etmek gerekir. Ham sayıların tek başına pek anlamı yoktur.
      Başta saf bir UPDATE ... SET ile de olur, ama çok fazla kilit alır. UPDATE içinde SELECT alt sorgusu kullanarak ya da SELECT FOR UPDATE SKIP LOCKED ile çekme kilidini daha verimli hale getirmek mümkündür; ama sonunda çekme sorguları birbirlerinin kilitlerini engelleyip kuyruğu durdurabilir.
      O noktada DB’ye nefes aldırmak için iş girişini kapatabilirsiniz, ama kaybedilen girişler yüzünden veri kaybı oluşur ve çoğu zaman asıl sorun çekme işlemlerinin birbirini engellemesidir.
      İş tablosunu aceleyle shard’layıp kilitlerden kaçınabilirsiniz; ancak bunu birden çok worker’a dağıtmak kırılgandır ve veri kaybına yol açabilir. Rastgele işlerin bir kısmını atabilirsiniz ama bu da doğal olarak veri kaybıdır.
      Bu seçenekler üretimde aşırı streslidir ve baştan aşağı yeniden tasarlamadan toparlanması da çok zordur. Yalnızca birkaç müşteriniz varsa ve sonsuza kadar saniyede birkaç düzine iş seviyesinde kalacağınızdan eminseniz başka; ama bunu bizzat yaşadıktan sonra, mümkün olduğunda veritabanı yerine gerçek bir kuyruk teknolojisi seçiyorum.
    • Graphile’in https://github.com/graphile/worker aracı da kabaca bu şekilde uygulanmış.
  • “Mühendisler kendi ihtiyaçlarından tamamen farklı olmasına rağmen havalı görünmek için FAANG altyapısını kopyalıyor” tarzı eleştirileri sık görüyorum, ama bunun büyük ölçüde bilgi ve dokümantasyon meselesi olduğunu düşünüyorum.
    FAANG’i ya da başka startup’ları izleyip sonsuz ölçeklenebilir kuyruk tabanlı bir mimari kurmak istiyorsanız, Redis, SQS gibi ölçeklenebilir çözümleri birkaç saat içinde kurmanızı sağlayan düzinelerce kaliteli rehber, eğitim ve teknik doküman var.
    Bakım maliyeti daha yüksek, ama itibarlı kaynaklardaki komutları, kodları ve ayarları kopyalayıp yapıştırarak başlayabiliyorsunuz.
    Buna karşılık PostgreSQL’in NOTIFY özelliğini kullanmak isteyip “SQLALchemy notify listen postgres” diye aradığınızda, birkaç yanıtsız Stack Overflow sorusu ve bağlamdan yoksun GitHub Gist’lerinden başka pek bir şey çıkmıyor.
    Bir yan projede bu yöntemi gerçekten denemek istiyorum, ama tek başıma 2-3 gün ayırıp çözmeye vaktim yok.
    Dışarıdan bakınca seçim “basit ama ölçeklenmez, yani yalnızca PostgreSQL kullanmak” ile “karmaşık ama ölçeklenir, yani Redis/SQS vb.” arasında gibi görünüyor; böyle olunca da sanki parlak teknolojilere ve FAANG’e kapılıp ihtiyacım olmayan ölçeklenebilirliği seçmişim gibi oluyor.
    Ama rehberleri ve kaynakları da hesaba katınca gerçek seçenekler daha çok “karmaşık ve ölçeklenebilirliği de öngörülemez, çünkü uygulama yöntemini ve tuzakları bilmiyorum” ile “basit ve ölçeklenebilir, herkesin pratikte yaptığı yol” arasında oluyor. Bu durumda FAANG’i izleyen mühendisin tercihi çok daha makul görünüyor.

    • PostgreSQL gibi “sıkıcı” teknolojilerin avantajı dokümantasyonlarının harika olmasıdır. NOTIFY kullanmak istiyorsanız sadece https://www.postgresql.org/docs/current/sql-notify.html adresini okumanız yeterli; arama becerisine gerek yok.
    • Ölçeklenebilirliğin bir bedeli vardır. İhtiyacınız yokken eklerseniz esneklik azalır; startup’lar için istenmeyen durum da tam olarak budur.
      Örneğin düşük yük altında PostgreSQL kullanırsanız şema migration’ları, yeni kısıtlar ekleme, analiz vb. neredeyse önemsiz işler hâline gelir.
      SQS, Cassandra vb. kullanırsanız ölçeklenebilirlik/erişilebilirlik kazanırsınız, ama asıl tasarımın uymadığını fark ettiğinizde değiştirmek çok daha uzun sürer. İş tarafı “foo tipindeki kullanıcılar bar değerlerini aynı anda birleştiremesin” gibi bir kısıt eklenmesini istediğinde bu böyledir.
      PostgreSQL olmadan da uygulanabilir, ama özellikle değişiklik gerekiyorsa kolay ya da basit değildir.
      Bu yüzden esnekliği korumak için PostgreSQL kullanmak, değişmeyeceğini bildiğinizde ise PostgreSQL’in üstüne başka bir şey eklemek daha iyi görünüyor. Elbette bu durumda altyapı ve bakım ek yükü de eklenir.
      Sonuçta bu her zaman bir ödünleşimdir; neyi, ne zaman, neyle takas edeceğinizi bilmeniz gerekir.
    • Bu yazı uygulama geliştiricilerinden çok kütüphane geliştiricilerini hedefliyor.
      Herkesin kendi uygulaması içinde PostgreSQL backend’i ya da başka bir kuyrukla özel bir arka plan iş worker’ı yazmasının ideal olduğunu düşünmüyorum.
      Arka plan işlerinde yanılması kolay ince noktalar ve uygulama ayrıntıları çoktur; genellikle bunları daha kapsamlı, bu işe özel bir kütüphane ya da framework’ün üstlenmesi daha iyidir.
      Her Rails uygulamasında Sidekiq/Active Jobs olmasaydı ve herkes kendi özel arka plan worker’ını yazsaydı, Rails uygulamalarının güvenilirlik açısından bugünkünden çok daha kötü bir itibarı olması muhtemeldi.
    • “Bakımı daha yüksek” kısmı küçük startup’ları öldürür. Elbette karşılığında çok şey de kazanılır, ama şirket batarsa bunların hiçbir anlamı kalmaz.
      Ana hedeften çok uzaklaşmadan en büyük faydayı sağlayan çözümü seçmek gerekir.
      Geliştiricilerinin zamanının yaklaşık %80’ini araçlar ve altyapıyla boğuşarak geçirdiği bir startup görmüştüm. Runway’i 3 aydı; şimdi o pistin sonunda sadece büyük bir çukur kaldı. Düşününce hâlâ ürperiyorum.
    • FAANG seviyesinde tasarıma gitmenin zaman kazandırdığı argümanında temel dayanak, çevrimiçi rehberler bulunabilmesinden ibaret.
      Karmaşıklığa yeterince saygı gösterilmiyor gibi geliyor.
  • PostgreSQL’i bir yayınla/abone ol veri yolu olarak LISTEN/NOTIFY ile kullanırken en büyük dezavantaj, LISTENin oturum düzeyinde bir özellik olması ve bu yüzden ifade düzeyinde connection pooling ile uyumsuz olmasıdır.
    Bu yöntemi kullanacaksanız advisory lock kullanmak iyi olur. Diğer açık kilitler veritabanına daha fazla yük bindirir, ama advisory lock’lar özellikle çok hafif olacak şekilde tasarlanmıştır.
    Sevdiğim uygulama örneği, birçok dile port edilmiş olan que’dur.

    • NOTIFY/LISTENi sevmeme nedenlerimden biri, sorun çıktığında tanı koymanın zor olmasıdır.
      Yakın zamanda da bir süre geçtikten sonra tüm NOTIFY/LISTENlerin durduğu ve ancak veritabanını yeniden başlatınca çözülen bir sorun yüzünden kullanmayı bırakmak zorunda kaldım https://dba.stackexchange.com/questions/325104/error-could-n...
    • Que’nun Node sürümü olup olmadığını merak ediyorum
  • Skype yıllar önce tüm CDR’leri işlemek için küçük bir eklentiyle birlikte PostgreSQL’i kuyruk olarak kullanmıştı. Hâlâ kullanıyorlar mı bilmiyorum ama 10 yıl önceki ölçütlere göre “web ölçeği” idi ve internette veritabanını kuyruk olarak kullanmanın anti-pattern olup olmadığı tartışılırken gayet sorunsuz çalışıyordu
    Transaction olması epey kullanışlı
    https://wiki.postgresql.org/wiki/SkyTools
    İşte oldukça fazla kullandığım için Sydpy’de bu konuda birkaç kez sunum da yaptım. PostgreSQL’i zaten iyi işletiyor ve destekliyorsanız kullanışlı
    Ama bugün olsa özel bir kuyruk kullanırdım; RabbitMQ’dan ise özellikle kaçınırdım

    • “RabbitMQ’dan özellikle kaçınırdım” kısmını biraz daha açıklarsan iyi olur. Başkalarının deneyimlerini ve düşüncelerini de merak ediyorum
    • Bugün özel bir kuyruk kullanma fikrine katılıyorum. Başlıca sebep, bulut ortamındaysanız çoğu bulut sağlayıcısının kullanımı çok kolay ve ucuz barındırılan kuyruk teknolojisi sunması
      Vendor lock-in’den endişe etseniz bile kuyrukların temel işlemleri fiilen push/pop düzeyinde küçük olduğu için, gerekirse taşınması kolay olacak şekilde yazmak da nispeten kolay
    • RabbitMQ ile kötü bir deneyimin olup olmadığını merak ediyorum. Varsa bunun teknolojinin kendisinden mi, yoksa dokümantasyon veya topluluk gibi çevresel unsurlardan mı kaynaklandığını da merak ediyorum
    • Veritabanını kuyruk olarak kullanma yaklaşımı iyi çalıştığında harika, ama bozulma biçimi sizi toparlanması çok zor bir duruma sürüklüyor
      Zaten veritabanını işletiyorsunuz diye kuyruğu da veritabanına koyduysanız, bu mantık iki ucu keskin bir kılıç. Kutlanacak bir şey değil ama kuyruk karmaşası artık ana veri deposunu da aşağı çekebilir
  • PostgreSQL’i uygulama kuyruğu olarak kullanırken en büyük avantajlardan biri, zamanlanan tüm asenkron işlerin transactional olmanın avantajından yararlanması
    Örneğin kayıt sonrası e-posta gönderen karmaşık bir backend değişikliği var; e-posta gönderme işini ekledikten sonra aynı transaction’ın daha sonraki bir kısmında hata olup rollback olursa, o e-posta işi en baştan kuyruğa hiç girmemiş olur

    • Başka non-idempotent sistemlerle bağlanırken kaçınılmaz olarak en az bir kez veya en fazla bir kez semantiklerinden birini seçmek gerektiğini netleştirmek lazım
      E-posta için gönderim onayını beklerken hata olursa, transaction’ı başarısız kılıp e-postanın yinelenmesi ihtimalini mi göze alacaksınız, yoksa devam edip e-postanın kaybolması ihtimalini mi; bunu seçmeniz gerekir
      Büyük avantaj, DB’yi asenkron olarak değiştiren kod yollarından geliyor. İşin tüketilmesi ve DB güncellemesi aynı transaction içinde olduğundan exactly-once semantiğini tamamen transaction ile işleyebilirsiniz
    • Bu gerçekten önemli bir nokta. SQS, iş işleme kümesinin otomatik ölçeklenmesini kolaylaştırdığı için PostgreSQL ve SQS’i birlikte sık kullanıyorum
      PostgreSQL’de tetiklenen olayı ve ilgili transaction’ın pg_current_xact_id() değerini içeren bir transaction log tablosu tutuyorum. Satırın yerleşik xmin değeri de kullanılabilir ama transaction wraparound’u düşünmek gerekir. Bu satıra insert yapıldığında NOTIFY oluşur
      Bir arka plan süreci tekrar tekrar çalışır; önceki çalıştırmanın xmin değeri ile mevcut pg_snapshot_xmin(pg_current_snapshot()) arasındaki transaction ID’lerine sahip tüm satırları transaction tablosundan seçer. Bu olayları işlere eşleyip SQS’e gönderir, xmin değerini kaydeder ve sonra bir sonraki NOTIFY için LISTEN ile bekler
    • Biz de kuyruğa eklemeyi commit sonrasına çalışacak şekilde zamanlayarak bu sorunu biraz hafifletiyoruz. Yine de güvenli olmayan kısımlar kalıyor ve RabbitMQ bağlantısı düşerse sıkıntı oluyor
    • Veritabanına bir şeyin işlendiğini bildirmek ile RabbitMQ gibi bir yere mesaj göndermek %100 transactional olamaz. Bu yöntemin en büyük nedeni tam da bu
      PostgreSQL kuyruğunda e-posta gönderme işini ekledikten sonra transaction daha sonra başarısız olursa e-postanın kuyruğa girmediği doğru
      Tamamen ayrı olması gereken veritabanı kodunu farklı transaction’lara ayırmaya değer. Tersine, kullanıcı DB’de oluşturulmadıysa kayıt e-postasını da göndermek istemeyebilirsiniz; duruma bağlı
    • Bir başka avantaj da iş alınmadan önce transaction’ın tamamlanmasının garanti edilmesi
      Redis tabanlı kuyruklarda ya da aslında başka hemen her kuyrukta, transaction commit edilmeden önce veritabanı kaydının var olduğunu varsayan bir işin çalıştığı durumla kısa sürede karşılaşırsınız. Bunu düzelten kod genellikle garip ve karmaşık olur
  • Kafka’da sevdiğim şey, onun sadece append-only log olması ve istemcilerin özünde yalnızca offset tutması
    Kavramsal olarak anlaması çok basit; kalıcı ve arızalara karşı da oldukça dayanıklı. Çünkü herhangi bir offset’e dönüp yeniden okuyabilirsiniz
    Ne yazık ki Kafka, dağıtık yapısı nedeniyle yeterince karmaşıklık getiriyor ve çoğu kullanım senaryosunda sonunda buna değmez hâle geliyor
    Kişisel olarak işletmesi daha kolay benzer bir şey olmasını isterdim. Tek bir node’da saniyede yüzlerce, hatta belki binlerce event işleyebilirdi; dağıtık karmaşıklık olmasa epey iyi olurdu
    Teorik olarak PostgreSQL ile de yapılabilir; satırları hiç silmezsiniz. Belki cevap bu olabilir

    • PostgreSQL’de native “offset” karşılığı olan auto-increment ID var ve tarihe göre partitioning de mümkün; bu yüzden basit bir Kafka alternatifi için çok iyi bir aday olduğunu düşünüyorum
      Consumer group, partition gibi tüm özelliklere ihtiyacınız yoksa consumer yapılandırması da çok daha basit olur
    • FasterLog’un bir gün yeterince olgunlaşmasını umardım
  • Windmill’de tam olarak bu yaklaşım kullanılıyor. Windmill açık kaynaklı bir Retool alternatifi ve modern Airflow’a yakın bir araç; her gün benchmark çalıştırıyor
    Makul bir GitHub CI instance’ında bir Windmill worker’ı ve PostgreSQL’i container olarak çalıştırınca benchmark saniyede 1.200 iş işliyor. Worker ekleyince saniyede 5.000 işe kadar stabil şekilde ölçekleniyor
    Multi-tenant instance’ta saniyede 5.000 iş bariyerini aşmak için Citus kullanmayı değerlendiriyoruz
    https://github.com/windmill-labs/windmill/tree/benchmarks

  • Saniyede yaklaşık 10 mesaj düzeyinde olduğumuz zamanlarda bazı kuyruklar için PostgreSQL kullandık ve fena sayılmayacak şekilde ölçeklendi. Ama açıkçası AWS, GCP, Azure üzerinde SQS ya da başka bir kuyruk yığını kurmak çok kolay; bunlar zaten bu amaç için yapılmış ve DLQ gibi şeyler de yerleşik geliyor
    Sırf bu sistem çöker de veritabanının geri kalan durumunu da etkiler mi diye kaygılanarak neden bu yolu seçeyim, bilmiyorum
    “En basit aracı kullan” ilkesini seviyorum ama bazen mühendisler işi fazla ileri götürüp, ana akım alternatifler görece ucuz ve basitken bile, geriye yalnızca belirsiz uyarıları olan en aptal aracı bırakıyor gibi geliyor

    • Benim benimsediğim yaklaşım, iş durumlarının çoğunu veritabanında saklamak ve iş kuyruğunu yalnızca worker’ları uyandırıp ilgili işi işlettirmek için kullanmak
      İş durumunu veritabanında saklayınca durumu düzgünce sorgulayabiliyorsunuz. Gerçek durumu birebir göstermese bile, üretimdeki arızaları analiz ederken yardımcı oluyor. Özellikle çoğu iş kuyruğu işlenen kayıtları doğrudan silip atıyor
      Ayrıca tüm arka plan işlerini idempotent hâle getirirseniz, “bu işi işlemesi için iş kuyruğuna mesaj gönderme” gibi bir yaklaşım neredeyse her zaman güvenli oluyor
      Yalnızca mesaj kuyruğuna bel bağlarsanız, performans sorunu çıktığında ne olup bittiğini anlamak çoğu zaman zor. Örneğin RabbitMQ kuyruk boyutunu söyleyebilir ama iç verileri inceleme işlevleri yok denecek kadar azdır ya da çok sınırlıdır
    • Nedeni transaction’lar ve veri tutarlılığı. SQS ile elde edemeyeceğiniz cevap bu
    • Çünkü her yazılımın gereksinimleri farklıdır. On-premises/B2B appliance’larda PostgreSQL dışında bir servis gerektirmemek başlı başına bir özellik olabilir
      Bazı yazılımların PostgreSQL’in kapasitesini asla aşmayacağı öngörülebilir; aşsa bile başka bir servise taşınması kolay olacak şekilde tasarlanabilir
      Ayrıca transaction’lı bir iş sistemini PostgreSQL içinde basitçe kurmak isteyebilirsiniz
  • Biz Elixir’in Oban’ını kullanarak PostgreSQL içinde her gün yüz binlerce ila milyonlarca işi işliyoruz
    Transaction başarılı olduğunda e-postayı planlamak gibi, arka plan işleri etrafında transaction semantiğinin olması inanılmaz derecede kullanışlı
    autovacuum için biraz ayar yapmak gerekiyor ama toparladıktan sonra bizim için çok iyi çalıştı