Azure Storage Services – Part 5:Scalability

Makale serimizin bu bölümünde Azure Storage hizmetlerinin ölçeklenebilirlik limitlerini inceliyor olacağız. İsterseniz öncesinde Scability (Ölçeklenebilirlik) ne anlama geliyor buna biraz bakalım.

http://en.wikipedia.org/wiki/Scalability linkinde görebileceğimiz gibi Scability aslında her sistemin (bu bir process yada uygulama da olabilir) artan iş yükünü karşılayabilecek şekilde yukarı veya aşağı esnekliğini nitelendirir.

Örneğin yatay büyüme (Scale-Out) bu sisteme üye ekleyerek artan talepleri karşılamayı sağlar. 2 web sunucunuz varken artan kullanıcı sorguları sebebi ile bu sayıyı 4’e çıkarak scale-out yapmış olursunuz.

Ya da dikey büyüme (Scale-Up) bu sistemdeki bir üyeye ek kaynaklar ekleyerek talepleri karşılamayı sağlar. Örneğin var olan sunucu üzerindeki CPU kaynağını yada hafızayı arttırmak aslında dikey bir ölçeklendirme yapmak anlamına gelir.

Yukarıdaki Wikipedia makalesini inceleyenler “Performans tuning versus hardware scalability” bölümünde paralel çalışan sistemlerin performansa etkisinin %70’e kadar çıkabildiğini gösteren Amdahl yasasını fark etmişlerdir.

\frac{1}{\alpha+\frac{1-\alpha}{P}}

Bu formüle göre aynı algoritmik işlem için 4 çekirdek kullanırsak elde edeceğimiz değer 2.105

\frac{1}{0.3+\frac{1-0.3}{4}} = 2.105

Eğer çekirdek sayısını 8’e çıkarırsak

\frac{1}{0.3+\frac{1-0.3}{8}} = 2.581

Görüldüğü gibi çekirdek sayısı iki katına çıktığında hız yaklaşık 5’te 1 oranında arttı. Bu yüzdendaha fazla donanım her zaman uygun yaklaşım olmayabilir.

Microsoft dünyasında da bir sistemin ölçeklenebilirliğini benzer metricler ile anlayabiliriz. Bu yazımızda ise Azure Storage Services için Scabilitiy detaylarına bakıyor olacağız.

İkinci makalemizde belirttiğimiz gibi Storage hizmetlerinden faydalanabilmek için ilk aksiyonumuz bir storage hesabı oluşturmaktı. Bu noktada Microsoft size iki seçenek sunuyordu:

  • Premium Storage
  • Standard Storage

Premium Storage yeni duyurulan bir hizmeti ve HDD yerine SSD diskler sayesinde sanal makineler ile yüksek performans elde edilmesini sağlıyordu. Özellikle I/O duyarlı yükler için tercih edilebilir bir seçenek olarak karşımıza çıkıyordu.

Farklı coğrafi lokasyonlarda değişmekle birlikte örneğin Avrupa ve Asya bölgeleri için Standard Storage Account ölçek limitleri aşağıdaki gibidir:

image

Özetle:

  • Her bir Storage Account kapasitesi içerisinde tüm servisler ile birlikte (blob, table, queue) 500TB’dır. Bu limitin üzerine çıkılması için ikinci bir Storage Account oluşturulması gereklidir.
  • 1KB’lık nesne boyutları ile saniyede toplam 20.000 Entity yada mesaj işlenebilir. Entity ve mesaj detaylarını önceki yazılarımızda incelemiştik.
  • Geo-Redundant ve Locally Redundant replikasyon modelleri için (detayları bir sonraki yazımızda inceliyor olacağız) farklı bant genişliği limitleri bulunmaktadır. Inbound (Storage Account’a gönderilen veriler) Geo-redundant için 5 Gigabit/s Locally-Redundant için 10 Gigabit / s limitleri bulunur. Outbound için ise (Storage Account’dan alınan veri) Geo-redundant için 10 Gigabit/s Locally-Redundant için 15 Gigabit / s limitleri bulunur.

Premium Storage Account için de limitler bulunuyor.

image

