26 Haziran 2023 Pazartesi

Static Metodlu Sınıf mı Yoksa Singleton mı ?

Nesne-merkezli diller bize nesne-merkezli kod yazmak için imkanlar sunmaktadır ama örneğin en azından nelerin nesne olacağı ve nesnelerin  aralarındaki ilişkilerin nasıl olacağı gibi kararlar tamamen biz geliştiricilerin/tasarımcıların sorumluluğundadır. Bu sorumluluğu yerine getirmede sıkıntılar yaşadığımız müddetçe kullandığımız programlama dilinin bize sağlıklı bir nesne-merkezli yapı oluşturmak için yardım etmesi mümkün değildir.   

Bir sınıf oluşturup, içine üç-beş tane private nesne değişkeni (instance variables) tanımlayıp, sonra da bu sınıfa set/get metotlarını koymak, kesinlikle kodunuzu nesne-merkezli yapmaz. Nesne-görünümlü yapabilir ama nesne-merkezli programlamanın, bundan daha fazla olduğunu söylememiz gereklidir. 

GoF’un Kitaplarındaki şablonlar, doğru nesneleri bulup doğru bir şekilde kurgulamak için farklı tasarım seçenekleri sunar. Bunu da geliştirdiğimiz yazılımın daha rahat değişebilmesi için yaparlar. “Design for change” (degişiklik için tasarla), GoF’un kitabının ilk bölümündeki üç temel prensiptenn birisidir. “Program to an interface, not an implementation” (arayüze program yaz, gerçekleştirmeye değil) ve “favor object composition over class inheritance” (nesne bileşimini sınıf kalıtımına tercih et) kitabın ilk bölümünde savunulan diğer iki prensiptir.

1-Sınıflarda aşırı miktarda statik metot kullanımı

Kodunuzdaki statik metotların sayısı, kodunuzun ne kadar nesne-merkezli olduğunun göstergesidir. Hiç statik metota sahip olmayan bir yazılım düşünmek pek de mümkün değildir ama statik metotların kullanımının istisnai durumlardan çıkarak genel bir hal kazanması, yaklaşımınızın nesne-merkezli olmaktan çıktığının kanıtıdır.

Nesne-merkezli programlamada esas olan, nesne kullanmaktır; statik metotlar ise nesne oluşturmadan, sınıf üzerinden programlama yapmanın yoludur ve bu yüzden de nesne-merkezli programlamanın varlık sebebine karşı bir durum oluşturmaktadırlar. Dolayısıyla aslolan, nesne metotlarının (instance methods) kullanılması, statik metotların ise tamamen istisnai sebeplere bağlı olarak yazılmasıdır. Ne kadar çok statik metot kullanırsanız nesne-merkezli programlamadan o kadar çok uzaklaşıp prosedürel programlamaya o kadar yaklaşmış olursunuz.

Statik metot kullanıp nesne metotu kullanmamak, yazılımı geliştirirken nesneye ihtiyaç duymamak demektir. Bunun birkaç sebebi olabilir:

Metot hakikatten de nesnenin kendisine özel olan durumunu değil, nesnelerin ortak değerlerde barınan durumuna bağımlıdır. Bu durumda nesnelerin ortak durumu, statik değişkenlerle ifade edilir ve bu ortak duruma erişen metotların da statik olmaları normaldir. Bu anlamda statik metot kullanımı, olmadığında sıkıntılı ve verimsiz olacak halin sağlıklı bir şekilde çözülmesinde ibarettir. Örneğin Singleton tasarım şablonunda bir tane yaratılmış olan nesnenin “static” olması ve o nesneye ancak yine “static” olan bir metottan ulaşılması bir zorunluluktur.

Nesnesiz programlama yapmanın başka bir sebebi ise nesne oluşturmanın anlamsız olduğu durumlardır. Bu gibi hallerde durum da vardır ve bu durum üzerinde çalışan metotlar da vardır ama durum ve metotların üzerinde tanımlandığı yapının nesne olması konusunda çok da açıklık yoktur. Böyle hallerde sınıfın kendisinin nesne olarak davranmasını bekleriz. Örneğin “Tanrı” diye bir sınıfımız olsa bu sınıftan kaç tane nesne üretmeyi düşünürdünüz? Eğer çok tanrılı bir dine inanmıyorsak, olsa olsa bir tane Tanrı nesnesi üretmek yeterli olurdu. Bu durumda hiç nesne oluşturmadan Tanrı sınıfını nesne gibi kullanmak dolayısıyla da bu sınıfın üzerindeki her türlü durumu ve davranışı statik yapmak son derece anlamlı olur.

Dolayısıyla bazen ya kavram o kadar soyuttur ki gerçekte bir nesne ile ifade etmezsiniz ve sınıfı nesne olarak kullanırsınız ya da kavramınızın verdiği hizmetler hiç bir şekilde o sınıftan üretilecek nesnelerin farklı olacak durunmlarına bağlı değildir. Java APIsindeki java.lang.Math sınıfı buna çok güzel bir örnektir. Math sınıfının nesnesini oluşturmanın anlamı yoktur, çünkü onun sınıfını yukarıdaki Tanrı sınıfında olduğu gibi olsa olsa ancak bir tane nesneye sahip olabilir (eğer tek bir evrenimiz varsa ve evrenin her yerinde aynı Matematik geçerliyse) bu yüzden sınıfını nesne olarak kullanabiliriz. Ayrıca Math sınıfının metotlarının davranışı bu sınıftan oluşturulacak nesnelerin durumuna bağlı değildir. Dolayısıyla Math sınıfından nesne oluşturmadan üzerindeki statik metotlar sayesinde ondan hizmet alabiliriz.

