Zırvalama Tahtası symfony, debian, PHP5, SQL ve pek çok ayrıntı

30Nov/110

nginx’de mogilefs üzerindeki dosyaların stream edilmesi.

Not: bu yazıyı okuyacak olanların mogilefs ve nginx'in ne olduğunu bildiğini, nginx'i istenilen her modülü aktif ederek yeniden derleyebilecek kadar bilgili kişiler olarak kabul edilir.

nginx ve mogilefs ailemizin birer parçası haline gelmiş durumda, nginx, mogilefs modülü sayesinde daha bir sevimli, bıcır bıcır bir hale geliyor. Tabii bir yere kadar geliyor, Eğer videolarınız ile beraber diğer dosyalarınızı (imageler vs.) mogilefs üzerinde tutmaya karar verirseniz videoların istenilen yere atlanılmasını sağlayan flv ve mp4 eklentilerini kullanamamaya başlayacaksınız. Bu gün bu problemi çözebilmek adına yaklaşık olarak 6 saatimi (belki biraz daha fazla) harcadım ve bunun büyük bir kısmı internette başkalarının bu problemi nasıl çözdüğünü arayarak geçirdim ve bulamadım, bulduğum çözümler ise iş yerindeki mimari için uygun değildi. Hazır çözüm bulamayınc kendi çözümümü üretmek için kolları sıvadım, ilk yaptığım iş bu 3 modülün (mogilefs, flv ve mp4 modülleri) nasıl çalıştığını hatırlamak oldu.

Flv ve mp4 eklentileri sadece dosya üzerinde işlem yapıyorlar, yaptıkları iş basitçe dosyayı aç, X byte ileri git ve içeriği okumaya başla olarak tarif edilebilir.

nginx üzerindeki mogilefs eklentisi ise mogilefs tracker'ından ilgili dosyanın hangi sunucularda ve hangi isimde olduğunun bir listesini alır ve bu sunuculara HTTP üzerinden bağlanarak ilgili dosyanın içeriğini istemciye aktarır.

MogileFS modülü ile derlenmiş bir nginx'in yapılandırma bildirimleri aşağı yukarı şöyle bir şeydir.

location / {
      mogilefs_tracker mog_trackers;
      mogilefs_domain mogilesfdomain.com;
      mogilefs_noverify off;
      mogilefs_pass {
        proxy_pass $mogilefs_path;
        proxy_hide_header Content-Type;
        proxy_buffering off; 
    }
}

Bu yapılandırma bildirimlerinde, ilgili sunucuya ne istek yapılırsa yapılsın bu isteğe ait dosya içeriğini mogilefs'den almasını söyledik. Ben, nginx'i web_dav desteği ile derleyerek mogilefs'deki dosyaların silinmesi, sunulması, yüklenmesi gibi işlemleri de nginx'e yaptırmayı seviyorum. Bu kısımda ki yapılandırma bildirimleride aşağı yukarı şöyle bir şeydir.

server {
   listen 7500;
   location / {
        autoindex                       on;
        root                            /var/mogdata;
     }
 
     location /dev/ {
        autoindex                       on;
        root                            /var/mogdata;
        expires                         modified +120h;
        client_max_body_size            4000m;
        client_body_temp_path           /var/mogdata/dev/temp;
        dav_methods                     PUT DELETE MKCOL;
        create_full_put_path            on;
        dav_access                      user:rw  group:r  all:r;
   }
}

Yukarıdaki ilk yapılandırma bildirimleri sırasında istenen dosya flv yada mp4 ise location bildirimi içerisine mp4; veya flv; yazmak her hangi bir şeyi çözmüyor çünkü ilk bildirim içerisindeki mogilefs_path değişkeni proxy'e geçiriliyor. $mogilefs_path değişkeni içeriği ise şöyle bir şey "http://192.168.1.5:7500/dev/0/00/000/000005.fid", proxy bu isteği yaptığında ikinci yapılandırma bildirimi içerisine girmiş oluyoruz, artık dosyalar ile ilgili işlem yapabiliyor durumdayız ancak bu seferde içerisinde istenen dosyanın flv yada mp4 olup olmadığını ve başlangıç byte'ının ne olduğunu bilemiyoruz, çünkü mogilefs dosya isimlerini ve uzantılarını karışıklığı önlemek adına kendisi düzenliyor bununla birlikte ilk yapılandırma bildirimi içerisinde bu bilgiyi proxy_pass satırında diğer sunuya iletmiyoruz. Her iki sıkıntıyıda çözmek oldukça kolay.

location / {
      mogilefs_tracker mog_trackers;
      mogilefs_domain mogilesfdomain.com;
      mogilefs_noverify off;
      set $start $arg_start;
      mogilefs_pass {
        proxy_pass $mogilefs_path?start=$start;
        proxy_hide_header Content-Type;
        proxy_buffering off; 
    }
}

5 ve 7 nci satıra baktığımızda url'den gelen parametreler içerisinden start olanını yerel bir değişkene set ettiğimi görebilirsiniz, bunun nedeni mogilefs_pass {} bildirimi içerisinde $args ve $arg değişkenlerine erişiminin olmaması. Ardından bu yerel değişkeni "http://192.168.1.5:7500/dev/0/00/000/000005.fid" olarak gelen $mogilesf_path değişkenine ekliyoruz, böylece $mogilesf_path değişkeni şöyle bir hal alıyor "http://192.168.1.5:7500/dev/0/00/000/000005.fid?start=80". (80 kısmı eğer ilk istekde ?start=80 varsa geçerli olacak , aksi halde $start değişkenin değeri boş.)

Artık dosyanın kaçıncı byte'ından okunmaya başlanacağını proxy'e geçirebiliyoruz ancak dosyanın flv veya mp4 olup olmadığını da öğrenmemiz ve bunu proxy'e iletmemiz gerekiyor.

