SD #5: System Design Trade-off'lari

 Malesef trade-off'a guzel bir Turkce karsilik bulamadim. Vardi birtane ama hatirlayamiyorum suanda. Nyese.

Scalability camiasinda hersey bir trade-off. Yani bir yerden kazaniyorsan baska bir taraftan kaybediyorsun. En baslica 3 cesit tradeoff var. 

1. Performance vs Scalability

- Sistem sadece bir kullanici icin bile yavas ise, performans problemi var demektir.
- Sistem bir kullanici icin hizli ama kullanici sayisi artinca yavasliyor ise, scalability problemi var demektir.

Peki bu ikisi nasil bir trade-off teskil edebilyor? Su makalede cok guzel ifade etmis:

One thing that tripped me up early on in my career was the difference between performance and scalability. At first I thought they were exactly the same. I was quite surprised when my first project to scale a system actually made my code run slower. 

Normal 1 kullanici icin cok performansli bir sistemi alip, scalable yaptigimizda, yine sadece 1 kullanici icin test edersek, belki performansin bir miktar dustugunu gorebiliriz. Soyle bir ornek dusunebiliriz: Servisimiz tek bir server uzerinde calisiyor ancak RAM ve CPU cok ust duzey, SSD kullaniliyor. Belirli bir kullanici sayisina kadar cok yuksek performans aliyoruz. Ancak o sayi asildiktan sonra sistem kullanilamaz hale geliyor. Buna mukabil, sistemi dusuk RAM ve CPU'lu ve de doner sabit diskli 5 sunucu ile tekrar kurguluyoruz. Bu durumda sorunsuz hizmet verebildigimiz kullanici sayisi belki 4 kat artiyor. Ayni zamanda da, 1 kullanici icin alinan performans onceki cozume gore biraz dusuyor. Grafikte guzel anlatilmis:


Ama tabi ki nihai hedefimiz sadece 1 kullanicinin maks performansi almasi degil, cok sayida kullanicinin yeterince iyi performans alabilmesi. Performans ve Scalabilirt arasindaki trade-off iste bu, arkadaslar. 

2. Latency vs Throughput

Latency: Bir sonuc uretebilmek icin gecen toplam sure. Yani bir API requestio attiktan sonra, cevap tekrar elime ulasana kadar gecen toplam sure. 
Throughput: Birim zamanda tamamlanabilen ortalama is sayisi. 

Ornek: Bir araba fabrikasinda, bir arabanin uretilmesi 8 saat surerken, fabrika 1 gunde toplam 120 araba uretebilmektedir. 
Latency: 8 saat
Throughput: 120 araba/ gun - 5 Araba/saat

Peki buradaki trade-off nerede? Performance vs Scalability trade-off'unda oldugu gibi, sadece 1 islemin performansli olmasini saglayacak yonte, birden fazla islem icin ortalama performansi dusuruyor. 

Soyle dusunelim. A sunucusundan B sunucusuna data aktarmak gerekiyor. Bunu (ornegi abartmak icin) Http uzerinden yaptigimizi dusunelim. Data buyuk, parcalara bolup gondermek gerekli. Tabii ki parca boyutu kuculdukce, gonderim hizi (latency) azalacaktir. Parca boyutunu kuculttukce bir yerden sonra, birim zaman icin toplamda gonderilen datanin azalmaya basladigini gorecegiz. 

Datayi teker teker gonderiyoruz ama her bir gonderme demek bir Http requesti demek. Http requesti suresinin tamami data gonderimi degil. Burada gonderilen datadan bagimsiz, sabit bir handshake sureci var. Yani hic data gondermeseniz bile, gececek olan bir sure var. Biz data boyutunu azalttikca, bu alt limit daha significant olmaya basliyor. Tek bir http requestinde hangi adimlar ariliyor surada cok iyi anlatilmis.


Demek oluyor ki biz kucuk data da gondersek buyuk data da gondersek bu adimlardan bazilari hep orada olacak. Yani optimal data buyuklugunu bulmamiz gerekiyor. Cok buyuk olursa bu sefer 3 numarali send asamasi cok uzayacak. Iste latency vs throughput'taki trade-off bu arkadaslar. Cok daha detayli bir inceleme icin ise soyle bir paper var. Ben sadece ilk chapteri okudum ama bookmarklara ekledim. (bizde yalan olmaz gardash) 

3. CAP Teoremi

Teorem cok acik net, in your face: distributed bir bilgisayar sisteminde su 3 garanti ayni anda saglanamaz:

1- Consistency
2- Availability
3- Partition Tolerance 

Partition tolerance: En sondaki en basta konusalim cunku bundan feragat edilemiyor. Buradaki partition lafi biraz kafa karistirici olabiliyor (bana oyle geldi). Anlatilmak istenen, bir data, birden fazla makine (ya da node diyelim) uzerinden yaziliyor ve okunuyorsa, node'lardan bir tanesinin diger node'larla iletisiminin kesilmesi anlamina geliyor. Yani burada bir network bolunmesi yasaniyor. Bolunen node (veya node'lar) kendi basina requestlere cevap vermeye devam edebilir ama kendi aralarinda iletisim kuramazlar. Bu iletisimin kesilmesine network partition adi veriliyor.  

Bizim olayimiz scalability oldugu icin ve de network hicbirzaman tam guvenilir olmadigi icin, network partition durumu olusursa (ki olusacaktir), bunu tolere edebiliyor olmamiz gerekiyor. O zaman trade-off sadece consistency ve availability arasina indirgenmis oldu. 

Consistency: Tum clientlarin ayni datayi okuyor olmasi durumu. Yani bir network partition olusursa, bir cevap donduremeyecegiz anlamina geliyor. Cunku bir node'a sistem ulasamiyor, ya o node uzerinde database guncellemesi yapildi ise bizim bundan haberimiz yok ise? Bu durumda consistent bir sistem timeout hatasi dondurecektir. Yani tutarli cevap verilemiyorsa, hata donulecektir.  

Availability: Sistemin ne pahasina olursa olsun (biraz abarti oldu, aslinda inconsistent olma pahasina demek daha dogru olur), bir cevap donduruyor olmasidir. Yani network partition olusursa, sistem biraz stale (bayat) bile olsa, mevcut eldeki cevabi dondurecektir. Partition tolerance ve availability birlestiginde, eventual consistency de denilen durum ortaya cikmaktadir. Yani sistem gercek zamanli olarak olmasa bile, kabul edilebilir bir sure icerisinde consistent olacaktir. Ama bu gerceklesene kadar bazi clientlar stale data okuyabilir. Eger bu kabul edilebilirse, ne ala.

Goruldugu gibi partition tolerance desteklenirken hem consistency hem de availability desteklenemiyor. Yapilacak trade-off ise, basarmaya calistigimiz seye gore degiskenlik gosterecektir. 

Ornegin degisik requirementler icin degisik database teknolojileri farkli trade-off'lar secmisler:



Relational database'lerin partition tolerance'den odun verdigine dikkat cekmek istiyorum. Bu da sanirim neden horizontal scale olamadiklarini gosteriyor. 

Consistency Patterns

Ayni datayi birden fazla node uzerinde tutuyorsak, bir sekilde bunlari senkronize etmemiz gerekir. Bu senkronizasyonu yapmanin da kendi icinde trade-off'lar barindiran 3 yontemine bakalim:

1. Weak Consistency: Bir yazma isleminden sonra, okuma yapan clientlarin tamami bu degisikligi gorebilir de goremeyebilir de. Elden gelen en iyi sekilde gormesi saglanmaya calisilir ama garanti edilemez. Mesela bir wahtsapp sesli konusmasinda, baglanti kopup tekrar baglanirsaniz, arada konusulanlari kaybetmis olursunuz. Sistem hicbir zaman o state'e tekrar gelmez.

2. Eventual Consistency: Sistem consistent duruma zaman icerisinde mutlaka gelir. Data asenkron olarak replika edilir. Ornegin DNS kayitlarinin guncellenmesi (daha dogrusu guncellemenin, network uzerindeki diger node'lara yayilmasi, propagate etmesi) buna ornektir. Eninde sonunda tum node'lar ayni state'e kavusur, ama belirli bir zaman gecmesi gerekir. 

3. Strong Consistency: Bir yazma isleminden sonra tum node'lar ayni state'e sahip olur. Data senkron bir sekilde replika edilir. RDBMS'lerde ve dosya sistemlerinde olan budur. Transaction mantigi gerektiren sistemlerde kullanilmasi avantajlidir.  

Availability Patterns

Birbirlerini destekleyecek sekilde 2 availability pattern'den bahsedecegiz. Yani ikisinden birisini secmek gerekiyormus gibi bir durum akla gelmesin. Fail-over patternleri node'lar icin kullanilabilirken, replication da database icin kullanilabilir. Ama hedef aynidir, highly available sistemler elde etmek!

1. Fail-over
En genel anlamda, distibuted sistemlerde bir componentin, bir de yedeginin hazirda bekletilmesi durumudur. Peki bir sistem yedekte beklerken, mevcut calisan sistem cokerse digerinin bundan nasil haberi olacak?

Cevap heartbeat yontemi. Mevcut calisan sistem, yedekte bekleyene surekli bir heatbeat sinyali gonderir (mesela belki saniye basina 1 sinyal). Bu sinyal kesilmedigi surece yedek sistem yedek kalmaya devam eder. Ama sinyal gelmesi gereken zamanda gelmez ise, kendisini online konuma tasir.  

Burada da iki farkli yontem one cikmaktadir.

Active/Passive
Bu modelde sadece aktif olan node trafik aliyor. Pasif node tamemen yedekte bekliyor. Yedekte beklemenin de iki cesidi var hot ve cold. Hot beklemede, hersey hazir sadece trafik pasife yonlendirildiginde direk calismaya basliyor. Cold beklemede ise bu node'un ayaga kalkmasi gerekiyor. 

Heartbeat sinyali aktif olan sistemden pasif olan sisteme gonderiliyor. Sinyal kesildigi anda, pasif olan kendisini aktif hale getiriyor ve trafigi ustlenmeye basliyor. Bu da onceden aktif olan node'un IP adresini almasini gerektiriyor. Bunun floating IP teknolojisi ile yapilabileceginden onceki scalability yazisinda bahsetmistik. 

Herhangi bir servisin scale'edilmesinde load balancer kullanilir. Ancak unutmamak lazim ki load balancerin kendisi de bir single point of failure konumudadir. Bu noktada iki farkli load balancer'i active/passive modeli ile replika etmek bizi bu durumdan kurtaracaktir. 


Active/Active
Bu modelde iki node da trafik almakta ve hizmet vermektedir. Aslinda bildigimiz onune load balancer konulmus replika node'lar kumesidir. 


Failoverin dezavantajlari:
- Ekstra donanim / kaynak gerektirir
- Karmasikligi artirir
- Active/passive'da data replicate edilemeden once active olanin cokmesi durumunda data kaybi yasanabilir. 

2. Replication
Master/slave ve Master/master olmak uzere iki cesit replication cesidi bulunmaktadir. 

Master/Slave
Bir master ve buna bagli n sayida slave ile olusturulan bir database replikasyon mimarisidir. Yazma islemleri sadece master'a yapilir. Daha sonra slave'lere propagate edilir. Okuma islemi ise hem slave'lerden hem de master'dan yapilabilir. 

Slave'lerin de kendine ait slave'leri olabilir, boylece bir agac yapisi elde edilebilir. 

Eger master node cokerse, slave'lerden bir tanesi master olarak promote edilene kadar sistem read-only mode'da hizmet vermeye devam eder. 

Dezavantajlari:
- Bir slave'i master olarak promote etmek icin ekstra logic gerekir.
- Master, kendisine gelen bir guncellemeyi slave'lere iletemeden cokerse data kaybi olusur.
- Eger yazma cogunlukta ise, slave'lere surekli master'dan yazma guncellemesi geleceginden, okuma yapmak isteyen client'lara verilen hizmet kalitesi duser.

Master/master
Sistemde birden fazla master vardir, hem yazma hem okuma yapilabilinir. Ancak master'lari sync halinde tutmak gereklidir. Bir node cokerse sistem yazma/okuma hizmeti vermeye devam edebilir.


Dezavantajlari:
- Tum node'lar master oldugu icin, client'in hangisine gitmesi gerektigine karar verecek bir load balancer kullanilmalidir. Ya da , client hangisine gitmesi gerektigini bilecek sekilde bir application logic icermelidir.
- Cogu master-master replikasyon mimarisi, dusuk consistency'e sahiptir ya da master'lar arasindaki replikasyon maliyetinden dolayi yuksek latency'e sahiptir.
- Node sayisi arttikca bunlari sync halinde tutabilmek, olusabilecek conflict'leri cozebilmek zorlasir. Bu durumda da latency artar.


Ozetle
Bu yazida scalable sistemler tasarlarken karsilasilabilecek 3 ana trade-off noktasina degindik. 
Diger sistem design postlarinda gorusmek uzere. 
Stay hungry :)




Yorumlar

Bu blogdaki popüler yayınlar

Python'da Multithreading ve Multiprocessing

Threat Modeling 1

Encoding / Decoding