Özetle:

  • Premium Storage içerisinde barındırılan VM disklerinin kapasitesi maksimum 32 TB olabilir.
  • Snapshot için kapasite 10TB’dır.
  • Inbound ve Outbound için toplam 50 Gigabits/s limit bulunur.

Görüldüğü gibi Premium hesap ile birlikte yüksek performans dışında daha yüksek ölçeklenebilirlik seviyeleri de sunulmaktadır.

Bildiğimiz gibi Storage Account oluşturulduğunda Blob, Table ve Queue şeklinde 3 farklı hizmete sahip oluruz. Storage hesabı oluştururken seçtiğimiz lokasyon içerisinde (bu lokasyonunda seçimi uygulama performansınız için önem taşır, uygulamanızın yer aldığı lokasyona yakın yada aynı lokasyonda storage account oluşturmanız daha efektif olacaktır) tek bir namespace altında bu 3 hizmet için URI adresleri bize sunulur.

Bu 3 hizmet içerisinde de bloblarımızı, entitylerimizi yada mesajlarımızı barındırabiliriz. Bu noktada en önemli konulardan birisi her bir nesnenin aynı zamanda bir partition anahtarına sahip olduğudur. Bu sayede bu nesnelere erişim sağlanabilir, farklı sunucular üzerinde barındırılmasına rağmen load balancing aksiyonları alınabilir. Bu 3 farklı hizmet için kullanılan partition key detayları aşağıdaki gibidir:

  • Blob = ContainerName + BlobName
  • Entity = TableName + PartitionKey
  • Message = QueueName

Partition anahtarı sayesinde aynı anahtara sahip tüm nesneler aynı grup altında toplanır ve aynı partition içerisinden erişim sağlanır. Aynı zamanda bu partition da bir partition sunucusu üzerinde barındırılır.

Storage hizmetinin bu yapısından dolayı tek bir partition için sahip olduğumuz limitleri bilmemiz uygulama dizaynı sırasında çok önemli oluyor.

Single Queue = Queue içerisinde tüm mesajlar tek bir queue partition tarafından erişilir. Bu partitionda saniyede toplam 2000 mesajı işleyebilir.

Single Table Partition = Table partition aslında aynı partition anahtarına sahip tüm entitylerdir. Bu table partition için throughput limitleri aşağıdaki gibidir:

  • 2000 Entity / Second

Bu limitin partition için olduğunu unutmamak gerekiyor. Table’ın kendisi 20000 entity işleyebilir.

Single Blob = Her bir single blob için 60 MBytes / s limit bulunmaktadır.

Peki yukarıdaki limitlere ulaşıldığında uygulamaya erişen kullanıcılar nasıl bir mesaj ile karşılaşacaklar? Aşağıdakine oldukça benzer:

serviceunavailable

Peki uygulamanız Azure Storage tarafındaki ölçek sınırlarına ulaştığında neler yapılması gerekiyor? Aşağıdaki adımlar izlenebilir:

  • İlk önce bu yoğun kullanıma ve limitlerin aşımına sebep olan yükün analiz edilmesi gereklidir. Tasarımda değişikliğe gidilerek daha az bant genişliği yada kaynak kullanımı sağlanabilir mi kontrol edilmelidir.
  • Verilen tasarım kararına göre aynı zamanda ikinci bir storage account kullanımı düşünülebilir. Aynı Azure Subscription içinde birden fazla storage account oluşturulabilirsiniz. Uygulamanızı da verileri bu iki storage account üzerinde tutacak şekilde tasarlamanız gerekmektedir.
  • Bant genişliği ile ilgili bir limite takılırsanız istemci tarafındaki verinin sıkıştırılması gibi bir yöntemi düşünebilirsiniz.
  • Retry stratejisi belirleyebilirsiniz.

Son madde önemli noktalardan birisi. Uygulamanız Azure storage üzerinde aksiyon alırken Busy yada Timeout mesajları sebebi ile herhangi bir data kaybı, data yazamama problemi yaşamaması gerekir. Bu sebeple SERVER BUSY, TIMEOUT gibi hata mesajları için bir retry stratejisi belirlenebilir.