if ($request_filename ~* \.mp4) {
         set $my_type "mpeg4";
}
 
if ($request_filename ~* \.flv) {
         set $my_type "flashvideo";
}

Yukarıdaki yapılandırma bildirimlerini server {} içerisine yazdığımızda istek yapılan dosyanın içerisinde .mp4 veya .flv geçiyor ise $my_type değişkeni set ediliyor. Artık $my_type değişkenini de proxy_pass kısmında kullanabilir ve dosyanın tipini de iletebiliriz. Bu kısımdaki son yapılandırma bildirimleri ile ana dosyamız şöyle bir hal alıyor.

if ($request_filename ~* \.mp4) {
         set $my_type "mpeg4";
}
 
if ($request_filename ~* \.flv) {
         set $my_type "flashvideo";
}
location / {
      mogilefs_tracker mog_trackers;
      mogilefs_domain mogilesfdomain.com;
      mogilefs_noverify off;
      set $start $arg_start;
      mogilefs_pass {
        proxy_pass $mogilefs_path?start=$start&type=$my_type;
        proxy_hide_header Content-Type;
        proxy_buffering off; 
    }
}

Artık, mogileFS'den istenen dosyanın türünü ve hangi byte'ından sonrasının istendiğini biliyoruz. Ancak birinci sorun bu bilgilerin query string olarak iletilmesi ile başlıyor, location içerisindeki regexler sadece $request_uri değişkeni için kullanılıyor ve bu değişken içerisinde query stringler ne yazık ki yok. Dolayısı ile Aşağıdaki gibi bir şey yapamıyoruz.

location ~ flashvideo {
    flv;
    ....
}

Bunun yerine elimizdeki bilgilerden yararlanarak şöyle bir yapılandırma biçimini deneyebiliriz.

server {
   listen 7500;
   location / {
        autoindex                       on;
        root                            /var/mogdata;
     }
 
     location /dev/ {
        if ($args ~* "mpeg4") {
           mp4;
        }
 
        if ($args ~* "flashvideo") {
           flv;
        }
        autoindex                       on;
        root                            /var/mogdata;
        expires                         modified +120h;
        client_max_body_size            4000m;
        client_body_temp_path           /var/mogdata/dev/temp;
        dav_methods                     PUT DELETE MKCOL;
        create_full_put_path            on;
        dav_access                      user:rw  group:r  all:r;
   }
}

Tam bu çalışır diye düşünüp nginx'i reload ettiğinizde göreceksiniz ki size flv ve mp4 bildirimleri burada yer alamaz diyip yeni config dosyasının yüklenmesine izin vermeyecektir, ve tam olarak bu nokta dananın kuyruğunun koptuğu yer.

Dökümantasyon, mp4 ve flv bildirimleri sadece location içerisinde kullanılabilir ve bu bildirimler hiç bir parametre almadıklarını söylüyor. O halde bir şekilde bu bildirimin if içerisinde de bulunabilmesine izin verebilirsek istediğimiz şeyi yapabiliriz.

Nginx kaynak kodu üzerindeki modül yapısında, modülünüze ait bildirimlerin diğer hangi bildirimlerin/bölümlerin içerisinde yer alabileceğini belirtmelisiniz.

Bu bildirimler flv modülü için src/http/modules/ngx_http_flv_module.c dosyasında aşağıdaki gibi yer yer almakta.

static ngx_command_t  ngx_http_flv_commands[] = {
 
    { ngx_string("flv"),
      NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
      ngx_http_flv,
      0,
      0,
      NULL },
 
      ngx_null_command
};

mp4 modülü için ise nginx_mod_h264_streaming/src/ngx_http_streaming_module.c dosyasında aşağıdaki gibi yer yer almakta.

static ngx_command_t ngx_streaming_commands[] =
{
  {
#ifdef BUILDING_H264_STREAMING
    ngx_string("mp4"),
#endif
#ifdef BUILDING_SMOOTH_STREAMING
    ngx_string("ism"),
#endif
    NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
    ngx_streaming,
    0,
    0,
    NULL
  },
  ngx_null_command
};

Her iki bildirim içerisinde yer alan "NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS" satırını "NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_NOARGS" ile değiştirdikden sonra nginx'i yeniden derleyerek flv ve mp4 bildirimlerinin location içerisinde bulunan if deyimi içerisindede aktif hale getirilebilmesini sağlayabiliriz.

Derleme işlemi sonrasında yukarıdaki tüm yapılandırma bildirimlerinin çalıştığı ve düzgünce stream yapabildiğimiz bir mogilefs destekli nur topu gibi bir nginx'imiz olacak.

(Yazar) e bebeğim eee, eeee, eee, e bebeğim eee, eeee, eee,
(Günlük) Elinin köüüü körüü körüü, Elinin köüüü körüü körüü...

5Aug/110

Coolshark’a yeni çalışma arkadaşları arıyoruz

Yaklaşık 6 aydır çalıştığım coolshark'a yeni çalışma arkadaşları aramaktayız, aradığımız nitelikleri http://goo.gl/Nud9B adresinde bulabilirsiniz; ilanı okudukdan sonra kendinize neden çalışayım ki diye sorabilirsiniz, benimkiler aşağıda belki sizin de ilginizi çeker.

Coolshark'da biz developerların işi yeni projeler geliştirmek ve geliştirilmiş olan ürünlerin devamlılığını sağlamak, gerek devamlılığı sağlanan ürünlerde gerekse geliştirilecek olan ürünlerde geliştirmeye başlamadan önce yeni teknoloji / dil, araç, kütüphane vb. canımızın istediğini deneyip testler/prototipler geliştirip yola kendi tercihlerimiz ile devam edebiliyoruz, projelerin ihtiyaçları karşılandığı sürece hiç kimse "biz bunu kullanıyoruz" demiyor ve bu karar tamami ile biz developerlar'a ait.