Bazen, gereksiz nesne oluşturmama gibi masum bir amacın, bol statik metot kullanımına yol açtığı görülmektedir. Gereksiz nesne oluşturma, belleğin gereksiz kullanımına dolayısıyla da, bellek kaçağı (memory leak), performans ve ölçeklenirlik (scalability) problemlerine yol açar. Ama bu durumun tedavisi “hiç nesne oluşturmamak” değildir. Yani “nesneler belleği şişiriyor dolayısıyla nesne oluşturmayıp statik metotlarla çalışıyoruz” demek daha büyük sorunlara yol açacaktır. Eğer hakikatten bellek kullanımı bu kadar önemli ve kritik ise, bunun mimari bir ihtiyaç olarak ortaya konması ve bu seviyede çözümünün aranması daha doğru bir yaklaşım olur. Böyle bir çalışma, nesne merkezli bir dil kullanmak yerine bellek konusunda daha cimri olan prosedürel bir dil kullanmak gibi bir karar ile de sonlanabilir. Ya da Java kullanarak ve bellek konusunda daha tedbirli davranarak da bu sorunu çözebilirsiniz. Nesne oluşturmaktan kaçınmanın ana sebebi nesnenin “gereksiz” değil olsa olsa “anlamsız” olmasıdır. Yukarıda bahsedilen Tanrı ya da java.lang.Math sınıfı gibi, modellenen işin bir parçası olmayan, dolayısıyla işle ilgili nesneden nesneye değişen bir duruma sahip olmayan, araçsal sınıflar ve genelde onların statik olan metotları olmasaydı (ve dolayısıyla da biz bu sınıfların metotlarını ancak nesneleri üzerinden kullanabilir olsaydık,) bellek probleminden çok daha önce kod yapımızla ilgili problemler yaşardık. Çünkü bu gibi sınıfların nesnelerini yaratmak çoğu zaman çok fazla bilgi gerektirir ve bu durumu aşmak için nesne üretici (factory) yapılar gibi başka tasarım desenlerine ihtiyaç duyardık.

  • Nesne yaratmanın anlamsız olduğu durumlar. Bu durumlarda sınıfı, nesne olarak kullanabilirsiniz. Nesne yaratılsa bile sadece bir nesneye ihtiyaç varsa ki bu durum Singleton tasarım şablonuna karşılık gelir, sınıfın kendisini o tek nesne yerine, sanki nesneymiş gibi kullanılabilir
  • Metodunuz, üzerinde çağrılacağı nesnenin hiç bir nesne değişkenini (instance variable) kullanmıyorsa, yani nesnenin durumuna ulaşmıyorsa, statik metot kullanabilirsiniz. Çünkü statik metotlar statik olmayan değişkenlere ulaşamazlar.
  • Metodun çağrılması için bir nesneye ihtiyacınız yoksa statik metot kullanabilirsiniz. Özellikle aynı nesnenin pek çok farklı bağlama geçilip, hepsinde kullanılması gibi durumlarda, farklı bağlama nesne geçmek yerine orada doğrudan sınıf üzerindeki statik metotları çağırmak son derece kolaylıktır. Özellikle araçsal davranışlara sahip (utility) sınıfları bu şekilde yazılımın her bölgesinde kullanmak anlamlıdır.
Statik Metotlu sınıf mı Yoksa Singleton mı ? 

Nesne-merkezli dillerde çok tartışılan konulardan birisi de hiç nesne oluşturmaya gerek bırakmadan kod yazmamızı sağlayan statik özellikler ve metotlar mı yoksa sadece bir tane nesne oluştuğundan emin olup, bu tek nesne üzerinde çağıracağımız nesne özellikleri ve metotları mı? Bu probleme kısaca statik-singleton (static vs. singleton) de denir çünkü tasarım kalıplarında, bir sınıftan sadece bir nesne oluşturmaya “singleton” yani “tekil” denir.

Statik kullanımı, tabi özellikle statik metot kullanımı, nesne-merkezli dillerin, yanlış kullanıma en fazla konu olanı ya da bir başka deyişle suistimale uğrayan alanı. Malum, loglama vb. utility metotlarını, üzerinde bulunduğu sınıfın nesnesini oluşturmadan hızlıca kullanmak isteriz. Örneğin aşağıda üzerindeki metotları statik olarak tanımlanmış ve bu yüzden de bu metotları kullanmak için nesnesine ihtiyaç duymayacağımız bir Log sınıfı vardır


Statik metotlara sahip böyle bir sınıfı kullanmanın kolaylığı çok açıktır: Sınıfın kendisini nesne gibi kullanmak. Dahası o tek nesneyi herhangi bir yere geçmenize bile gerek yok, doğrudan sınıfın üzerinden statik alanlara ve metotlara ulaşabilirsiniz.

