Modern dağıtılmış yazılım mimarisinde, "Aktif-Aktif" veritabanı dağıtım hayali genellikle gecikmeyi en aza indirmek ve küresel bölgeler arasında yüksek kullanılabilirlik sağlamak için peşinden koşulan bir hedeftir. Ancak, coğrafi olarak ayrılmış PostgreSQL örnekleri arasında çift yönlü yazmalara izin verdiğiniz anda, dağıtılmış işlemlerin karmaşık dünyasına adım atmış olursunuz. Bu paradigmadaki en önemli engel, kaçınılmaz yazma çakışmasıdır; farklı bölgelerdeki aynı veriye eşzamanlı yapılan değişiklikler çarpışır ve doğru şekilde yönetilmezse veri kaybına veya bozulmaya yol açabilir.
Bu rehber, teorik sınırlamaların ötesine geçerek, PostgreSQL'de bu yazma çakışmalarını tespit etme ve çözme için pratik stratejileri ve üretim ortamları için uygulanabilir mühendislik çözümlerini incelemektedir.
Yazma Çakışmasının Anatomisi
Geleneksel tek bölgeli veritabanlarının aksine, aktif-aktif bir kurulum, işlem sıralaması için tek bir gerçeklik kaynağına sahip değildir. Farklı bölgelerdeki (örneğin New York ve Singapur) iki kullanıcının aynı kaydı eşzamanlı güncellemeye çalıştığında, sistemin hangi değişikliğin kazanacağını belirlemesi gerekir. Standart PostgreSQL'de, aktif-aktif çoğaltma genellikle tasarım gereği bozan sıkı küresel serilebilirlik modeline dayandığından, bunu yerel olarak yapmak imkansızdır.
Güçlü bir çakışma çözümü mekanizması olmadan, bir işlemin sessizce diğerini üzerine yazdığı "kayıp güncellemeler" veya küme genelinde işlem işlemini durduran "ölümcül döngüler" (deadlocks) riskiyle karşı karşıya kalırsınız.
Strateji 1: Uygulama Seviyesinde Çakışma Algılama
En şeffaf yaklaşım, çakışma çözümü mantığını uygulama katmanına taşımaktır. Bu, her satırda sürümleme meta verilerini korumayı gerektirir. Tablolarınıza bir last_modified_version veya last_updated_at sütunu ekleyerek, uygulamanızın işlemleri taahhüt etmeden önce değişiklikleri doğrulamasını sağlayabilirsiniz.
Burada, bir işlemde iyimser kilitleme (optimistic locking) uygulamanın pratik bir örneği bulunmaktadır:
BEGIN;
-- Sürüm değişmemişse kaydı güncellemeyi dene
UPDATE inventory
SET quantity = quantity - 1,
last_modified_version = last_modified_version + 1
WHERE id = 123 AND last_modified_version = 5;
-- Etkilenen satır sayısını kontrol et
-- Eğer 0 satır etkilendiyse, başka bir bölgede bir çakışma oluştu
IF (ROW_COUNT() = 0) THEN
RAISE EXCEPTION 'Eşzamanlı değişiklik algılandı. Lütfen tekrar deneyin.';
END IF;
COMMIT;
Bu senaryoda, uygulama istisnayı yakalar, en güncel verilerle işlemi yeniden dener veya kullanıcıyı bilgilendirir. Bu strateji etkili olsa da, sıkı istemci tarafı işleme gerektirir ve yüksek rekabet altında artan yeniden deneme trafiğine yol açabilir.
Strateji 2: Çakışma Çözümleme İşlevlerinden Yararlanma
Daha otomatik bir yaklaşıma ihtiyacınız olan senaryolar için, belirli çoğaltma araçları veya özel tetikleyiciler müdahale edebilir. Standart mantıksal çoğaltma çakışmaları işleme almasa da, pg_replication_slot gibi uzantılar ve özel çakışma çözümü tetikleyicileriyle belirli bölgelere öncelik verme veya zaman damgalarına dayalı "Son Yazan Kazanır" (Last Write Wins) mantığı uygulama gibi mühendislik çözümleri geliştirilebilir.
Bununla birlikte, dikkatli saat senkronizasyonu olmadan yalnızca zaman damgasına dayalı "Son Yazan Kazanır" yaklaşımı tehlikelidir. Zaman damgasını bir tamponla karşılaştıran bir tetikleyiciyi düşünün:
CREATE OR REPLACE FUNCTION resolve_conflict() RETURNS trigger AS $$
BEGIN
-- Gelen satır daha yeniyse onu tutun; aksi takdirde yerel satırı tutun
-- Bu, açıklama amaçlı basitleştirilmiş bir mantıktır
IF (TG_OP = 'INSERT' AND NEW.timestamp > (SELECT timestamp FROM conflicts_table WHERE id = NEW.id)) THEN
RETURN NEW;
ELSE
RETURN OLD;
END IF;
END;
$$ LANGUAGE plpgsql;
Önemli bir not olarak, PostgreSQL, küme içinde çakışma çözümü için yerel olarak çift yönlü tetikleyicileri önemli bir yük olmadan desteklemez. pg_partman gibi araçlar veya Citus veya async-replication gibi özel ara katman yazılımları (middleware), bu özel mantık akışları için genellikle daha iyi çerçeveler sunar.
Strateji 3: Çakışma Önleme İçin Şema Tasarımı
En etkili çakışma çözümü stratejisi, genellikle çakışmaların hiç oluşmasını önleyen bir stratejidir. Bu, "Kullanıcı Kimliğine Göre Şarılama" (Sharding) veya "Bölge Bazlı Veri Bölütleme" yoluyla sağlanır.
Verilerinizi mantıksal olarak ayırabilirseniz, örneğin ABD'deki bir kullanıcının AB'deki bir kullanıcı tarafından oluşturulan veriyi asla değiştirmemesi sağlanırsa, çakışmalar ortadan kalkar. Örneğin, bir kullanıcı yönetimi sisteminde, region_code = 'US' olan tüm verilerin fiziksel olarak yalnızca ABD kümesine saklanıp çoğaltılmasını ve AB kümesinin yalnızca region_code = 'EU' verilerini işleyecek şekilde zorlayabilirsiniz. Bu mimari kısıtlama, yazma çakışmaları için yüzey alanını önemli ölçüde azaltır.
Sonuç
Çok bölgeli aktif-aktif bir PostgreSQL kümesi oluşturmak, yüksek getirili ancak yüksek riskli bir girişimdir. Sihirli bir değnek yoktur; en iyi çözüm tamamen spesifik tutarlılık gereksinimlerinize bağlıdır. Güçlü tutarlılık için yalnızca okuma kopyalarını veya lider/uydu (leader/follower) kurulumlarını düşünün. Kullanılabilirlik ve düşük gecikme için, nihai tutarlılığı benimseyin ve uygulama seviyesinde iyimser kilitleme veya şema tasarımı yoluyla güçlü çakışma çözümü mekanizmaları uygulayın.
Sisteminizi tasarlarken, her zaman ham kullanılabilirlikten veri bütünlüğünü önceliklendirin. Başarısız bir yazma, bozulmuş bir veritabanından daha iyidir. Yazma çakışmalarının mekaniklerini anlayarak ve bu pratik stratejileri uygulayarak, ölçeklenebilir şekilde güvenilir şekilde çalışan dayanıklı küresel veritabanları oluşturabilirsiniz.