Bu durum Coolshark'ı mevcut teknoloji şirketlerinden ayırmakta, gerek yönetim gerekse takım içerisinde teknoloji, yöntem baskısı olmadığından daha rahat bir şekilde işimizi yapabiliyoruz, aynı zaman da kendimizi de yeni teknolojiler konusunda eğitebiliyoruz, öğrenip deneyim kazanabiliyoruz.

Bazen çok basit mikro projeler geliyor karşımıza, bu mikro projeler bizim dikkatimizi dağıtıyormuş gibi görünse de aynı zamanda uzun bir süre aynı proje ile uğraşmanın vermiş olduğu stress'i üzerimizden alıyor ve kafamızın biraz olsun rahatlamasını sağlıyor.

Coolshark'da tüm projeler şirketin kendi içerisinde kullanılmak üzere hazırlanıyor dolayısı ile sadece ve sadece kendi ihtiyaçlarımızı karşılıyoruz, bu bizleri "müşteri kaprisi"nden uzaklaştırıyor ve daha sakin geliştirme yapmamızı sağlıyor. Yukarıdaki cümleden sadece intranet uygulamaları geliştirdiğimizi düşünmeyin sakın.

Pycon videolarını izlerken kimse gelip "ne o, video mu izliyorsun?" gibi kinayeli sorular sormuyor.

Yemek sonrası ufak şekerlemeler yaparken bir gözü açık bırakıp hafif kulak kabartarak "aman patrona yakalanmayayım" diyerek şekerlemenin büyüsünü bozmuyoruz, 30/45 dakikalık şekerlemelerin kimseye zararı olmadığını biliyorlar.

Denemekden korkmuyoruz, korkanı ise alıştırıyor ve korkmamasını sağlıyoruz.

Şirket geliştirilen yazılımların open source edilmesine pek alışkın olmamasına rağmen arada ufak tefek şetleri open source ederek yönetimi buna alıştırıyoruz. :) (Bkz: )

Aile büyükleriniz için: Hafta sonu tatil, sigortası var, sabah 9 akşam 6, yemek var, maaş gününde yatıyor, özel sağlık sigortası bile var.

Benim nedenlerim sizi tatmin etmedi ise siz yinede gelin görüşelim belki tatmin edici bir cevap bulabilirsiniz.

Not: Bu blog girdisinde yer alan nedenlerim kısmını şu anda fark etmediğim, unuttuğum şeyler için zaman zaman güncelleyebilirim.

Filed under: Genel No Comments
5Jul/112

fibonacci, peki ama neden?

Pek çok iş başvurusunda her ne hikmet ise recursive (öz yinelemeli) fonksiyon olarak ya faktoriyel ve/veya fibonacci sayılarını bilmem kaçıncı milyon kez yeniden yazmanızı isterler. Aslında bu soruyu soran kişi/firmaların derdi "acaba fibonacci sayılarını biliyor mu?", "faktoriyel'i biliyor mu?", "acaba bunları yazabiliyor mu?" değildir. İş görüşmesi yaptığınız kişinin zihninde yukarıdaki sorulardan her hangi birinin varlığından şüpheleniyor iseniz şu soruyu sorabilirsiniz: "Recursive fonksiyon örneği için daha yaratıcı bir probleminiz yok muydu?" veya "Neden fibonacci/faktoriyel?" veya "Recursive fonksiyon yazıp yazamadığımı mı merak ediyorsunuz?" Kısaca tüm ukalalığınızı takınabilirsiniz.

Google, Facebook, Last.fm, Yahoo! gibi büyük firmalar size fibonacci sorarken merak ettikleri tek şey recursive işleminizin "tail call" olup olmadığıdır, öz yinelemeli fonksiyon yazıp yazamadığınız değil.

Tail call'ı anlamak için önce programın çalışma esnasında bir fonksiyonların çalışmasını anlamamız gerekiyor.

fonksiyon hede(){
   var hodo:sayı = hodo(); 
 
   döndür hodo == 1 ? 'eşit' : 'değil ki - tekrar dene'; 
}
 
yaz hede();

Eğer yukarıdaki kod çalışsa idi hede fonksiyonunun ilk satırındaki hodo çağrısı yapılmadan önce; hodo'nun çalışması bittiğinde döndüreceği değerin nereye yazılacağı Call Stack (Yığın) içerisine yazılacaktı. hodo çalışmasını bitirdiğinde ise geri döndürdüğü değerin yazılacağı adres bilgisi stack içerisinden okunacak ardından değer bu adrese yazılacak ve Stack üzerinden adres bilgisi silinecekti. (Şu Stack'in çektiği çileyi kimse çekmedi).

Peki aşağıdaki kod çalıştığında ne olur?

fonksiyon hodo(){
   döndür 'boş fonksiyon';
}
 
fonksiyon hede(){
   döndür hodo(); 
}
 
yaz hede();

Doğru bildiniz; Ekrana boş fonksiyon yazar ve program sonlanır, ancak hede içerisinden çağrılan hodo fonksiyonunun dönüş değeri ile ilgili her hangi bir işlem yapılmadığından ve en son deyim döndür olduğundan Stack içerisine hodo'nun döndüreceği değer olarak yaz deyiminin olduğu adres verilir. Yukarıdaki kod ile aşağıdaki kod aynıdır.

yaz hodo();

Stack üzerinde yapılan ve programın verimini arttıran bu yönteme Tail call denir. Bir örnek daha vereyim:

fonksiyon hodo(){
   döndür 'boş fonksiyon';
}
 
fonksiyon hede(){
   var dönenDeğer:Dizi = hodo();
 
   döndür dönenDeğer; 
}
 