Eğer yukarıdaki Log sınıfının metotlarını statik yapmak istemezsek ve bu sınıfın sadece bir tane nesnesinin olacağını planlıyorsak bu durumda Log sınıfını singleton olarak şöyle kodlardık:


Cevap
Nesne-merkezli dillerde aslolan nesne olduğuna göre, statik metotlu sınıf yerine tek nesneli sınıfı yani singletonı tercih etmemiz gerekir.  Nesnelerin yönetimi ve çok bellek tüketmesi gibi sorunlar, henüz nesne kavramını anlamamış ama nesne-merkezli bir dili iyi bildiğini düşünenlerin dile getirdiği sebeplerdendir. 

  • Singleton sınıfla bir miras hiyerarşisi kurup, metotlarını ezmeyi (override) tercih edebilirsiniz. Bu şekilde polymorphic davranış elde edebilirsiniz ki bu durum nesne-merkezli dillerin en temel özelliğidir. Fakat bu durum statik metotlar için geçerli değildir çünkü statik metotlar ezilemezler, sadece nesne metotları ezilebilirler. Bu yüzden polymorphic davranış elde etmek, örneğin “program to interface, not an implementation” gibi prensipleri uygulamak statik metotlu sınıflar ile mümkün değildir. Bir örnek vermek istersek, God sınıfını statik metotlu yaparsanız, örneğin farklı dinlere sahip olanlar, God sınıfından miras alan Allah, Yahweh vb. sınıflarlar oluşturup, üzerindeki “createUniverse()” gibi metotları ezemezler.  Bu durum statik metotlu sınıf kullanımının en ciddi kısıtıdır. Bu yüzden sınıflarınızın miras mekanizmasıyla genişletilip (extension) genişletilmeyeceğinden emin olmalısınız. Örneğin java.lang paketindeki Math sınıfındaki metotlar statiktir ve zaten bu sınıf final yapılmıştır. Çünkü hem Math sınıfının nesnesinin oluşturulmaması istenmiş hem de genişletilmesinin önüne geçilmiştir. Aynı durum java.lang.System sınıfı için de geçerlidir. Fakat aynı paketteki Runtime sınıfı, genişletilebileceği düşüncesiyle statik metotlu değil tek nesneli yapılmıştır. Bu durumda farklı amaçlarla Runtime sınıfınıdan miras devralıp metotlarını ezebilirsiniz. Bu yüzden bu tip sınıflarda “getRuntime()” türünden (java.awt.Desktop sınıfında da getDesktop()) metotlar vardır. (Java SE API’sinde “getInstance()” metotlu belki 100den fazla sınıf var.”getInstance()” metoduna bakarak bunların tek nesneli sınıflar olduğunu düşünmeyin. Java SE API’sindeki statik “getInstance()” metotları çoğunlukla yapılandırıcı metot alternatifi olarak kullanılmıştır ve yeni bir nesne döner. Örneğin java.util.Calendar sınıfı.)
  • Yukarıdaki maddeye çok benzer şekilde, singleton sınıflar, arayüzlerden türetilebilir dolayısıyla da devraldıkları metotlara kod sağlarlar. İlk maddedeki örnek, arayüz kullanımıyla da çözülebilir. Örneğin daha genel düşünüp, Creator isimli bir arayüz oluşurup, bunun GodAllahYahweh gibi sınıflarla genişletilmesini sağlamak daha güzel bir çözüm gibi görünüyor. Dolayısıyla “program to interface, not an implementation” prensibi uygulanmış olur.
  • Static metotlarda ki bir diger problem de unit test yazarken mock objelerde ortaya cikiyor.
    Bu yüzden static metotlarin ufak utility class larinda yazilmasi daha doğru olacaktır.
reference

13 Nisan 2023 Perşembe

Redis (Remote Dictionary Server)

Neden Redis Kullanırız ? 

Redis bir  open-source key-value database serverdır. Bunlara ek olarak data structure server olarak da bilinmektedir.

Redis diğer NoSQL veritabanlarına göre içinde data tipleri bulunduran bir yapıya sahiptir. Örneğin, rediste key’e karşılık gelen valuelerin string olma gibi bir zorunluluğu yoktur. Redis Lists, hash ve sets gibi veri tiplerini destekler. 



Öncelikle Redis, uygulama performansını artırmaya yardımcı olmak için MySQL veya PostgreSQL gibi databaselerin  önünde cache olarak kullanılan bir in-memory veritabanıdır. Redis hakkındaki bir diğer önemli konu ise verileri memory’de tutuyor olmasıdır. Redis dataları öncelikli olarak kendi koştuğu makinanın RAM’inde tutar. Performansının yüksek olmasının sebebi dataları memoryde tutmasıdır. Bunun dışında redis ile istenilirse RAM’de tutulan veriler disk’e de yazılabilir.

Kullanım alanları
  • Nadiren değişen ve sık sık talep edilen veriler için kullanımı uygundur. Caching
  • Sayaçlar
  • Oturum (Session Verileri)
  • Kuyruk işlemleri
  • publish-subscribe mechanisms


