Tuesday, June 15, 2010

Java Garbage Collection : Unexpected Full GC

I've been digging garbage collection logs these days. We have a production server which suffers long pauses of garbage collection. According to garbage collection log file, there are three different cases that force the JVM to do a StopTheWorld collection:

1. Full GC after a promotion failure: This is the evidence of not enough contiguous space in the old (tenured) generation. The simplest solution to this is increasing heap size. (I am glad it worked for us.)

2. Full GC after a concurrent mode failure (in other words; full promotion guarantee failure) : CMS collector can not catch up with the object allocation speed of the application. If this is the case try setting CMSInitiatingOccupancyFraction to a lower value. CMS will start early but will finish its job on-time.

3. The third one was very difficult for me to figure out. Here is what it looks like: Everything seems to work fine but suddenly Full GC kicks in and 37 seconds of break for application threads.

216066.711: [GC 216066.713: [ParNew: 911554K->71428K(943744K), 0.0860545 secs] 2346321K->1508264K(4089472K), 0.0878449 secs] [Times: user=3.63 sys=0.32, real=0.09 secs]
216066.896: [Full GC 216066.897: [CMS: 1436836K->1003062K(3145728K), 37.8691646 secs] 1527124K->1003062K(4089472K), [CMS Perm : 86016K->31249K(86016K)], 37.8707796 secs] [Times: u
ser=37.64 sys=0.35, real=37.87 secs]
216106.398: [GC 216106.399: [ParNew: 838912K->46337K(943744K), 0.0555625 secs] 1841974K->1049399K(4089472K), 0.0573404 secs] [Times: user=1.33 sys=0.45, real=0.06 secs]
The above log says that in order to clean permanent generation a full collection occured and (86016 - 31249) kilobytes of space freed in permanent generation.

What I've learned today is : The garbage of the permanent generation is only collected by a Full GC. If you see unexpected full collections try expanding permanent generation.

Monday, June 7, 2010

Project Voldemort

Üniversitede bilgisayar mühendisliği okuyup da veritabanları dersini almayan yoktur. Veritabanı dersinde bize ilk öğretilen şey "normalization" oldu, dediler ki :

  • Aman tablolarda tekrar eden bilgiler olmasın. (Primary Keys, unique constraints)
  • Sakın farklı yapıdaki bilgileri getirip aynı tabloya koyma. Herbir farklı yapı için ayrı bir tablo oluşturmaya özen göster.
  • Müşterinin adres bilgilerini Adres tablosuna, sipariş bilgilerini Siparis tablosuna, kişisel bigilerini Musteri tablosuna koy. Denizli'den sipariş veren 30 yaşından küçük müşterileri bulmak için Adres, Sirapis ve Musteri tablolarını çarp (join dediğimiz olay da sonuçta tabloların bir çarpımıdır), işte sana istediğin bilgi.

Bu yöntem yıllarca bilişim sektörünü idare etti. Hala birçok alanda idare etmeye devam ediyor. Bugün Oracle olmasaydı ne yapardık :D Artan ihtiyaçları karşılamak için günümüzde uyguladığımız yöntem, eğer bütçe yeterli ise Oracle'ı çalıştıracak daha güçlü bir makina satın almak oluyor. İşlemci gücünü artırmak için her ihtiyaçta daha güçlü bir makinaya taşınmak gün gelir çözüm olmaktan çıkar. Bu tür durumlarda iş gücünü birden fazla sunucuya dağıtmak gereklidir. İlişkisel veritabanları dağıtık ortamlarda (distributed) beklenen performansı veremeyebilirler, çünkü network üzerinde farklı makinalarda olan sunucular arasında basit bir SQL join'i çalıştırmak çok güçtür.