yaz hede();

Yukarıdaki örnek ile bir önceki örnek arasında bir fark yoktur, hodo fonksiyonunun dönüş değeri bir değişkene atanmış gibi gözükse de derleyici bu kodu derlerken hodo'nun dönüş değeri ile ilgili bir işlem yapılmadan döndürüldüğünden optimizasyon sırasında yaz hodo(); haline getirecektir.

Yazılımcıların pek çoğu Tail call'ı dikkate alarak yazmazlar, açıkcası bu tür bir optimizasyon'a (erken optimizasyon) daha iş bitmeden başlamak çok da sağlıklı değildir diye düşünüyorum, tabii alışkanlık haline getirirseniz o ayrı. Fakat recursive bir fonksiyon yazıyorsanız ve tail call'ı dikkate almaz iseniz yeterince büyük değerler verildiğinde stack'i dolduracak ve programınız çalışamaz duruma gelecektir. Aşağıdaki python kodu siz değer olarak 100 verdiğinizde oldukça uzun zaman alacaktır, bunun en büyük nedeni stack üzerinde bir şeyler yazmanın zaman gerektirdiğidir. Her yazma işlemi sırasında ram üzerinde bir yerin ayrılması işlem bittiğinde ise bu bölgenin boşaltılması gerekmektedir. Her fib fonksiyonu çalışmasını bitirebilmek için bir sonraki fib fonksiyonunun dönüş değerine ihtiyacı vardır.

# bu kod ile google'a giremezsiniz.

# t.py
def fib(n):
    if n < 2:
        return n
    else:
        return fib(n - 1) + fib(n - 2)
 
print fib(20)

Aynı işi yapan tail call optimizasyona uygun kod ise:

# t2.py
def fib(i, current = 0, next = 1):
  if i == 0:
    return current
  else:
    return fib(i - 1, next, current + next)
 
print fib(20)

Yukarıdaki kod daha büyük değerleri çok daha hızlı hesaplayacaktır çünkü derleyici bu kodu optimize edip derlerken son satırda bulunan return deyimindeki fib'in dönüş değeri üzerinde her hangi bir işlem yapılmadığını görecek ve tail call optimizasyonu uygulayacaktır, bu durumda stack'e sadece print ifadesinin olduğu adresin yazılması ve ardından kod çalıştırılması demektir. stack üzerinde daha fazla işlem yapılmadığından çalışma performansında artış ve ram kullanımında düşüş olacaktır. Her iki kodu çalıştırdığımızda:

timu@chewie:~$ time python t.py 100
^CTraceback (most recent call last):
  File "t.py", line 14, in <module>
    main()
  File "t.py", line 13, in main
    print(fib(limit))
  File "t.py", line 9, in fib
    return fib(n - 1) + fib(n - 2)
  ....
KeyboardInterrupt
 
real    6m42.400s
user    6m35.869s
sys     0m0.180s
timu@chewie:~$  #Beklemekden sıkıldım

# bu kod ile google şansınızı biraz arttırabilirsiniz.

timu@chewie:~$ time python t2.py 100
354224848179261915075
 
real    0m0.013s
user    0m0.000s
sys     0m0.012s
timu@chewie:~$
timu@chewie:~$ time python t2.py 997
10261062362033262336604926729245222132668558120602124277764622905699407982546711488272859468887457959087733119242564077850743657661180827326798539177758919828135114407499369796465649524266755391104990099120377
 
real    0m0.015s
user    0m0.008s
sys     0m0.012s
timu@chewie:~$

Tail call basitçe bir fonksiyonun en son satırında bulunan return deyimine ait değerin eğer bir fonksiyon tarafından üretilmiş ise her hangi bir işlem uygulamadan (mantıksal veya matematiksel) geri döndürülmesidir (yazılımcının müdahale edeceği seviye).

Büyük firmalar size fibonacci veya faktoriyel sorarken büyük değerler ile recursive fonksiyonlar çalıştırıldığında stack'de meydana gelecek olan büyümeyi ve zaman kaybını dikkate alıp almadığınızı görmekdir ve kodunuzun tamamına değil sadece son satırına bakarlar. Elbette bu firmalar sizin bu algoritmaları ezberinizden yazdığınızı düşündüklerinde tail call optimizasyon nedir diye sorabilirler, bu nedenle çözümleri ezberlemeyin gidin wiki pedia maddesini okuyun ve bu okudunuzdan daha iyi bir makale yazın.

(Yazar) Sevgili Günlük...
(Günlük) Sevgili yazarcık.
(Yazar) Çok oldu görüşmeyeli,
(Günlük) Yaaa di mi, yokluğun hiç hissedilmedi inan ki.

11Nov/101

Cassandra ve Hdfs

Hdfs sektörde dağıtık dosya sistemleri arasında nerede ise standartlaşmıştır, bunun en büyük nedenlerinden biri map/reduce ile çok büyük dosyalar üzerinde işlem yapılmasına izin vermesidir. Ancak hdfs'in mimarisinde bulunan namenode yani dosya erişim bilgilerini tutan ve dosya ile ilgili bir işlem istedindiğinde bu bilgilere erişim sağlayan fiziksel makinenin varlığı beni olmadık biçimde rahatsız etmekte, zira bu makineye erişim kesildiğinde tüm sisteme erişim kesilmekte, elbetteki namenode'un yedeğini alıp failover yapmak mümkün (secondary namenode) ancak bu donanıma ait fiziksel sınırları rahatlatmamıza yardımcı olmuyor, disk büyüklüğü kadar metadata tutabilmek, metadataya disk erişim hızlı kadar hızlı erişebilmek vs.