Transaction Yönetimi
Transaction: Bir transaction içerdiği SQL ifadelerinin ya tamamını gerçekleştirir, ya da hiçbirini gerçekleştiremez. İşlemlerin tamamı gerçekleşmediği sürece işlemlerin hiçbiri gerçekleşmemiş sayılır.
Bir müşteri kendi hesabından başka bir hesaba 1500 TL para transferi gerçekleştiriyor.
  • Önce kişinin hesabından transfer edilecek olan 1500 TL tutar düşülmelidir.
  • Sonra diğer kişinin hesabına transfer edilecek olan 1500 TL tutar eklenmelidir. 
Bu işlemlerden birincisi gerçekleştikten sonra herhangi bir sorundan dolayı İkinci işlem gerçekleşmezse hesaplarla ilgili ciddi sorunlar yaşanabilir.

Bu tür sorunları önlemek için transaction yapıları kullanılır.

Transaction her iki işlemi de tek bir işlem olarak ele alacağı için herhangi birisi gerçekleşmediği zaman diğer gerçekleşen işlemleri de yok sayacaktır. 

Yani gerçekleşen işlemi geri alacaktır(rollback).

Eğer işlemlerin tamamı sorunsuz bir şekilde gerçekleşirse, tüm İşlemleri kalıcı(commit) hale getirecektir.
 
Çoğu database gibi redis de transaction yönetimini destekler. Transaction kullanarak, birden fazla redis commandini gruplayabilir, böylece bu commandlerin araya herhangi bir başka komut girmeden bir kerede hepsinin çalışacağını veya çalışmayacağını garanti edebilirsiniz. 

Ancak redis transcationu ile ilgili bilinmesi gereken en önemli noktalardan biri rollback olmamasıdır. Bunun anlamı transcation içerisindeki komutlardan biri fail olursa kalanlar çalışmaya devam eder.

Örneğin, syntaxi doğru ama çalıştırılırken hata alınacak bir komut bu tip bir hataya sebebiyet verebilir. Burada bilinmesi gereken önemli husus, komutlardan biri hatalı olsa dahi diğer komutlar çalışmaya devam eder.

Redis Handling Failure

Redis, ilişkisel databaselerin aksine ACID prensibiyle çalışmayan bir NoSql çözümü olduğu için ve verileri memoryde tuttuğu için, olası bir felaket senaryosunda veri kaybetmemek için kendimizi her türlü felaket senaryosuna hazırlamamız gerekir

1.Persistency Ayarlarını Doğrulamak
Herhangi bir sıkıntı sebebiyle veri kaybıyla karşılaşma ihtimalimize karşı güvenlik önlemlerimizden biri persistence ayarlarımız olabilir. Redis persistence’ı iki farklı opsiyon ile destekler. Snapshotting ve Append-only file
  • Snapshotting bizim belirlediğimiz belli zaman aralıklarıyla redisteki verinin snapshotını alır ve diske kaydeder. RDB snapshottingi db backupları gibi düşünebilirsiniz. Snapshotting özelliğiyle veri kaybetme olasılığınız her zaman vardır. RDB snapshotın mantığı belli aralıkla rediste belli sayıda key’in değişip değişmediğini kontrol ederek bu da göre snapshot almak üzerine kuruludur. Örneğin, bu aralığın 5 dakikada bir olduğunu varsayarkan ilk snapshottan sonraki 5 dakikalık aralığın 4. dakikasında server down olursa o 4 dakikaya kadar olan verileriniz henüz backup alınamadığı için kaybolur.
  • Append-only file ise redise gelen her commandı kaydeder yani real time’a yakın bir veri koruyuculuğu sağlar. Bu sayede herhangi sebepten bir veri kaybı yaşadığımızda bu opsiyonlar ile verilerimizi geri yükleyebiliriz.
2.Çöken Bir Master Makineyi Değiştirmek
Bazı zamanlar makinaların bir sebepten down olma durumu söz konusu olabilir. Bu sebep kötü makine, kötü memory belki de elektriklerin gitmesi bile olabilir. Ancak sebepleri bir yana, en nihayetinde redis serveri değiştirmemiz gerekecektir. Eğer bir grup redis serverını replicasyon ve persistence ile koşturuyor isek bu durumla mücadele edebilecek şansımız var demektir.

Bir örnek senaryo üzerinden ilerleyelim.

Master olarak hareket eden A makinamız, bir de slave olarak davranan B makinamız olsun. A bir sebepten dolayı ağdan kopmuş olsun ve bir de C makinamız olsun. Bu durumda plan gayet basit. B makinasına SAVE keywordü üzerinden güncel snapshot üretmesini söyleyeceğiz. Bu snapshotu C makinasına kopyalayacağız. Ve daha sonra redisi C makinasında ayağa kaldıracağız. Ve en sonunda B makinasına Cnin slaveyi olmasını söyleyeceğiz. Ya da bir alternatif yol olarak da slaveyi yeni mastera çevirmek isteyebilirz.

Her iki durumda da, Redis kaldığı yerden devam edebilecektir. Bundan sonraki tek işimiz, istemci yapılandırmamızı uygun sunuculara okumak ve yazmak için güncellemek ve isteğe bağlı olarak Redis’i yeniden başlatmamız gerekirse disk üstü sunucu yapılandırmasını güncellemektir

