Olay Kaynağı (ES), Komut Sorgu Sorumluluk Ayrımı (CQRS) ile birleştiğinde, birçok yüksek ölçeklenebilirlik sistem mimarisinin omurgası haline geldi. Okuma tarafının faydaları iyi belgelenmiş olsa da, yazma tarafı genellikle önemli bir darboğaz sunar: olayların depolama arka ucuna sıralı olarak yazılması. Milyonlarca komut aynı anda geldiğinde, basit uygulamalar işlem çakışması, kilit yükseltme ve I/O doygunluğu gibi sorunlarla karşılaşabilir. Bu yazıda, toplu işleme ve salt ekleme mekaniklerine odaklanarak bu yazma yollarını optimize etmek için ileri düzey stratejileri inceliyoruz.
Yazma Çakışmasının Zorluğu
Standart bir Olay Kaynağı uygulamasında, her komut kalıcı hale getirilmesi gereken bir veya daha fazla alan olayına neden olur. Her olay ayrı bir veritabanı işlemi tetiklerse, ağ dönüş gecikmesi ve işlem günlüğü tutma maliyetleri geçiş kapasitesini ciddi şekilde düşürebilir. Ayrıca, iyimser eşzamanlılık denetimi (OCC), her yazma işleminde sürümleri karşılaştırmayı gerektirir. Birden fazla komut aynı anda aynı topluluk nesnesine (aggregate) yönelikse, doğrulama başarısız olabilir ve yeniden denenmeleri gerekir; bu da kalabalık sürü sorunlarına yol açabilir.
Yüksek geçiş kapasitesine ulaşmak için satır satır eklemeden toplu, salt ekleme işlemlerine geçiş yapmalıyız. Bu yaklaşım, kilitlenme maliyetlerini en aza indirir ve I/O verimliliğini maksimize eder.
Yüksek Geçiş Kapasiteli Toplu İşleme Stratejileri
Olayları toplu hale getirmek, veritabanına yapılan dönüş gecikmelerinin sayısını azaltır. Olayları tek tek commit etmek yerine, uygulama olayları yerel bir arabellekte toplar ve toplu olarak boşaltır (flush). Ancak basit toplu işleme gecikmeye neden olabilir. Anahtar, toplu iş boyutunu gerçek zamanlı gereksinimlerle dengelemektir.
Yaygın bir desen, bellek içi bir kuyruğu izleyen bir arka plan işçisini içerir. Kuyruk belirli bir boyuta veya zaman eşiğine ulaştığında, işçi toplu işi tek bir atomik birim olarak commit eder. Bu, toplu işteki tüm olayların kalıcı hale getirildiğinden veya hiçbiri kalıcı hale getirilmediğinden emin olur; böylece performansdan ödün vermeden veri tutarlılığı sağlanır.
// Yüksek geçiş kapasiteli bir olay sunucusu için sahte kod
class EventBatcher:
def __init__(self, batch_size=100, flush_interval_ms=50):
self.buffer = []
self.batch_size = batch_size
self.flush_interval = flush_interval_ms
def append(self, event):
self.buffer.append(event)
if len(self.buffer) >= self.batch_size:
self.flush()
def flush(self):
if not self.buffer:
return
# Tüm toplu iş için tek bir veritabanı işlemi başlat
try:
with db.transaction():
for event in self.buffer:
db.insert("events", event)
db.commit()
except IntegrityError:
# Eşzamanlılık çakışmalarını burada işleyin
retry_buffer(self.buffer)
finally:
self.buffer = []
Bu strateji, veritabanının yalnızca onlarca veya yüzlerce olay için tek bir commit kaydı yazması gerektiğinden işlem günlüğü maliyetini önemli ölçüde azaltır. Ancak geliştiriciler dikkatli olmalıdır: toplu işleme, bir hatanın yayılma çapını artırır. Bir toplu iş başarısız olursa, o toplu işteki tüm olaylar geri alınmalıdır; bu da karmaşık yeniden deneme mantığı gerektirebilir.
Salt Ekleme Depolama Mekanikleri
Olay Kaynağı doğası gereği salt eklemedir. Mevcut satırları güncelleyen geleneksel ilişkisel veritabanlarının aksine (bu sayfa bölünmelerine ve parçalanmaya neden olabilir), yeni kayıtların eklenmesi çoğu depolama ortamında rastgele yazmalardan çok daha hızlı olan sıralı I/O'ya olanak tanır.
Bundan yararlanmak için, Apache Kafka, Amazon Kinesis veya EventStoreDB gibi özel olay depoları gibi sıralı yazımlar için tasarlanmış salt ekleme günlük yapılarını veya veritabanlarını kullanmayı düşünün. Bu sistemler, yazma yolunu okuma yolundan tamamen ayırarak yüksek yazma geçiş kapasitesi için optimize edilmiştir.
Salt ekleme günlükleri uygularken, veritabanı motorunuzun verimli bölütlendirme (partitioning) desteğine sahip olduğundan emin olun. Bölütlendirmeyi topluluk nesnesi ID'sine veya kiracı ID'sine göre yapmak, ilgili olaylara yapılan yazımların yerel kalmasını sağlar ve kilit çakışmasını daha da azaltır. Örneğin, tek bir Sipariş topluluk nesnesine ait tüm olaylar, sıralı tutarlılığı garanti etmek için aynı bölüme yazılmalıdır.
Toplu Yazımlarla İyimser Eşzamanlılık
OCC'yi toplu bir ortamda uygulamak, dikkatli bir sürüm yönetimi gerektirir. Her tek olaydan önce sürümü kontrol etmek yerine, toplu işin başında sürümü kontrol edebilirsiniz. Komut alındığından beri sürüm değişmişse, tüm toplu iş geçersiz kılınır ve yeniden denenir.
Bu yaklaşım, toplu işin yazılması için geçen süre içinde topluluk nesnesi durumunun büyük ölçüde değişmeyeceğini varsayar. Bu bir ödünleşim olsa da, çok daha yüksek eşzamanlılığa olanak tanır. Katı doğrusal tutarlılık gerektiren sistemler için ek kilit mekanizmaları veya sıralı numaralar gerekli olabilir, ancak çoğu iş alanı için toplu OCC tarafından sağlanan nihai tutarlılık kabul edilebilirdir.
Sonuç
Olay Kaynağı sisteminde yazma yolunu optimize etmek, CQRS'nin vaat ettiği ölçeklemeye ulaşmak için kritiktir. Yüksek geçiş kapasiteli toplu işlemeyi salt ekleme depolama stratejileriyle birleştirerek, geliştiriciler gecikmeyi önemli ölçüde azaltabilir ve sistem geçiş kapasitesini artırabilir. Anahtar, toplu iş boyutlarını dengelemek, eşzamanlılık çakışmalarını zarifçe yönetmek ve sıralı yazımlarda mükemmel performans gösteren depolama motorlarını seçmekten geçer. Sisteminiz büyüdükçe, bu optimizasyonlar, hantal bir uygulama ile ağır yük altında sorunsuz şekilde ölçeklenen bir uygulama arasındaki farkı oluşturacaktır.