Uzun zaman önce gelen deliliklerden biri kendime ait bir dağıtık dosya sunucusu yazmakdı, bu dosya sisteminin mevcut sistemlerden farklı olduğu nokta, merkezi bir metadata sunucusuna ihtiyacının olmaması idi. Metadata ve dosya içeriğini tutmak için düşündüğüm yöntemlerden biri cassandra'nın iç mimarisini kopyalayarak her node'un hem bir metadata sunucusu hemde fiziksel dosya sunucusu haline getirmek, metadata ve dosya içeriğini replika ederek Spf'i ortadan kaldırmak. Bir diğeri ise metadata'yı tamamen cassandra'ya yazmak ve oradan okumak böylece kalan tüm zamanı datanode'ları oluşturmak için kullanmak (yazılımı geliştirmek için harcanacak olan süreden bahsediyorum elbette)

Ardından datanode'ları yazmak yerine mevcut yazılmış olan bir dosya sistemi üzerinde yapılacak değişiklikler ile metadata sunucusunu aradan çıkartmak ve böylece mevcut sistem için yazılmış tüm istemci kütüphanelerinden, mevcut yazılımlardan faydalanabilme fikri ağır basmaya başladı ve Hadoop'u ve dolayısı ile hdfs'i bu amaçla kullanmayı düşünmeye başladım, bu sayede hadoop farklı kullanım alanları da elde edebilir diye düşünmekteyim.

Oturup bir kaç basit akış şeması çıkarttığımda bu işin bazı problemleri olduğunu fark ettim, bunlardan en önemlisi dosyaya yapılan ekleme işlemlerinin sırasının ne olacağı idi.
Eğer merkezi bir metadata sunucunuz yok ise istediğiniz her hangi bir makineye bağlanıp bu dosyayı eklemek için aç demek ve eklemek istediğiniz içeriği ekleyip dosyayı kapatmanız veya en azından flush diyerek içeriğin mevcut dosya bilgisi ile merge edilmesini sağlamalısınız. Tabii iki farklı node'a gelecek iki farklı komut (ekleme yapmak için aç, yazmak için aç (mevcut içeriği siler)) veya iki farklı makineye gelecek olan iki farklı içerik ekleme komutunun ana dosyaya hangi sıra ile merge edileceği, dosya lock işlemleri vs gibi bir takım tek başıma cevap vermemin anlamsız olduğu bir takım sorular ile karşılaştım.

Ne yazıkki yukarıdaki soruların büyük bir kısmı sektördeki farklı ihtiyaçlar için farklı cevapların verileceği türden sorular. Bu nedenle siz bu yazıyı okuyan yazılımcılara sormak istiyorum: dağıtık bir dosya sisteminde yapılan dosyaya yazma, ekleme, silme işlemleri için ne türden senaryolar üretebilir ve tavsiyeler verebilirsiniz?

Elbette bu yazıyı okurken böyle bir yazılımı şu anda geliştirdiğimi veya yakın zamanda geliştireceğimi düşünmeyin. Bu yazıyı sadece fikir alışverişi yapabilmek amacı ve saksıları çalıştırmak için hazırladım. Yorumlarınızı esirgemeyin.

(Yazar) Sevgili günlük.
(Günlük) ....
(Yazar) Başlıkla ilgisizmi oldu ne?
(Günlük) ....

24Aug/100

Topluluk ve tasarım

Aşağıdaki yazıyı 2007 Kasım'ında yazmaya başlamış ancak daha sonra ilerde devam ederim diyerek taslak halinde bırakmışım, arşivde gezerken fark ettim ve yarımda olsa yayınlamaya karar verdim. Kime, ne yardımı dokunur bilemiyorum. Katkıda bulunabilir, devamını getirebilirsiniz.

Grafik ve tasarım birbirinden ayrılmaz ikili olsa da her ikise birbirinden farklı işlerdir. Grafik oranı düşük ancak temiz öğeler ile hazırlanmış pek çok site dolaşıyor etrafta ve bunların pek çoğu web 2.0 kavramı ile hareket ediyor. Hayır öyle janjanlı değil sadece temiz yalın tasarımları olan ve içeriği kullanıcıların belirlediği siteler. Peki 10 sene önceki web'te olup bitenler ve bugün'ü karşılaştırdığınızda neler görüyoruz? Yani akımları ve modaları kastediyorum. 10 sene önce arkaplanda yanıp sönen yıldızlar vardı. her taraf sadece hareketli olsun diye donatılmış gifler vardı. Ve insanlar internette bir şeyler yapmaya çalışıyordu (I kiss you). Kimileri başardı kimileri başaramadı. Başaranlar yeni akımlar oluşturmaya veya onları takip etmeye devam ettiler. 10 sene öncesinin arka planında ışıklar, yıldızlar olan siteleri hala ayakta ve bir taraftada yeni anlayışın oluşturduğu siteler var. Peki kimler hangilerini tercih ediyor ve neden?

Kişisel olarak izlenimim dünün genç olanları, geliştirdikleri unsurları/fikirleri kullanan siteleri tercih ettiği yönünde. Yani eğer 25 yaş üstü iseniz ve şimdiye kadar sadece chat yapmak yerine bir takım oluşumları takip etmiş bir şeyler yapmak için çaba göstermiş iseniz içeriği kendinizin belirleyebildiği siteleri tercih ediyorsunuz.

Oysa dünün orta yaşlıları veya interneti hatun/erkek düşürme mekanı için sınırsız bir kaynak olarak görenler hala arka planında yıldızlar parlayan siteleri tercih ediyorlar çünkü ne kendilerini ne de içinde bulundukları toplulukları geliştirmek yönünde hiç bir çaba gösterme gereği duymadılar. Zevkleri ve görüşleri aynı kaldı. İnce ve temiz tasarıma sahip toplulukların içerisinden zeki ve kültürlü insanların bulunması, hala bir şeyler yapma isteğini dürtükleyen ve bunun için çaba gösteren insanların bulunmasından ibaret.