Redis Scaling
Scaling tek bir makinenin gücünün sınırlarını gördüğümüzde veri ve performans ihtiyacımız artmaya devam ediyorsak karşımıza çıkan en net çözümdür. Scaling out yani yatayda ölçekleme, processleri birden fazla makinaya dağıtıp süreçleri paralelde işletmek üzerine kuruludur. Dolayısıyla bu da dağıtık bir sistem inşa etmek etmek demektir. Rediste scalingi read ve write kapasitesini artırmak için ayrı çözümler olarak kulanabiliriz. Read kapasitesini artırabilmek için read-only slave serverlar ekleyerek yani replicasyon özelliğinden faydalanarak, Write kapasitesinin sınırlarını artırmak için de sharding yönetimi kullanabiliriz.

Redis Read Scale Etmek

1- Redis Replication

Replikasyon dediğimiz yöntem aslında ana master serverimizde tuttuğumuz verimizin diğer serverlarda kopyasının tutulmasıdır. Redis de ölçeklenmeye yardımcı olmak açısından replikasyonu desteklemektedir. Redis her ne kadar çok hızlı olsa da zaman zaman darboğaza düştüğü durumlar olabilir ve bu gibi durumlarda da replikasyon özelliği yardımımıza koşabilir.

2- Master-Slave Replikasyonu
Redis master-Slave replikasyonunu destekler. Yani bir master serveriniz ana redis serverınızdır ve diğer slaveler bu mastera subscribe olup masterda veri güncellendikçe kendilerini update eder ve orjinal veriye sahip olur.

Slave serverlar iki ana amaç için kullanılabilir. İlki redis master serverınızın yavaş çalıştığı durumlarda yükü bölmek ve sistemi hızlandırmak. Redis master, serverınızda çok fazla operasyon olduğunda read operasyonlarını slavelere dağıtıp yükü hafifleterek serverı hızlandırabilirsiniz. Tabi slavelere güncel veriyi dağıtacak olan node master node olduğundan writelar yine master node üzerinden ilerlemeye devam eder.

İkinci bir kullanım amacı ise handling failure. Eğer master nodeunuz herhangi bir sebepten çökerse,  slave nodedaki veriyi kullanarak yeni bir master node’a taşıyabilir ya da direkt slave node’u master olarak kullanabilirsiniz.



Bazen bir mastera çok sayıda slave bağlandığında da performans kayıpları olabilir. Aslında redis serverlarının temelde master veya slave olması arasında fark yok. Dolayısıyla bu durumda biz master node’a slaveleri bağlarken slavelere de diğer slaveleri bağlayabiliriz. Yani master’ın slaveyi, slave’in de başka bir slave’i olabilir. Bu durumda bütün veriyi slavelere master üzerinden değil, bir kısmına master üzerinden diğer kısmına diğer slaveler üzerinden dağıtabiliriz. Ayrıca redis append-only özelliği ile replikasyonu bir arada kullanırsak veri kaybını sıfıra indirgeyebiliriz.

3- Redis Read Scale
Redis serverlarımızı scale etmeden önce, performans attırmak için gözden geçirebileceğimiz tüm seçenekleri gözden geçirmeliyiz  eğer ki elimizdeki seçenekler tükendiyse sclale etme sürecine girmemiz yerinde olacaktır.

  • Eğerki küçük yapılar kullanıyorsak, ziplist sizeımızın çok büyük olmadığından emin olmamız gerekir.
  • Kullandığıımız veri yapılarının performans etkilerini gözden geçirmemiz gerekir.
  • Eğer ki redise cachelemek için büyük objeler gönderiyorsak bu objeleri compress etmek ve netword bandwidth’ini read ve writelar için azalmayı düşünmeliyiz.
  • Redis pipelining ve connection poolingi özelliklerini de kullanmayı unutmamalıyız.
Eğer performansa dair herşeyi yaptığımızı düşünüyorsak geriye bir tek scale etmek kalıyor  . Read performansını arttırmanın en temel ve basit yolu redise read-only slave serverlar eklemektir. Bu read-only slave serverlar master servera bağlı olup masterın replicalarını alıp real-time'a yakın bir sürede güncel olurlar. Yani siz master server’a bir data yazarsınız ve master da bunu slaveleriyle paylaşır. Burada önemli nokta master çökerse ne olacağıdır.

Bir slave mastera bağlandığında masterın snapshotu yani bağlandığı anda masterdaki verinin kopyası alınır ve slave’e gönderilir. Eğer birden fazla slave mastera bu sırada bağlanırsa tüm slavere ayrı snapshot çekilmesi yerine hepsine aynı snapshot gider ve bu performans açısından oldukça fayda sağlar. Ancak çok fazla slave mastera bağlandığı takdirde, master slave arasında trafik artışı ve performans problemi çıkabilir. Bunu azaltmanın yolu da master’a slave bağlarken slave’e de başka slaveler bağlamaktır. Yani master-slave-slave şekilde bir ağaç yapısı kurmaktır.

