Heroku gibi bir cloud servisi aracılığıyla aynı uygulamadan bir küme oluşturduğunuzda zamanlanmış işlerin senkronizasyonu başınızı ağrıtabilir.
Zamanlanmış işler günümüz web uygulamalarının ayrılmaz bir parçası. Bu işler aracılığıyla yeri geliyor istatistiki bilgiler oluşturuyor, yeri geliyor operational data store güncelliyoruz. Şu an benim aklıma gelen kullanım amaçları bunlar olsa da soracağınız her geliştiriciden farklı yanıtlar alacağınıza eminim.
Zamanlanmış işleri gerçekleştiren ayrı bir uygulama kullanıldığında elbette senkronizasyon sorunu ortaya çıkmayacaktır. Fakat sırf bu amaçla ayrı bir uygulama geliştirmenin ek bir maliyet getireceği de açıktır. Play Framework gibi web uygulama çatıları zamanlanmış işleri çocuk oyuncağı haline getirdiğinden bu işleri gerçekleyen kodun web uygulaması içinde yer alması oldukça avantajlı görünmektedir.
Herokuda uygulamanızı birden fazla dyno üzerinde çalıştırabilirsiniz. Böylece yükü birden fazla process ile dengelemiş olursunuz. Zamanlanmış işler açısından bakıldığında aynı iş kodu tüm uygulamalar üzerinde olduğundan aynı iş birden fazla kez çalışacaktır. Bu durum ise büyük ihtimalle istenmeyen sonuçlara sebebiyet verecektir.
Zamanlanmış iş kodu tüm uygulamalarda olduğu halde aynı anda yalnızca birinin çalışmasını sağlayabilirsek sorunumuzu çözmüş olacağız. Sorunun çözümü için ortak bir lock olarak kullanabileceğimiz bir araç bulmamız gerekiyor. Heroku bu sorun için standart bir çözüm tanımlamıyor. Bu sebeple elimizdeki olanaklara bir göz atalım.
Heroku eklentileri arasında oldukça başarılı Memcached eklentileri yer alıyor. Memcached sunucusu uygulamalarımız dışında bir yerde çalıştığından ve tüm uygulamalar aynı Memcached sunucusuna eriştiğinden senkronizasyon amacıyla kullanılması mümkün görünüyor.
Tüm uygulamalarımız aynı PostgreSQL veritabanına eriştiğinden veritabanı da senkronizasyon amacıyla kullanılabilir gibi duruyor.
Memcached içinde yer alan boolean add(key, value)
metodu tam istediğimiz işi yapıyor. Bu metod ancak bu anahtara karşılık bir değer yoksa değeri setliyor. Setleme başarılı ise true, değilse false dönüyor. Memcached'in bu özelliğini kullanarak senkronize olan bir Play Job kodu aşağıda yer alıyor. Bu iş parçası her saniyede bir çalışıyor.
Play Framework Memcached sunucusuna da Cache arayüzü üzerinden erişir. Bunun için yalnızca application.conf dosyasında Memcached ayarlarının yapılması yeterlidir.
# Memcached configuration # ~~~~~ # Enable memcached if needed. Otherwise a local cache is used. memcached=enabled # # Specify memcached host (default to 127.0.0.1:11211) memcached.host=127.0.0.1:11211
Aynı uygulamadan faklı portlarda 2 adet çalıştırdığımda oluşan ekran görüntüsü aşağıda yer alıyor.
Göreceğiniz üzere bir uygulamadaki iş çalışırken diğeri lock alamıyor ve çıkıyor. Aynı uygulamadan 4 adet çalışırsa durum ne olur diye merak edenler için yanıt aşağıda yer alıyor.
Görünüşe göre Memcached çözümü oldukça başarılı. Ama gerçekten öyle mi? Memcached son tahlilde bir önbellek sunucusudur ve hiçbir uygulama önbelleğe yazılan verinin bir sonraki okumada orada olacağına güvenmemelidir. O halde kritik uygulamalar için Memcached çözümü gerçek bir çözüm değildir. Fakat düşük olasılıkla çıkacak sorunları kaldırabilecek durumda iseniz Memcached ile yola devam edebilirsiniz.
Veritabanı ile senkronizasyon için JPA tarafından sağlanan Optimistic Locking yönteminden faydalanacağız. Bir tabloda id'si 1 olan bir kayıt tutalım. Böylece aynı tabloda farklı id'lere sahip farklı locklar da saklayabiliriz. Lock'ı alan Job bu kaydın tarih alanını o anki zamana setleyecek, lockı bırakırken ise bu alanı null yapacak. Kaydı tarih alanı null iken okuduğumuz halde kayıt üzerinde değişiklik olmuşsa(yani biz tarih alanını setleyene kadar başka bir Job bu alanı setlemişse) OptimisticLockException alacağız.
JPA modelini aşağıdaki şekilde oluşturdum.
Buna göre Job kodu ise aşağıdaki gibi oldu.
Aynı uygulamadan yine 4 kez çalıştırdığımızda aynı anda yalnızca birinin lockı alabildiğini aşağıdaki ekran görüntüsünden anlayabiliyoruz.
Veritabanı yöntemi Memcached yöntemine göre çok daha kararlı çünkü lock kaydının her zaman ilgili tabloda olacağına güvenebiliyoruz. Fakat aynı durum Memcached için ne yazık ki geçerli değil. Diğer yandan veritabanı yönteminde lock contention Memcached yöntemine göre çok daha fazla. Artılarını ve eksilerini değerlendirerek iki yöntemden birini seçmek gerekiyor.
Varsa önerdiğim yöntemlerdeki hataları ya da farklı çözüm önerilerini yorum olarak bekliyorum.
Follow me on Twitter