Filed under: ben, Bu ne?, Genel, Kişisel No Comments
7Jul/100

Web site istatistikleri ve collectd

Bir web sitesinin büyüme istatistiklerini oluştururken ilk başlarda bir hesap tablosu yeterli olabilmektedir ancak büyüme devam ettikçe bu hesap tablosu anlamsızlaşacak ve büyümenin yavaşladığı veya düştüğü zamanlar için geriye dönük izleme yapmak zorlaşacaktır, bu durumda patronlara elimizdeki veriyi daha anlaşılır cicili bicili grafikler çıkartacak ufak tefek yazılımlar hazırlamak bizim en önemli vazifemiz haline gelir ve buda takdir edersiniz ki sıkıcı bir iştir.

Yıllardır pek çok farklı ürün/servisde kullanılan rrd ve rrd araçları bu sıkıcı işleri bizim yerimize yapabilir ancak bu seferde rrd komutuna ait parametreleri neyi nasıl yaptığını öğrenmemiz, deneyimlememiz gerekiyor. Elbette rrd ve araçlarını kullanan farklı uygulamalar için eklentiler yazabilir ve ağ trafiği, bağlantı durumları, yük vb. sistem izleme araçlarının yanında kendi yazılımımız için grafikler oluşturulmasını sağlayabiliriz. Aşağıda collectd isimli veri toplama aracının dbi modülünü kullanarak bir web sitesinin kullanıcıl sayılarını alan ufak bir yapılandırma mevcut. Aşağıdaki yapılandırma sayesinde günlük kullanıcı sayısındaki ivmeyi görebiliyoruz. Birde buna her gün kayıt olan kişilerin sayılarını eklersem eğer tam olacak :)

<Plugin dbi>
  <Query "users">     
     Statement "SELECT count(*) as count FROM users" 
     <Result>
      Type "gauge"
      InstancePrefix "users"
      #InstancesFrom "count"
      ValuesFrom "count"
    </Result>
  </Query>
  <Database "xxxxx">
    Driver "mysql"
    DriverOption "host" "xxxxxxx"
    DriverOption "username" "xxxxxxxxx"
    DriverOption "password" "xxxxxxxxx"
    DriverOption "dbname" "xxxxxxxxx"
    SelectDB "xxxxxxxxxx"
    Query "users"
  </Database>
</Plugin>

bu yapılandırmanın ürettiği grafik ise aşağıda

kullanıcı sayısı

(Yazar) Sevgili günlük
(Günlük) .....
(Yazar) Yeniden biçimlerdir beni.
(Günlük) Mucizeler departmanı yukarda canım.

Filed under: Genel No Comments
14Jun/102

Lucene + Zemberek + Cassandra = Lucandra

Lucandra, lucene indexlerini Cassandra'ya yazarak indexlerin birden fazla makinede tutulmasını (partition ve replication) sağlıyor, böylece ölçeklenebilir lucene indexleri elde edilerek arama için tek noktada oluşacak bir hata ile arama özelliğinin devre dışı kalmasının önüne geçiliyor.

Bir süredir bu proje benimde ilgimi çekiyordu ancak Türkçe için yeterli değildi, zira Türkçe için kök bulmak problemli idi ve buna bir çare bulmak gerekiyordu, 2 sene önce açık kaynak günlerinde bu konu ile ilgili olarak yapılan çalışmalar arasında kök bulucu olarak zembereğin kullanıldığını duymuş ve kök bulmak için neden lucandra'ya zemberek eklemiyorum diye kendi kendime sormaya başlamıştım. İlk bir kaç günlük tırsıntı ve kaynak kod okuma ile geçen zaman sonrasında projenin forklanması ve toplam 3 ana commit ile (daha fazla commit'im var ama içlerinden 3'ü önemli) zemberek kök bulucu sistemi projeye eklenmiş oldu. Sadece lucene ile değil solr ile de kullanılabilinir durumda. Zembereğin eklenmesi için yaptığım değişiklikler, 1 satır silmek, 5 satır eklemek, 1 satırda değişiklik yapmak, 3 adet kütüphaneyi (zemberek-cekirdek, trlucene, zemberek-tr) lib dizinine eklemekden ibaret, tabii birde kök bulucu içerisinde yer alan "gereksiz kelime"lerin güncellenmesi var.

Şimdilik sadece Türkiye Türkçesi ile çalışıyor ancak Azeri Türkçesi ve Türkmence Türkçesi için de çalışmasını isterseniz tek yapmanız gereken bu iki dile ait zemberek kütüphanelerini lib dizinine eklemek ve başlangıç sırasında -Dlucene.analyzer= parametresi ile hangi dil için analiz yapılacağını belirtmenizden ibaret.

Henüz Türkçe desteği mainstream'de yer almıyor (pull request gönderdim, bekliyorum), eğer denemek isterseniz http://github.com/selam/Lucandra adresinden kaynak kodları alabilirsiniz.

Sonradan gelen edit: kodlar github üzerinden kaldırılmıştır.

Filed under: Genel 2 Comments
30May/101

Düşünüyorum

V8'in gayet güzel bir arabirimi var, nodejs projesinden de görülebileneceği gibi geliştirme yapmakda oldukça kolay, javascript oldukça yaygın bir dil ve geniş bir kullanıcı ağı var.

Peki, acaba linux kerneli ve V8 javascript engine kullanan bir işletim sistemi olamaz mı? Linux boot sürecinde init yerine V8'i çalıştırsa ve V8 her bir terminal için javascript shell çalıştırsa? şu anda V8'in shell erişimi sadece bir kaç ufak tefek komutdan oluşuyor ancak güzel bir geliştirici katkısı ile hazır haline geçmesi pek sorun olmaz diye düşünüyorum. sistem üzerinde bulunacak tüm kütüphaneler sadece ve sadece kernel'in ve V8'in ihtiyaç duyacağı kütüphaneler olsa, bir JsOS'un prematüre hali olabilir mi?