Redisin replicasyon ve failover durumları için kullanılabilmek için yeni bir toolu olan redis sentinel de göz önünde bulundurulabilir. Aslında Redis sentinel bir redis server modudur ve bu nodeda redis normal bir redis server olarak hareket etmez. Bunun yerine master ve slavelerin davranışını ve statusunu takip eden bir yapı olarak hareket eder. Masterin fail olması durumunda redis sentinel var olan slaveler arasından bir master seçebilir. Bu slave master seçildikten sonra sentinel tüm diğer slaveleri yeni master üzerinden bağlayacaktır.
Redis Write ve Memory Kapasitesini Scale Emtek
Replication yöneti ile redisin read’ini ölçekleyebileceğimizden bahsettik. Write’ların ise hala master node üzerinden yürümeye devam edeceğinden  de bahsettik. Peki write yükü artığında ve write’ı güncellememiz gerektiğinde ne yapmalıyız? Burada artık sharding devreye girer. Normal şartlarda shardingle memory kapasitemizi artırmayı düşünürüz ancak bu yöntem eğer ki makinemizin gücü son noktaya ulaştuysa write performansımızı artırmak için, geçerli bir yöntemdir. Yani redis üzerinde tek makina sınırlarına ulaştıysa artık bu memoryi ve dolayısıyla write’ı scale edebilmek için sharding metodunu kullanabiliriz.
Redis’de veri parçalama (partitioning), tüm verileri birden çok Redis örneğine bölme tekniğidir, böylece her örnek yalnızca anahtarların bir alt kümesini içerecektir
Eğer ki memory’i optimize etmek ve performansı maximize etmek için her şeyi denediysek ve elimizdeki makinenin sınırlarını gördüysek, artık verimizi farklı instancelara sharding etmenin vakti gelmiş demektir.
Ne Zaman Sharding Kullanmalıyız ?
Çok büyük dataları yönetiyorsak ve tek bir makinenin memorysiyle yetinmek yerine çok fazla bilgisayarın gücüne ihtiyaç duyuyorsak, hesaplama gücünü birden çok CPU, birden çok bilgisayar arasında ölçeklendirip ve ağ bant genişliğini kullanmak istiyorsak shardinge başvurabiliriz. Tabi verileri yatayda parçalara ayırmak yani sharding yapmak ve bunları ayrı makinalara koymak yani dağıtık bir sistem oluşturmak kulağa kazandıran bir yöntem gibi gelse de bunun bazı trade-offları var ve sistemleriniz dağıtık hale geldiğinde sorunlarınız da dağıtık hale geliyor. Rediste shardinge başvurmadan önce shardingin getireceği bazı sıkıntılardan bahsedebiliriz.

  • Sharding sonucu farkı instancelarda bulunan multiple keylerle operasyon yapamazsınız.Farklı 
  • redis instancelarında bulunan keyler ile transaction işlemi yapamazsınız.
  • Key bazlı partitioning yapamazsınız. Mesela çok büyük bir list veya sorted list objesini farklı instance’a koymak gibi.
  • Backup ve persistence yönetimi eskiye göre çok daha komplex olur. Çünkü çok daha fazla RDB ve AO fileları ile uğraşmanız gerekir  backup involves aggregation (merging) of the RDB files from many instances.
  • Runtimeda clusterınıza instance eklemek veya silmek data misbalancingleri oluşturur ki bunun çözümü de preshardingtir.

PRESHARDİNG YÖNTEMİ
Rediste sharding yapısını kullanmaya başladığınızda runtimeda instance eklemek ve silmek oldukça zordur. Ancak bunun üstesinden gelmek için kullanabileceğiniz teknikler de var. Bunlardan biri preshardingtir.

Preshardingin mantığı oldukça basittir. Bir makinada başlangıçta birden fazla redis instanceı oluşturursunuz ki bu 32 ya da 64e kadar çıkabilir çünkü redis oldukça lightweight bir üründür. Bu sayede data storageın büyümesi gerektiğinde ve redisin bunu handle etmesi gerektiğinde redis instancelarını bir makineden başka makineye taşımak mümkündür. Eğer ki bir adet redis serverınız varsa ve bir adet daha eklemeniz gerekiyorsa redis instancelarınızın yarısını diğer servera taşırsınız. Bu işlemi her redis instanceı bir servera karşılık gelene kadar yapabilirsiniz.
Redis Architecture
  1. Single Redis Instance
  2. Redis High Availability
  3. Redis Sentinel
  4. Redis Cluster
Single Redis Instance
Single Redis İnstance, Redis'in en basit dağıtımıdır. Kullanıcıların servislerini büyütmelerine ve hızlandırmalarına yardımcı olabilecek küçük örnekler oluşturup çalıştırmalarına olanak tanır. Ancak dezavantajlarıda vardır. Örneğin,Redis  fail veya unavailable olursa yapılan tüm istemci çağrıları başarısız olur ve bu nedenle sistemin genel performansını ve hızı düşer. Redis, verileri kalıcı kılmak için ayarlanmamışsa, yeniden başlatma veya yük devretme durumunda veriler kaybolur.

Redis High Availability

Secondaryler mainin replicalarıdır. Birden fazla olabilirler. Redisten okuma durumunda read operasyonları için ölçeklenmeye fayda sağlarlar ve main instancesi fail yada unavailable olduğunda onun yerine main olarak atanabilirler.

31 Mart 2023 Cuma

Aggregations (Avg,Min,Max,Sum)