Web 2.0 ile beraber internet kullanıcılarının içerik paylaşması internet sitelerinin işlemesi gereken bilgiyi inanılmaz derecede artırdı. Bunun en çarpıcı örneği Twitter. Twitter aracılığı ile gönderilen tweet sayısı günde 50 milyon'a ulaşmış durumda. (Saniyede yaklaşık 600 tweet) Depolanan veri miktarı arttıkça bu verileri bölümleyip (partition) birden fazla yerde saklama ihtiyacı ortaya çıkıyor. Milyonlarca kayıt barındırıdan ve farklı disklerde olan tablolar ile yapılan join işlemleri uygulamaların okuma (read) performansını olumsuz etkilediği için NoSQL depolama sistemleri geliştirilmiştir. NoSQL sistemler, RDBMS'lere göre veriyi daha hızlı işlerler, okuma ve yazma performansları RMDBMS'lere göre çok daha iyidir. Bunun diğer bir sebebi de RDBMS'lerin ACID (atomicity, consistency, isolation, durability) kısıtlarını yerine getirmek zorunda olmaları ve bunun için ekstra işlemler yapmak zorunda kalmalarıdır. NoSQL depolama sistemleri RDBMS'leri ortadan kaldırmayacak fakat bazı uygun projelerde RDBMS'lerin yerini alacak gibi gözüküyor.

Bu yazıda LinkedIn'in kullandığı ve duyurduğu bir proje olan Project Voldemort'tan bahsedeceğim. LinkedIn, iş dünyasının facebook'u olarak biliniyor. SSS sayfasında söyledikleri doğruysa 8 Nisan 2010 itibari ile 65 milyon üyesi varmış. 65 milyon üyenin birbirleri ile olan bağlantılarını, iş tecrüblerini, oluşturdukları profesyonel grupları hesaba katınca ortaya inanılmaz boyutlarda bir veri çıkıyor. LinkedIn mühendisleri (eminin onların bizlerden bir farkı yok :D ) bu kadar çok veriyi saklamak ve işlemek için Amazon'un Dynamo'sundan ve memcached'den esinlenerek Voldemort'u yazmaya karar vermişler. Böyle güzel bir projenin ismi neden Voldemort olur onu da anlamış değilim. Bknz : Lord Voldemort

Project Voldemort'un ne olduğunu kısaca şöyle açıklayabiliriz:

  • Dağıtık (distributed) bir sistemdir. Veriler birden fazla lokasyona dağıtılmış saklanabilir.
  • Verilere erişim aynı bir Hashtable'a erişim gibi key'ler ile yapılır. SQL gibi bir sorgulama dili desteklemez.
  • Verileri diske yazar. Sistemin kapatılıp açılması verilerin kaybolmasına sebeb olmaz.

Projesinin ana sayfasında yazan Voldemort'un ne olduğunu anlatan maddeler ise şöyle:

  1. Veriler otomatik olarak sunucu grubundaki diğer makinalara kopyalanır(replicated)
  2. Veriler otomatik olarak bölümlenir (partitioned). İstediğin bir veriyi bulmak için bütün makinalar aranmaz, sadece tek bir makinaya bakarak veriler bulunabilir.
  3. Sunucu grubu içindeki bir sunucunun kapanmasından uygulama etkilenmez.
  4. Veri bütünlüğünü sağlamak için veriler versiyonlanır.
  5. Her bir sunucu diğer sunuculardan bağımsızdır. Single Point Of Failure (bunun türkçe karşılığını bulamadım. Kendim birşey uydurayım dedim beceremedim) SPOF yoktur.
  6. Basit bir arayüzü vardır. SQL desteklemez. Join'leri kod içinde sizin yapmanız gerekir.
    value = store.get(key)
    store.put(key, value)
    store.delete(key)
    
  7. Verileri diskte depolarken ön belliğinden, disk okuması yapmadan, verilere erişim sağlar. Ayrıca bir caching katmanı kullanmaya gerek kalmaz.
  8. Bu çok hoşuma gitti : Verileri depolama katmanı taklit edilebilir(mockable). Unit testler sadece hafızada çalışan bir Voldemort instance ile yapılabilir.

Bu projeyi deneme fırsatım olmadı. En kısa zamanda kurup örnek bir projede kullanıp, izlenimlerimi bu sayfalarda paylaşacağım. Buraya kadar okuduklarınız ilginizi çektiyse projenin sayfasına gidip daha detaylı bilgiye erişebilirsiniz.