(Yazar) - Sevgili günlük
(Günlük) - Her istediğinde hatırlayacağın birimiyim ben?
(Yazar) - ...........
(Günlük) - Aleyhinde kullanacağım

8Apr/104

Cassandra

Cassandra, facebook tarafından geliştirilmiş ve daha önce amazon tarafından geliştirilen Dynamo ile benzer yapıya sahip yapısal key/value veri tabanıdır. Cassandra veri modeli oldukça basittir, geliştirme yapmak oldukça hızlı ve eğlencelidir. Ancak eğlenmeden önce Cassandra'nın veri modellerini anlamak gerekiyor.

Cassandra veri modeli için Keyspace, ColumnFamily, Column, Key, SuperColumn tanımlarını kullanır.

Keyspace

Keyspace basitçe verinizin ait olduğu veri tabanı adıdır diyebiliriz. Keyspace'ler kurulumunuzun yapılandırmasına bağlı olarak /var/lib/cassandra/data/ dizini içerisinde bulunurlar. Eğer SQL'e aşına iseniz SQL'de bulunan veri tabanı adı ile Cassandra'da bulunan Keyspace'in aynı anlama geldiğini düşünebilirsiniz. SQL'den farklı olarak istemciden Cassandra'ya açılan bağlantının hangi veri abanına ait olduğunu söyleyemezsiniz, başka bir değiş ile "use dbname" gibi bir deyim Cassandra üzerinde bulunamamaktadır bu nedenle verilerinizi yazarken veya okurken verinizin hangi "Keyspace" üzerinde olması gerektiğini Cassandra'ya söylemelisiniz.

ColumnFamily

ColumnFamily ise, Keyspace içerisinde bulunan gruplanmış verileri taşıyan tablo olarak düşünülebilirsiniz. Bu tablolar verinize ait satırları tutmaktadır. SQL'den aşina olduğumuz sütünların row haline gelmiş halleri olarak düşünebilirsiniz ancak SQL'de tablo tanımı sırasında belirtilen sütünların ColumnFamily içerisinde tanımlanmasına gerek yoktur. Bir Keyspace içerisinde istediğiniz kadar ColumnFamily tanımlayabilirsiniz.

Key

Key'ler ColumnFamily içerisinde bulunan ve SQL'deki primaryKey alanına denk gelen, verilerinize erişmek için kullanabileceğiniz alanlardır. Key içerisinde bulunan veriler Cassandra için birer "Satır"dır. Ancak bu satırların SQL'de bulunan satırlar olmadığını hatırlamalısınız. Bir ColumnFamily içerisinde istediğiniz kadar Key barındırabilirsiniz.

Column

Column ise Key'lerin içerisinde bulunan sütunlardır. Bu sütunların yapısı SQL'de bulunan sütünlardan oldukça farklıdır. sütunlar içerisinde 3 adet alan bulunmaktadır. Bu alanlar "name", "value" ve "timestamp"'dir. Tabloların SQL'deki gibi bir yapısı olmadığını söylemiştik, işte bu nedenle Column içerisinde name ve value değerleri bulunur timestamp ise karışıklığı önlemek içindir. Tüm yapının anlaşılması için her hangi bir dilde bulunan Array veya hash dictionary'den faydalanabiliriz. Bir key içerisinde istediğiniz kadar Column tanımlayabilirsiniz.

  $Keyspace = Array(
    'ColumnFamily' => Array(
      'Key' => Array(
        'ad' =>  Array('value' => 'Timu', 'timestamp' => '1270663849'),
        'soyad' => Array('value' => 'Eren', 'timestamp' => '1270663849')
      )
    )
  );
  Keyspace = dict(
    ColumnFamily = dict(
      Key = dict(
          ad = dict('value' = 'Timu', 'timestamp' = '1270663849'),
          soyad = dict('value' = 'Eren', 'timestamp' = '1270663849')
      )
    )
  )

Column içerisinde yer alan timestamp, verinin tutarlılığını sağlamak için bulunmaktadır ve istek sırasında istemci tarafından gönderilir. Eğer Cassandra'ya aynı anda verinin güncellemesi için iki istek gönderilir ancak bu isteklerden biri network problemleri veya farklı data center'lardan gönderilmesi nedeni ile cassandra'ya geç ulaşır ise timestamp değeri mevcut verinin üzerinde bulunan timestamp değerinden küçük olacağından güncelleme işlemi yapılmayacaktır. Cassandra üzerindeki verilere "Key"'ler ile ulaşabileceğinizi söylemiştik, isterseniz bir Key içerisinde bulunan Column'lardan tümüne veya bir kısmına erişebilirsiniz.

SuperColumn?

SuperColumn'ları Key'ler içerisinde bulunan bir ColumnFamily olarak düşünebilirsiniz. ColumnFamily cassandra yapılandırma dosyasında (storage-conf.xml) tanımlanır ve SuperColumn olup olmadığı ColumnType öz niteliğinde belirtilir.
ColumnType değeri "Super" verilir ise o ColumnFamily bir SuperColumn demektir. Burada dikkat etmeniz gereken ColumnFamily'i SuperColumn olarak tanımladığınızda Key içerisinde Column türünden veri barındıramazsınız.

  $Sinelist = Array( // Keyspace
    'Users' => Array( // ColumnFamily 
      'timu' => Array( // Key
        'information' => Array( // SuperColumn, similar as ColumnFamily
          'ad' =>  Array('value' => 'Timu', 'timestamp' => '1270663849'), // Column
          'soyad' => Array('value' => 'Eren', 'timestamp' => '1270663849') // Column
        ),
        'images' => Array( // another SuperColumn
          'thumbnail' => Array('value' => 'http://static.sinelist.com/users/xxxx.jpg', 'timestamp' => '1270663849')
        ) 
      )
    )
  );