PUT ders/_bulk
  {"index":{"_id":1}}
  {"ders_adi":"MsSql","ders_suresi":"60","sinif":"A1","tipi":"Database","eğitmen":"Kaan","mevcut":"40"}
  {"index":{"_id":2}}
  {"ders_adi":"Css","ders_suresi":"45","sinif":"A2","tipi":"Frontend","eğitmen":"Kaan","mevcut":"12"}
  {"index":{"_id":3}}
  {"ders_adi":"Html","ders_suresi":"45","sinif":"A2","tipi":"Frontend","eğitmen":"alican","mevcut":"27"}
  {"index":{"_id":4}}
  {"ders_adi":"Java","ders_suresi":"25","sinif":"B1","tipi":"Backend","eğitmen":"karam","mevcut":"21"}
  {"index":{"_id":5}}
  {"ders_adi":"C sharp","ders_suresi":"50","sinif":"A2","tipi":"Backend","eğitmen":"ayşe","mevcut":"25"}
  {"index":{"_id":6}}
  {"ders_adi":"Oracle","ders_suresi":"50","sinif":"B2","tipi":"Database","eğitmen":"ayşe","mevcut":"32"}
    {"index":{"_id":6}}
  {"ders_adi":"Jquery","ders_suresi":"50","sinif":"B3","tipi":"Database","eğitmen":"ayşe","mevcut":null}

AVG
ilgili fieldin ortalamasını alır. İlgili fieldda null değerler varsa onları missing ile 0 değeri verebiliyoruz.
GET ders/_search
{
  "aggs": {
    "ortalama": {
      "avg": {
        "field": "mevcut",
        "missing": 0
      }
    }
  }
}

MİN
GET ders/_search
{
  "aggs": {
    "minimum": {
      "min": {
        "field": "mevcut"
      }
    }
  }
}

MAX

GET ders/_search
{
  "aggs": {
    "maximum": {
      "max": {
        "field": "mevcut"
      }
    }
  }
}

SUM
Kann öğretmeninin sınıf mevcudunun toplamını alacağız.
GET ders/_search
{
  "query": {
    "bool": {
      "must": [
        {"match": {
          "eğitmen": "Kaan"
        }}
      ]
    }
  },
  "aggs": {
    "ortalama": {
      "sum": {
        "field": "mevcut"
      }
    }
  }
}
.

30 Mart 2023 Perşembe

Id, Wildcard ve Prefix ile sorgulama

PUT ders/_bulk
  {"index":{"_id":1}}
  {"ders_adi":"MsSql","ders_suresi":"60","sinif":"A1","tipi":"Database"}
  {"index":{"_id":2}}
  {"ders_adi":"Css","ders_suresi":"45","sinif":"A2","tipi":"Frontend"}
  {"index":{"_id":3}}
  {"ders_adi":"Html","ders_suresi":"45","sinif":"A2","tipi":"Frontend"}
  {"index":{"_id":4}}
  {"ders_adi":"Java","ders_suresi":"25","sinif":"B1","tipi":"Backend"}
  {"index":{"_id":5}}
  {"ders_adi":"C sharp","ders_suresi":"50","sinif":"A2","tipi":"Backend"}


Id ile Sorgulama
Idsi 2 ve 3 olan kayıtları getir.
GET ders/_search
{
  "query": {
    "ids": {
      "values": [2,3]
    }
  }
}



Wildcard ile sorgulama.
Sonu tipi alanında sonu end ile biten kayıtları getir.
GET ders/_search
{
  "query": {
    "wildcard": {
      "tipi": {
        "value": "*end"
      }
    }
  }
}


Prefix ile sorgulama
Prefix wildcarda oranla daha performanslı ve kaynakları daha az kullanan bir yapıdır.


Sorting ve Pagination

PUT ders/_bulk
  {"index":{"_id":1}}
  {"ders_adi":"MsSql","ders_suresi":"60","sinif":"A1","tipi":"Database"}
  {"index":{"_id":2}}
  {"ders_adi":"Css","ders_suresi":"45","sinif":"A2","tipi":"Frontend"}
  {"index":{"_id":3}}
  {"ders_adi":"Html","ders_suresi":"45","sinif":"A2","tipi":"Frontend"}
  {"index":{"_id":4}}
  {"ders_adi":"Java","ders_suresi":"25","sinif":"B1","tipi":"Backend"}
  {"index":{"_id":5}}
  {"ders_adi":"C sharp","ders_suresi":"50","sinif":"A2","tipi":"Backend"}


Pagination

Array gibi çalışır. 0 dan başla ilk iki kaydı getir.

GET ders/_search
{
  "size": 2,
  "from": 0
}


Sorting

GET ders/_search
{
  "sort": [
    {
      "ders_adi.keyword": {
        "order": "desc"
      }
    }
  ]
}

.. 

Uri ve Dsl query

PUT ders/_bulk
  {"index":{"_id":1}}
  {"ders_adi":"MsSql","ders_suresi":"60","sinif":"A1","tipi":"Database","eğitmen":"Kaan","mevcut":"40"}
  {"index":{"_id":2}}
  {"ders_adi":"Css","ders_suresi":"45","sinif":"A2","tipi":"Frontend","eğitmen":"Kaan","mevcut":"12"}
  {"index":{"_id":3}}
  {"ders_adi":"Html","ders_suresi":"45","sinif":"A2","tipi":"Frontend","eğitmen":"alican","mevcut":"27"}
  {"index":{"_id":4}}
  {"ders_adi":"Java","ders_suresi":"25","sinif":"B1","tipi":"Backend","eğitmen":"karam","mevcut":"21"}
  {"index":{"_id":5}}
  {"ders_adi":"C sharp","ders_suresi":"50","sinif":"A2","tipi":"Backend","eğitmen":"ayşe","mevcut":"25"}
  {"index":{"_id":6}}
  {"ders_adi":"Oracle","ders_suresi":"50","sinif":"B2","tipi":"Database","eğitmen":"ayşe","mevcut":"32"}
    {"index":{"_id":6}}
  {"ders_adi":"Jquery","ders_suresi":"50","sinif":"B3","tipi":"Database","eğitmen":"ayşe","mevcut":null}