SERVER BUSY, TIMOUT gibi mesajlarla Azure içerisindeki tümleşik load balancing yapısı gereği de karşılaşabilirsiniz. Örneğin sizin nesnelerinize erişimin arttığı noktalarda Azure kendi içerisinde partitionları farklı bir node’a otomatik olarak taşıyacaktır. Bu anlarda yukarıda bahsettiğim mesajların alınması normaldir. Bunun için aşağıdaki retry stratejilerinden birini kullanabilirsiniz:

  • No Retry
  • Fixed Backoff
  • Exponential Backoff

Bu caching detayları için sözü Fatih’e bırakalım.

“Bazen her şey planlandığı gibi gitmiyor ve “normalde” çalışan bağlantı isteğiniz, bir anda sizi geri dönüşü olmayan bir hataya itebiliyor.
Bu tür hataları almamak için, uygulamanızın bu koşullarda ne yapacağını bilmesi gerekiyor. Yani uygulamanızın bir retry stratejisine sahip olması gerek.

Genellikle kullanılan retry stratejilerinden üç tanesini şöyle sıralayabiliriz ;

1. No Retry

Hazırladığınız ve işlem için gönderdiğiniz verinin, diğer uca ulaşıp ulaşmadığı sizin için hiç önemli değilse bu stratejiyi kullanabilirsiniz.
Çünkü bu strateji ile, yaptığınız işlemin başarısız olduğu durumlarda uygulamanızın hata almasını engelleyebilirsiniz fakat işlemin tekrarı gibi bir şey söz konusu olmaz.

2. Fixed Backoff

Bu strateji ile birlikte uygulamanızın, ne kadar süre aralıkla, en fazla kaç defa tekrar bağlantı kurmayı deneyeceğini belirleyebilirsiniz.
Örneğin ; 30 saniye aralıklarla, en fazla 5 defa bağlantı kurmayı dene gibi bir yaklaşımda bulunabilirsiniz.
Bu stratejinin de dezavantajı var tabi. Bağlantı kurmaya çalışan birden fazla iş parçacığınız varsa, hepsinin aynı anda bağlantı kurmaya çalışabileceğini göz önünde bulundurmanız gerekiyor.
Bu da bizi üçüncü retry stratejisine yönlendiriyor.

3. Exponential Backoff

Bu strateji, başarısızlıkla sonuçlanan her bağlantı sonrasında bekleme süresini biraz daha arttırarak, hem diğer iş parçacıkları ile çakışma sorununu ortadan kaldırıyor
hem de sürekli olarak aynı aralıkla bağlantı kurmaya çalışmadığımızdan dolayı, kurmaya çalıştığımız bağlantının başarı oranını arttırıyor.
Bekleme süresinin belirlenmesi aşamasında kullanılabilecek formül için bir kaç tanımlama yapacak olursak ;

TDefaultBackoff = Normal bekleme süresi
TMaxBackoff = Beklenebilecek en yüksek süre
TMinBackoff = Beklenebilecek en düşük süre
TRetries = Deneme sayısı
TBackoff = Beklenecek süre

Yukarıdaki tanımlamalara göre formülümüzü bu şekilde tanımlayabiliriz ;

TBackoff = Random(0.8 * TDefaultBackoff, 1.2 * TDefaultBackoff) * (2^TRetries – 1)
TBackoff = Minimum(TMinBackoff + TBackoff, TMaxBackoff)

Bu formülü bir C# uygulamasına uyarlamamız gerekirse şöyle bir kod elde edebiliriz ;

int retries = 1;
var defaultBackoff = TimeSpan.FromSeconds(30);
var minBackoff = TimeSpan.FromSeconds(3);
var maxBackoff = TimeSpan.FromSeconds(90);
var random = new Random();

double backoff = random.Next((int)(0.8D * defaultBackoff.TotalMilliseconds),
(int)(1.2D * defaultBackoff.TotalMilliseconds));
backoff *= (Maş.Pow(2, retries) – 1);
backoff = Maş.Min(minBackoff.TotalMilliseconds + backoff,
maxBackoff.TotalMilliseconds);

Leave a Reply

Your email address will not be published. Required fields are marked *

86 − = 84