Tıpkı ColumnFamily içerisinde ki Key'e ait sütunların tamamına veya bir kısmına nasıl erişiyor isek aynı şekilde SuperColumn için de erişebiliriz. SQL'de bulunan tablolar arası ilişkiler Cassandra üzerine bulunmamaktadır, dolayısı ile veriler arası ilişkileri uygulama tarafında halletmek zorundasınız.

Sıralama

Şu ana kadar Cassandra'nın verileri nasıl organize ettiğini anlamış olduk. Ancak dikkat etmemiz gereken bir unsur daha var ki o da
sıralama. Cassandra'ya verinizi insert ettiğinizde nasıl bir sıralama ile tutması gerektiğini söyleyebilirsiniz, böylece veriyi her zaman sıralı olarak alabilirsiniz.

Cassandra üzerine bulunan Column'lar her zaman Column adına göre sıralı olarak tutulur. Bu sıralama storage-conf.xml dosyasında ColumnFamily tanımlanırken CompareWith öz niteliğine verilen değer ile belirlenir, cassandra ön tanımlı olarak BytesType, UTF8Type, LexicalUUIDType, TimeUUIDType, AsciiType, ve LongType sıralama methodlarını kullanabilir. Bu yöntemlerden her hangi birini seçtiğinizde dikkat etmeniz gereken şey Column adlarının belirtilen sıralama algoritması ile uyumlu olması gerektiğidir. Eğer CompareWith parametresi LongType ise sütun adları 64bit (8 byte) integer uzunluğunda olmak zorundadır. Sıralama her zaman büyükten küçüğe doğrudur.

Eğer ColumnFamily bir SuperColumn ise CompareWith parametresi Key isimlerini etkiler ve sıralama key'ler için yapılır. Sütünların sıralanması ise CompareSubcolumnsWith parametresi ile belirtilir.

Sayfalama

Cassandra ile çalışırken sayfalama yapmak istediğinizde ortaya bir sorun çıkar, bu sorun SQL'den alışık olduğumuz offset değerinin sayısal bir değer olmamasından kaynaklanır, Cassandra için offset değeri bir key olmalıdır, Cassandra'ya şu kadar kayıttan sonra bana 10 tane kayıt ver diyemez ancak şu kayıt ile birlikte bana 10 tane kayıt ver diyebilirsiniz. Burada dikkat etmeniz gereken cassandra size verdiği veriler içerisinden belirtmiş olduğunuz key'e ait verilerin de bulunduğu bu yüzden sayfalama yaparken eğer başlangıç değeri verilmemiş ise 10 tane verilmiş ise 11 tane kayıt çekip başlangıç bilgisi verilen kayıdın kullanıcıya döndürülecek cevap içerisinden çıkartmanız gerekebilir.

Replikasyon

Cassandra içerisinde replikasyon için 3 adet yöntem bulunmaktadır, bunlar RackUnaware, RackAware ve DatacenterShard'dır. Rackaware eğer birden fazla data center ile çalışıyor ve içerisinde kendimize ait bir network kurabilmiş isek faydalıdır aksi halde bir anlamı yoktur. Bir Cassandra makinesine gelen yazma isteği replikasyona tabii tutulacak ise replikasyon methoduna göre bir node seçilir eğer data center içerisinde her hangi bir network kurulumuna gitmemiş isek RackUnaware yöntemi bizim için kullanışlı olacaktır. Bu yöntem Cassandra kurulumlarından bir tanesini rastgele seçerek replikasyonu gerçekleştirecektir. Eğer kendi networkümüz var ve replikaslikasyon yöntemi olarak RackAware kulanırsak, cassandra replikasyon için önce farklı bir data center da bulunan bir node aramaya çalışır bulursa replikasyonu gerçekleştirir, eğer bulamaz ise bu sefer aynı data center içerisinden farklı bir rack bulmaya çalışır ve bulursa replikasyonu gerçekleştirir. Cassandra data center'ları ve Rack'leri bulabilmek için gossip (dedikodu) ile oluşturduğu node listesi içerisindeki ip adreslerine bakarak karar verir. Node listesindeki ip adresinin her birinin 2 nci byte'ı kendi ip adresinin 2 nci byte'ı ile aynı ise ve 3'ü byte'ı kendi ip adresinin 3'ncü byte'ından farklı ise aynı data center içerisinde farklı bir rack, eğer 2 nci byte'ı kendi ip adresinin 2 nci byte'ından farklı ise, node'un farklı bir data center da olduğuna karar verir. RackAware de öncelik her zaman farklı bir data center'a replikasyonun gerçekleştirilmesidir. Eğer replicationFactor 2 ve daha fazla verilirse öncelik farklı datacenter ve farklı rack'dir.

Data center 1 (ip aralığı 192.168.1.1 den 192.168.255.254)
Rack 1: (ip aralığı 192.168.1.40 den 80)
node1: 192.168.1.40
none:2 192.168.1.44
none:3 192.168.1.48

Rack 2: (ip aralığı 192.168.2.40 den 80)
node1: 192.168.2.40
none:2 192.168.2.44
none:3 192.168.2.48

Data center 2 (ip aralığı 192.167.1.1 den 192.167.255.254)
Rack 3: (ip aralığı 192.167.1.40 den 80)
node1: 192.167.1.40
none:2 192.167.1.44
none:3 192.167.1.48

Rack 4: (ip aralığı 192.167.2.40 den 80)
node1: 192.167.2.40
none:2 192.167.2.44
none:3 192.167.2.48

Tagged as: 4 Comments