Yukarıdaki gibi bulk insert ile elasticsearche kayıtlarımızı insert ettik.

Uri Query
GET ders/_search?q=ders_suresi:45

DSL Query
Match ile search ederken arama kriterini nasıl yazarsak yazalım match eder. örn. JAVA,JaVA
GET ders/_search
{
  "query": {
    "match": {
      "ders_adi": "Java"
    }
  }
}

Term ile serach ederken arama kriterini küçük harflerle yazmamız gerekir.örn java
GET ders/_search
{
  "query": {
    "term": {
      "ders_adi": "Java"
    }
  }
}


İndex içerisindeki sadece belli alanları getirmek istiyorsak 
GET ders/_search
{
"_source": ["ders_adi","eğitmen"]
}

Compand Query:

AND Query
GET ders/_search
{
  "query": {
    "bool": {"must": [
      {"match": {
        "sinif": "A2"
      }},
      {"match": {
        "tipi": "Frontend"
      }}
    ]}
  }
}

OR Query
GET ders/_search
{
  "query": {
    "bool": {"should": [
      {"match": {
        "sinif": "A2"
      }},
      {"match": {
        "tipi": "Frontend"
      }}
    ]}
  }
}

Must Not Query 

Sinifi A2 olan veya tipi frontend olan kayıtların hiç birisi gelmeyecek
GET ders/_search
{
  "query": {
    "bool": {"must_not": [
      {"match": {
        "sinif": "A2"
      }},
      {"match": {
        "tipi": "Frontend"
      }}
    ]}
  }
}

AMA Query
Sinif A2 olan ama Tipi frontend olmayan kayılar.
GET ders/_search
{
  "query": {
    "bool": {
      "must": [
        {"match": {
          "sinif": "A2"
        }}
      ],
      "must_not": [
        {"match": {
          "tipi": "Frontend"
        }}
      ]
    }
  }
}

Filter Query
Ders süresi 10 ile 50 arasında olan kayıtlar
GET ders/_search
{
  "query": {
    "bool": {"must": [
      {"match_all": {}}
    ],
    "filter": [
      {"range": {
        "ders_suresi": {
          "gte": 10,
          "lte": 50
        }
      }}
    ]  
      
    }
  }
}


Ders tipi frontend olan, sinifi A2  olan ve ders süresi 10-50 arsında dersleri getir.
GET ders/_search
{
  "query": {
    "bool": {"must": [
      {"match": {
        "tipi":"Frontend"
      } },
      {
        "match": {
          "sinif": "A2"
        }
      }
    ],
    "filter": [
      {"range": {
        "ders_suresi": {
          "gte": 10,
          "lte": 50
        }
      }}
    ]
    }    
  }
}
.

Elasticsearch Optimistic Concurrency Control

Elasticsearch distributeddır. Bir document üzerinde  created,updated yada deleted operasyonları olursa documentin yeni versionu clusterdaki diğer replica nodelara gönderilir. Documentin eski bir versionun daha yeni bir versionu overwrite etmemesi için document üzeride gerçekleştirilen her işleme(CRUD), bu değişikliği koordine eden primary shard tarafından bir seq_no atanır. seq_no her operasyonda artar ve böylece daha yeni operasyonların eski operasyonlardan daha yüksek bir sıra numarasına sahip olması garanti edilir. Operasyonlar sırasında bu seq_no bilgisinide kullanarak eski versionların yeni versionları overwrite etmesi önlenmiş olur.

PUT products/_doc/1567
{
  "product" : "r2d2",
  "details" : "A resourceful astromech droid",
  "stock": 5
}

Yukarıdaki bir komut dizini product documenti oluşturacak ve ona primary_term ve seq_no atayacaktır.


Bir documenti get yaptığımızda bize belgenin en son seq_no ve primary_term değerleri gelir. Biz bu document üzerinde değişiklik yaptığımızda bu seq_no ve primary_term bizden önce değişikliğe uğramış mı onu kontrol ederiz. Bunuda if_seq_no ve if_primary_term paremetrelerini kullnarak yaparız. örneğin bizden önce birisi bu kayıt üzerinde işlem yaptıysa seq_no değişmiştir. Ozaman aşağıdaki işlem başarısız olur. Fakat kimse işlem yapmadıysa başarılı olur.

POST products/_update/1567?if_seq_no=0&if_primary_term=1
{
  "doc":{"stock": 4}
}

Yukarıdaki komutu stock bilgisinde değişiklik yaparak tekrar çalıştırırsak conflict hatası alırız. Çünkü artık seq_no değeri değişti.

POST products/_update/1567?if_seq_no=0&if_primary_term=1
{
  "doc":{"stock": 3}
}