Erlang’ın özü hafif süreçler ve mesaj aktarımı değildir (2023)
(stevana.github.io)- Erlang’ın güvenilir dağıtık sistemler kurabilmesinin temelinde hafif süreçlerin kendisinden çok, tekrarlanan eşzamanlılık ve hata işleme kalıplarını genelleştiren behaviours bulunur
- behaviours, arayüzler gibi uygulama noktaları sunarken,
gen_servergibi eşzamanlılık işlemlerini ortak bir bileşenin içinde gizleyerek uygulama kodunu sıralı mantığa yaklaştırır - Joe Armstrong’un tezi, yalnızca
gen_server,gen_event,gen_fsm,supervisor,application,releasegibi küçük bileşenlerle AXD301 gibi büyük sistemlerin nasıl kurulduğunu ele alır supervisor, hata veren süreçlerione_for_one,one_for_allgibi stratejilerle yeniden başlatır; “Let it crash!” felsefesi ise kurtarma sorumluluğunu supervisor ağacına taşır- Durum makinesi biçimindeki behaviours, simülasyon testlerini ve biçimsel doğrulamayı kolaylaştırır; geliştirici eşzamanlılık ve simülatör karmaşıklığından çok problemin semantiğine odaklanabilir
Erlang’ın çözmeye çalıştığı problem
- Erlang, güvenilir dağıtık sistemler kurmaya yönelik bir araç olarak başladı
- Başlangıçta güvenilir dağıtık sistemler kurmak için bir Prolog kütüphanesiydi; daha sonra bir Prolog lehçesinden geçerek bağımsız bir dil hâline geldi
- Ericsson’un telefon santrallerini programlamak için kullanıldı ve 80’ler ile 90’larda yüz milyonlarca kullanıcı ölçeğinde trafiği ve katı SLA’leri yönetti
- Ericsson 1998’de Erlang kullanımını yasakladı; geliştirme ekibi, yasaklayacaklarsa açık kaynak olarak yayımlanması gerektiğini savundu ve Ericsson bunu yaptı
- Joe Armstrong, Erlang’ın tasarımı ve uygulanmasındaki kilit kişilerden biriydi; 2002’de SICS’te doktora tezini yazmaya başladı ve 2003’te Making reliable distributed systems in the presence of software errors tezini tamamladı
- Bu tez, matematik veya teoriden çok Erlang’ın arkasındaki fikirlere ve güvenilir dağıtık sistemler inşa etme deneyimine odaklanır
- Erlang’ın büyük fikri hafif süreçler ve mesaj aktarımı değil, Erlang’da behaviours denen genel bileşenlerdir
behaviours: arayüz ve altyapının birleşimi
- Erlang behaviours, Java veya Go’daki arayüzler gibi birden fazla uygulamaya sahip olabilen tip imzası kümeleridir
- Programcı ilgili arayüz uygulamasını sağladığında, o arayüz için yazılmış genel fonksiyonları kullanabilir
- Temel fark, behaviours’ın basit bir arayüzün ötesine geçip altyapı kodunu da birlikte sunmasıdır
- Uygulama programcısı problemin semantiğini, yani iş mantığını yazar
- Eşzamanlılık gibi altyapı kodları behaviour tarafından otomatik olarak sağlanır
- behaviours, deneyimli uzmanların yazdığı en iyi uygulamaları içerir
- Tüm sistem küçük bir behaviour kümesini yeniden kullanırsa, behaviour uygulaması iyileştirildiğinde sistem de kod değişikliği olmadan iyileşebilir
- Behaviour kullanımı yapıyı zorunlu kılarak testleri ve biçimsel doğrulamayı kolaylaştırır
gen_server: sıralı kodun arkasına gizlenmiş eşzamanlılık
gen_serverörneği bir anahtar-değer deposudur;storeanahtar-değer çiftlerini kaydeder,lookupise bir anahtarın değerini bulur- Temel callback olan
handle_call,storeisteğindeDictdurumunu günceller;lookupisteğinde ise durumdan anahtarı sorgular - Bu uygulama
gen_server’a verildiğinde, aynı anda gelenstorevelookupisteklerini işleyebilen bir sunucu oluşturulur - Önemli nokta,
handle_call’ın kendisinin tamamen sıralı olmasıdır- Eşzamanlılık, genel
gen_serverbileşeninin içinde gizlenir - Uygulama kodu, durum ve girdiyi alıp yeni durum ve çıktı üreten bir forma yaklaşır
- Eşzamanlılık, genel
- Joe Armstrong’un tezi bu yaklaşımı
gen_server,gen_event,gen_fsm,supervisor,application,releasebileşenlerine kadar genişletir
AXD301 örneği ve behaviours’ın ölçeği
- Joe Armstrong, Ericsson’un AXD301 telefon santralini örnek olarak kullanır
- AXD301 projesinde şu bileşenler yer alır
- 122
gen_serverörneği - 36
gen_eventörneği - 10
gen_fsmörneği - 20 supervisor
- 6 application
- Tamamı tek bir release olarak paketlenmiştir
- 122
- AXD301, Erlang kodu 1 milyon satırdan fazla olan bir sistemdir
- Bu ölçekteki bir sistemin küçük bir behaviour kümesiyle oluşturulmuş olması, Erlang yapısının temel dayanağı hâline gelir
Diğer behaviours’ın rolleri
gen_eventgenel bir olay yöneticisidir- Olay işleyicilerini kaydeder ve olay yöneticisi ilgili mesajları aldığında işleyiciyi çalıştırır
- Joe Armstrong hata günlüğe kaydetme gibi kullanımları örnek verir
- Örnek logger son beş hata mesajını kaydedip raporlayabilir
gen_fsm, daha sonragen_statemolarak yeniden adlandırılan durum makinesi behaviour’ıdır- Protokol uygulamaları için daha uygundur
- Protokoller çoğu zaman durum makineleri olarak belirtilir
- Herhangi bir
gen_server’ıngen_statemolarak ve tersinin de uygulanabileceği kabul edilir
application, supervisor ağacı ve uygulama teslimi için gereken geri kalan öğelerden oluşurrelease, bir veya daha fazla application’ı paketler- Yükseltme işleme kodunu da içerir
- Yükseltme başarısız olursa önceki kararlı duruma geri dönebilmelidir
supervisor ve “Let it crash!”
supervisor, başka süreçlerin düzgün çalışıp çalışmadığını kontrol eden bir süreçtir- İzlenen süreç başarısız olursa supervisor, önceden tanımlanmış stratejiye göre onu yeniden başlatabilir
- Örnekteki
{one_for_one, 5, 1000}stratejisi şu anlama gelirpacket_assembler,kv,simple_loggeriçinden biri başarısız olursa yalnızca başarısız olan süreç yeniden başlatılır- 1000 saniye içinde 5’ten fazla yeniden başlatma gerekiyorsa supervisor’ın kendisi başarısız olur
permanent, 500, worker, ilgili sürecin her zaman canlı kalması gereken bir worker olduğu ve supervisor yeniden başlatmaya çalışırken düzgün kapanış için 500 ms süre verdiği anlamına gelirone_for_allstratejisinde, bir süreç başarısız olursa tüm alt süreçler yeniden başlatılır- Bir supervisor başka bir supervisor’ı izleyebilir ve mutlaka aynı bilgisayarda çalışması gerekmez
- Bu yapı Docker container düzeyinde değil, thread/hafif süreç düzeyinde çalıştığı için bunu “sadece Kubernetes” olarak görmek zordur
- “Let it crash!”, başarısız olan sürecin yeniden başlatılacağı varsayımına dayanır
- Program yalnızca normal yolu ifade eder
- Normal yolda bir sorun çıkarsa doğrudan kurtarmaya çalışmaz, crash olur
- İşlemeyi supervisor ağacında daha üstte yer alan başka bir program üstlenir
- AXD301’in uzun vadeli kararlılığına ilişkin kanıtlar sistematik biçimde toplanmamıştı; ancak Joe Armstrong’un tezinde, büyük bir müşterinin 11 düğümlü bir sistemi %99.9999999 güvenilirlikle çalıştırdığına dair bir PowerPoint sayısı bulunduğu yazıyordu
- Bu değerin nasıl elde edildiği belgelenmemişti
- Diğer kesinti süresi raporu değerleri için de hesaplama yönteminin belirsiz olduğuna dair notlar vardı
behaviours başka dillerde uygulanacak olsaydı
- Yalnızca hafif süreçler ve mesaj aktarımı, Erlang’ı güvenilir sistemler için uygun hâle getirmez
- Daha dürüst açıklama, behaviours’ın sağladığı yapı ve bu yapının güvenilir yazılıma nasıl yol açtığıdır
gen_server’ın arayüz imzası şu biçimde görülebilirInput -> State -> (State, Output)- Bu, girdi ve mevcut durumu alıp yeni durum ve çıktı üretmek anlamına gelir
- Bu sıralı imzayı eşzamanlı istek işlemeye dönüştürmenin yolu şöyle düşünülebilir
- HTTP sunucusu isteği
Input’a dönüştürüp kuyruğa koyar - Olay döngüsü girdiyi kuyruktan alıp sıralı uygulamaya iletir
- Çıktı istemci yanıtı olarak geri döndürülür
- Birden fazla
gen_serverdesteklenecekse her sunucuya ad verilip isteğe ad ve girdi birlikte eklenebilir
- HTTP sunucusu isteği
gen_event, kuyruktaki belirli olay türleri için callback kaydına izin veren bir yöntemle uygulanabilirsupervisor,gen_serverfonksiyon çağrılarını bir istisna işleyicisiyle sarmalayıp istisna oluştuğunda supervisor’a haber veren bir yöntemle basitleştirilerek düşünülebilir- Supervisor aynı bilgisayarda çalışmıyorsa işler daha karmaşık hâle gelir
applicationverelease, yapılandırma, dağıtım ve yükseltme zor problemler olduğu için önemlidir; ancak derinlemesine ele alınmaz
behaviours ve test edilebilirlik
- Erlang behaviours’ın yapısı simülasyon testlerine ve biçimsel doğrulamaya uzanır
- Son dönemde ilgi çeken konu, FoundationDB tarzı dağıtık sistem simülasyon testleridir
- Simülasyon testi, sistemi simüle edilmiş bir dünyada çalıştırır ve simülasyonun ağ mesajlarının ne zaman gönderileceğini tamamen kontrol ettiği bir yöntemdir
- FoundationDB, simülasyon testi için actor’ları olan bir C++ lehçesi veya kendi programlama dilini oluşturdu
- Bu yaklaşımın, yalnızca aşağıdaki durum makinesi tipiyle bile epey ilerleyebileceği düşünülür
Input -> State -> (State, [Output])[Output]bir çıktı dizisidir
- Simülatör mesajları varış zamanına göre öncelik kuyruğuyla yönetir
- Bir mesaj çıkarır
- Saati ilgili mesajın varış zamanına ilerletir
- Mesajı alıcı durum makinesine iletir
- Çıktı mesajları için yeni varış zamanları üretip yeniden kuyruğa koyar
- Her şey deterministikse ve varış zamanları seed ile üretiliyorsa, farklı interleaving’ler keşfedilebilir ve yeniden üretilebilir hatalar elde edilebilir
- Bu yöntem, mesajlar bellek içinde işlendiği ve timeout beklemeden saat varış zamanına ilerletildiği için Jepsen’dan daha hızlıdır
- Bu durum makinesi biçimi “network normal form” olarak adlandırılır ve ağ üzerinden alıp gönderen tüm programların bu biçime refactor edilebileceği tahminine yol açar
gen_servervegen_statem’in fiilen aynı tip yapısına sahip olması, bu yapının keyfî olmadığına dair bir işaret olarak görülebilir
Biçimsel doğrulama ve durum makinesi yapısı
- Joe Armstrong bir konuşmasında, dağıtık leader election’ı doğru uygulamanın zor olduğunu söylemişti
- Bir simülatör varsa bu problem önemli ölçüde basitleşebilir
- Uçak üretiminde rüzgâr tünelinin yardımcı olması gibi, production öncesinde kararsız ağ veya güç kaybı gibi uç koşullar test edilebilir
- Bu simülatör behaviours için genel veya parametreli olabilir
- Geliştiricinin ayrıca simülatör yazması gerekmez
- Karmaşıklık,
gen_server’ın eşzamanlılık kodu gibi behaviour’ın arkasına gizlenir
- FoundationDB’nin testleri hakkında Kyle “aphyr” Kingsbury, FoundationDB’nin testlerinin kendi Jepsen testlerinden çok daha sıkı göründüğü yönünde bir tweet paylaştı
- Biçimsel doğrulama da program bir durum makinesi olarak yazıldığında kolaylaşır
- Lamport’un TLA+ model checking çalışması, spesifikasyonun bir durum makinesi olduğunu varsayar
- Kleppmann, durum makinesi yapısını kullanan yapısal tümevarımla durum patlaması problemini ele almanın bir yolunu gösterir
- Erlang behaviours’tan elde edilen yapı, Joe Armstrong’un zor dediği problemleri daha kolay çözmek için yeniden kullanılabilir
İlgili çalışmalar ve kaynaklar
- Erlang’dan fikir alan ilgili çalışmalar şunlardır
- Martin Thompson’ın LMAX Disruptor’ı ve aeron’dan fikir alarak behaviours’ın çalışacağı hızlı bir olay döngüsü oluşturma çalışması
- Durum makinesi tipine async I/O ekleme çalışması
- supervisors’ı daha ayrıntılı uygulama çalışması
- Durum makinelerinde hot code swapping
- Kaynak olarak Joe Armstrong’un doktora tezi, OTP design principles,
gen_server,gen_event,gen_statem,supervisor,applicationve release belgeleri sunulur - Actor model ile Erlang ilişkisi konusunda, Erlang geliştiricilerinin actor model’i bilmeden Erlang’ı oluşturduğu ve Carl Hewitt’in makalesinin Erlang süreçleri ile actor model arasındaki farkları belgelediği şeklinde özetlenir
- Akka’da “actors” ve supervisor trees vardır; ancak diğer Erlang behaviours’a karşılık gelen bir şey yok gibi görünür ve Akka’nın “behavior” kavramı da Erlang behaviours’tan farklıdır
1 yorum
Hacker News yorumları
Erlang ve BEAM’in şaşırtıcı yanı, özelliklerinin derinliği. Orijinal yazıda Behaviour/Interface ana izlenimdi, ama benim için daha büyük nokta, karmaşık sistemler kurarken bunu diğer dillere göre çok daha az geliştirme kaynağıyla yapabilmek
OTP’nin kendisinin içinde de çok şey var. Elixir’i iOS cihazlarda çalışacak şekilde derleme işiyle uğraştım; yalnızca Erlang’ın release sürecini değil, ei kütüphanesini kullanarak C ile bir Node derleyip Erlang, Elixir, Gleam vb. diğer Erlang node’larıyla sıradan bir dağıtık ağ gibi iletişim kurabildim
Ayrıca Erlang’ın rpc kütüphanesi üzerinden C’den Elixir uygulama fonksiyonlarını çağırmak bile mümkündü. Kodlama/kod çözme ek yükü var ve FFI daha hızlı olurdu, ama gecikme bütçemizin rahatça içindeydi; daha önce adını bile duymadığım bir özelliği birkaç gün içinde ayağa kaldırabildim
Daha büyük çıkarım şu: Erlang, modern teknoloji yığınlarının boğuştuğu pek çok sorunu onlarca yıl önce, hem ölçek hem uygulama maliyeti açısından çözmüş. HN’de Erlang/Elixir’e karşı bir tür clickbaitvari sevgi var, ama bu gerçek benimsemeye dönüşmedi; Erlang yığınında varsayılan olarak bedavaya gelen şeyleri uygulamaya çalışırken para yakan şirketler var
Üzerinde çalıştığım proje bir backend veri hattıydı ve işlediği veri de çok fazla değildi; buna rağmen temel bir hatayı tam olarak izole etmek inanılmaz derecede zordu
Bu süreçte Node.js’in çeşitli özelliklerini öğrendim ve Elixir/Erlang/OTP ile karşılaştırınca Node.js’in tasarım gereği daha az güvenilir olduğu sonucuna vardım
Ruby’de de çok çalıştım, Python’a da dokundum; ama mevcut nesil dil platformlarının çoğu güvenilir dağıtık sistemler kurmakta zorlanıyor. BEAM VM ve OTP platformu bu kısmı zaten çözmüş durumda
Örneğin çoğu BEAM sisteminde işçi rolünü üstlenen GenServer, nihayetinde basit parametrelerle birden fazla fonksiyon çağıran bir yapı
Dolayısıyla bu fonksiyonları doğrudan çağırıp parametreleri elle geçirerek, sonra da yalnızca çıktıyı doğrulayarak test edebilirsiniz. Asenkron kodla uğraşacak karmaşık bir test sistemi kurmaya ya da testlerde işin bitmesini bekleyip durmaya gerek yok
Junior’ların sık kaçırdığı bir nokta, ama fark edince oldukça özgürleştirici oluyor
Bazı insanların, çoğunlukla yöneticilerin, bizim deneyimimize dayanarak kitap yazmak istediğini gördüm. İşimizin neden başarılı olduğuna dair farklı noktaları esas almaları beni hep hayal kırıklığına uğrattı. Benim vazgeçilmez gördüğüm şeyleri onlar çoğu zaman olsa iyi olur seviyesine indiriyordu
Burada da biri hafif süreçler ve mesaj iletiminin gizli sos olmadığını söylerken, Erlang’ı iletişim kuran sıralı süreçler (CSP) olarak görme bakışının bu özelliklerden ayrılamayacağını kaçırıyor. Buna rağmen CSP’yi tekrar tekrar gizli sosun bir parçası olarak anıyor
Örneğin uygulama programcısının sıralı kod yazması ve eşzamanlılığın behaviour içinde saklanması, iş mantığının sıralı olduğu için yeni ekip üyelerinin başlamasının kolay olması ya da supervisor ve “let it crash” felsefesinin güvenilir sistemler üretmesi gibi
Behaviour ilginç; 80’lerde yaygındı ve 2000’lerde de bazı yerlerin çözmeye çalıştığı bir sorunu çözüyor, fakat Erlang’da hem amaç hem de araç. O diğer özelliklerin uygulanma biçimi bu; ama Erlang’ın hâlâ Erlang gibi kalması için mutlaka böyle olmak zorunda mıydı, emin değilim
CSP, occam ve diğer diller üzerinden Go kanallarına ilham veren taraftır. Arabelleksiz kanallarda senkronizasyon biçimi en belirgin farktır; aktör modelindeki posta kutusunda pattern matching gibi farklar da vardır
CSP ile aktör modeli tartışması dışarıdan benzer görünse de gerçek sonuçları oldukça farklıdır ve bu da epey ilginçtir
Aktör modelinin daha mantıklı olduğunu düşünüyorum, ama kişiden kişiye çok değişebilir
Ericsson’ın neden Erlang kullanmayı bıraktığını, Joe’nun neden işten çıkarıldığını daha çok öğrenmek için geldim
Kısaca bakınca Ericsson yeni projeleri Javaya geçirmiş ve bu yüzden Erlang kenara itilmiş gibi görünüyor. Sonrasında Joe ve çalışma arkadaşları 1998’de Bluetail’i kurdu; şirket Nortel tarafından satın alındı
Nortel, Toronto Borsası değerinin yaklaşık üçte birini oluşturan bir telekom deviydi. 2000’de hisse fiyatı 125 dolara kadar çıktı, ama 2002’de 1 doların altına düştü. Bu dot-com çöküşünün bir parçasıydı ve telekom harcamalarındaki sert düşüşle birleşince Nortel özellikle ağır darbe aldı
Joe’nun işten çıkarılmasını “batan gemide onun bölümünün önce su alması” olarak görmek güvenli görünüyor. Nortel 60 bin kişiyi, yani toplam iş gücünün üçte ikisinden fazlasını işten çıkardı. Bu işten çıkarma, Joe’nun görevini yapamadığının işareti değildi; iş biriminin verimsizliğini gösteren bir şey de değil, çaresizce alınmış büyük ölçekli bir önlemdi
“fired” güçlü bir değer yargısı taşır ve bir hata nedeniyle gerekçeli olarak işten atıldığı imasını verir. Gerçekten öyle olsa bile orijinal yazının yazarının bunu bilmesi mümkün değil; bilmesi de gerekmez
Hafif süreçler ve mesaj aktarımı sayesinde Erlang’a yeniden bakmaya başladım; şimdiye kadar behaviour benim için ikincil kalmıştı.
Proje, görsel akış tabanlı programlamayı (FBP) Erlang’a taşımak. FBP sanki Erlang için yapılmış gibi; buna rağmen mevcut bir uygulama görememiş olmak şaşırtıcıydı.
FBP için esas kullandığım araç Node-RED olduğundan temel fikir, Node-RED ön yüzünü bir Erlang arka ucuna bağlamak ve tüm düğümleri süreç yapmak. Node-RED ön yüzü, düğümler arası mesaj aktarımını modellemek için uygun; bu yüzden Erlang süreçleri ve mesajlarıyla çok basit bir 1:1 eşleme mümkün.
Temel işlevlerin bir kısmını uyguladım ve özellikleri yavaş yavaş eklemek için akışları birim testlerine dönüştürmeye başladım. Node.js arka ucu olan Node-RED ile %100 uyumlu olursa iyi olur. Ayrıntılar GitHub deposunda → https://github.com/gorenje/erlang-red
Genel olarak Erlang buna şaşırtıcı derecede iyi uyuyor; neden kimsenin benzer bir şey yapmadığına şaşıyorum. Yoksa zaten var mıydı?
[1] = https://jpaulm.github.io/fbp/index.html
Bana göre Erlang/Elixir’in gücü aktör modeli uygulamasında, Prolog’dan gelen eşleştirmede, değişmezlikte ya da behaviour’ın kendisinde değil; Joe’nun daha azıyla daha fazlasının yapılabileceğini gösterme iradesinde yatıyor.
İyi tasarlanmış ve doğrulanmış bir hesaplama sistemi; yalnızca diğer dillerde değil, “web” dünyasında da nadir görülen bir tutarlılığa sahip. Kusursuz değil ama oldukça etkileyici.
Ne yazık ki yazılım dünyasında sadeliğin mümkün kıldığı şeylere yönelik takdirin ve benimsemenin epey yetersiz olduğunu düşünüyorum. Karmaşıklık insanların uzman olmasını sağlar, yöneticilerin büyük ekipleri ve çok sayıda toplantıyı gerekçelendirmesine imkân verir, uzmanların da uzman kalmaya devam etmesini sağlar.
Erlang, şirketlerin daha az insan ve sınırlı performansla yazılım çözümleri hayata geçirmeye çalıştığı bir dönemde geliştirildi. Sonraki onlarca yılda bu alana para akınca, “azın herkes için iyi olacak şekilde daha fazlası anlamına gelmesi” değeri daha az cazip görünmeye başladı.
Erlang/BEAM’deki en ilginç kavram, kısmi kurtarmanın en baştan yerleşik olması.
Beklenmeyen bir durumla karşılaşınca tüm süreci öldürmek ya da devam edip hasarı göze almak yerine, mümkün olan en ince ayrıntı düzeyinde bilinen sağlıklı duruma geri döner.
Bu fikir uzun zaman önce “microreboots” adıyla araştırılmıştı ve “crash-only software” ile de ilişkiliydi; ancak yalnızca Erlang/BEAM bunu işletim sisteminin birinci sınıf kavramı hâline getirdi.
Erlang bu tür sorunları ele almak için iyi bir model sunar, ama bu tamamen düşünmeyi bırakabileceğiniz anlamına gelmez. “Bırakalım yeniden başlasın, her şey düzelir” diye yaklaşırsanız canınız yanabilir.
Erlang, OTP ve BEAM behaviour’dan fazlasını sunar. VM; supervisor’ları, yalıtılmış süreçleri ve birden çok fiziksel/sanal makineyi tek bir kaynak havuzu gibi ele alan dağıtık modu olan bir sanal çekirdeğe daha yakındır.
OTP ayrıca veritabanı Mnesia, önbellekleme için atomik sayaçlar ve ETS tabloları gibi çeşitli kullanışlı modlar da sunar. Çalışma zamanı bytecode hot reloading’i de destekler; böylece sistem kesintisi olmadan yama uygulanabilir. Sözdizimi ekran okuyucu dostu değil ama okunabilir.
Birden fazla makinenin kaynaklarını tek bir havuz gibi ele alma yeteneği açısından BEAM’e benzer platform olarak akla gelen şey ancak Apache Mesos[1] civarıdır.
Yaklaşık bir yıl önce kişisel danışmanlık şirketim arka uç dili olarak Erlang’ı benimsedi. Sonrasında TCP tabanlı yığını QUIC’e geçirmek veya Rust yamalarını entegre etmek gibi işler için BEAM’in içlerini keşfetmeye başladık.
Kernel panic ya da güç kaybı olmadıkça başarısız olmayan hafif ve yüksek verimli sistemler için gerçekten harika bir seçim. Şu anda film/oyun prodüksiyon takipçisi ve pipeline yöneticisi gibi çok yoğun ve yüksek eşzamanlılığa sahip yazılımlar geliştiriyoruz; özel hastane yönetim hizmeti Ar-Ge’sine de hazırlanıyoruz.
[1]: https://mesos.apache.org/
Elixir tarafında bakımı yapılmayan şeyler için kendi iç binding’lerimizi veya uygulamalarımızı sürdürmemiz gerekmesi yüksek olasılık.
Ayrıca çok fazla sözdizimsel şeker var ve kullanıcıların bir şeyleri DSL arayüzüyle soyutlamaya yönelik tuhaf bir takıntısı olduğu da görülüyor.
“Dil ve kütüphane tasarımcıları neden yalnızca hafif süreçler ve mesaj iletimi fikrini kopyalıyor da Erlang behaviour’larının arkasındaki yapıyı almıyor?” sorusunun bir nedeni var.
Erlang behaviour’larının fonksiyon imzaları, Erlang’ın diğer özellikleriyle, özellikle de alışılmadık değişmezlik kullanımı biçimiyle sıkı sıkıya bağlıdır. Sunucu için ayrı bir init çağrısının gerekmesi de bu yüzdendir; durum yönetiminin de aynı şekilde çalışabilmesi için çok belirgin bir forma ihtiyaç vardır.
Ancak başka dillerde aynı hedefe ulaşmak için Erlang’ın yaptığını birebir kopyalamamak gereken durumlar neredeyse her zaman çoktur. “gen_server’ı başka bir dile port ettim” deyip Erlang ile tamamen aynı arayüzü gösteriyorsa, bunu Erlang’ın ne yaptığını derinlemesine anlamayan bir port olarak görürüm.
Ben Go’ya supervisor tree fikrini taşıdığımda[1] da bunu Go’ya uygun şekilde yaptım. Modern Go’da “denetlenebilir şey” için doğru arayüz, Erlang’dakiyle aynı imza değil, yalnızca şudur:
type Service interface {
Serve(context.Context)
}
Go’da hepsi bu; bundan fazlasını kullanmamak gerekir. Başka dillerde farklı olabilir. Go’da channel’lar olduğu için “handle_event/2”ye gerek yoktur; onları kullanmak gerekir. Channel’lar daha iyi ya da daha kötü olduğu için değil, dil o şekilde çalıştığı için.
Başka dillerde başka şeyler kullanılabilir; başka altyapılarda da “handle_event/2 çağırmak” yerine Kafka’ya ya da bir bulut event bus’ına gönderebilirsiniz. Asıl mesele olay güdümlü sistem kurmaktır, Erlang’ın birebir uygulamasını kopyalamak değil.
Erlang topluluğunda, Erlang’ın bunu tam olarak yapma biçiminde olağanüstü özel bir şey olduğu ve farklı yapıldığında bunun hemen yanlış ve güvenilmez sayılması gerektiği konusunda aşırı bir özgüven sorunu var. 2005’te böyle olabilir, ama 2025’te değil.
Bir zamanlar Erlang neredeyse anlamlı tek cevaptı; ama 2025’in sorunu cevap bolluğunun içinden yol bulmak. Güvenilir yazılım için Erlang’dan öğrenmeyi şiddetle tavsiye ederim, ancak Erlang’ın başardığı şeyi birebir başka dillere körü körüne port etmeye kesinlikle karşıyım. Neredeyse tüm diğer dil bağlamlarında bu yanlış cevaptır. Diğer değişmez dillerde bile yapı yeterince farklıdır; aynen kopyalanamaz.
[1]: https://jerf.org/iri/post/2930/
Elixir’i profesyonel olarak kullanan biri olarak Erlang hakkında en ilginç soru bence tam da bu.
k8s, Kafka ve AWS’in dağıtık sistem ürünlerinin büyük başarısına bakınca, güvenilir eşzamanlı sistemler tasarlamaya yardımcı olan araçlara talebin kesinlikle muazzam olduğu görülüyor. Peki Erlang/Elixir neden bu payı alamadı?
Arkadaşlarımla sık sık tartışıyoruz ama cevabı bilmiyorum.
Erlang’ın ağ mesajı işlemeyi soyutlamış olduğunu düşünüyorum. Üstelik uzun süre çalışan sunucular gibi şeyler için önerilen kalıbın 3-4 varyasyonunu gördüm.
Port ederken idiomatik bileşenler kullanmak gerektiğine tamamen katılıyorum. Ancak dillerde, port sürecinde değerli özü kaybettiren gizli mekanizmalar olabilir. Bir tür programlama dili anti-göreliliği denebilir.
“Channel mı? X ile sarmalarsın olur biter” yaklaşımı, kulağa geldiğinden çok daha fazla birlikte çalışabilirliğe zarar verebilir. Örneğin Go’nun http.Handler’ına bakınca basittir, ama standart kütüphanede bulunmasının büyük pratik sonuçları vardır. Önceden koordinasyon olmadan da büyük ölçüde birbiriyle uyumlu bir middleware ekosistemi oluşur.
io.Reader ve benzerleri de öyle. Bu son derece basit arayüzlerin uygulamadan daha değerli olduğu söylenebilir.
Bir tahmin olarak, Erlang güvenilir dağıtık sistemler için birçok arayüzü doğru yakaladıysa, tümünü mümkün kılan unsur tam da bu olabilir.
Katılmıyorum. Arayüz, herhangi bir dile eklenebilecek önemsiz bir kavramdır. Resmî arayüz sözdizimi olmayan dillerde bile program alanında taklit edilebilir.
BEAM’in başarılı olmasının nedeni, tek bir node üzerinde 1 milyon process çalıştırabilmesi, karmaşık dağıtık durum makinelerini kolayca ifade edebilmesi ve sistemin bir bölümünü kesintisiz yeniden başlatabilmesidir. Bunun dışında da pek çok nedeni var.
behaviour/interface’in en temel parça olduğunu gerçekten düşünmüyorum.
İzole process heap’leri için OS process’leri kullanılabilir; supervisor tree için Kubernetes kullanılabilir; mesaj iletimi Java’da iki thread ve paylaşılan bir queue ile yapılabilir; hot code loading Java’da da mümkündür; düşük gecikme için LMAX Disruptor ayarlanırsa Erlang’ı bile geçebilir, vb.
Ama asıl mesele tüm bunları tek bir platformda ya da kütüphanede toplamaktır. OS process’leri ağırdır ve bir sunucuda 2 milyon tane çalıştırmak kolay değildir. Green thread ya da promise kullanırsanız izole heap’i kaybedersiniz.
Kubernetes de bir ölçüde kullanılabilir, ama iç içe supervisor tree’leri iyi ele almaz. Muhtemelen yapılabilir, fakat kodun yanında pod’lar, controller’lar, volume’lar ve daha bir sürü şey ortaya çıkar.
Çeşitli dillerin actor kütüphaneleriyle mesaj iletimi yapılabilir, ama receive içinde pattern matching yapmayı ya da başka bir node’daki başka bir thread’e göndermeyi şeffaf biçimde bütünleştirmezler.
Hot code loading de mümkündür, ama runtime veri yapıları ve durumla ne yapılacağı sorundur. Erlang bunun etrafında tasarlanmıştır; gen_server’da durum değişmez ve açık olduğu için yalnızca kodu değil, durumun kendisini de yükselten callback’ler vardır.
BEAM’in bu sorunu ortadan kaldırdığını söylemem, ama çizginin eğimini azalttığını düşünüyorum. Dağıtım, otomatik kurtarma ve yük dengeleme dahil kendi içinde tutarlı idiom ve özellikler, modüller arası sürtünmeyi azaltır.
12 mühendisin 30 endpoint’i kolayca yönetebildiği ve yüzey alanının hâlâ kuvvet yasasını izleyebildiği bir sistem kurmayı sağlar.
Bu yazının içeriğine katılmıyorum. Behaviour, sistemin temel mimarisi sayesinde mümkün oluyor.
Behaviour bir arayüz değil; Java gibi dillerdeki soyut nesneye daha yakın. İş birliği arayüzünün arkasına gizlenmiş temel ve kendi kendine yeten işlevselliği uygular; ancak her sürecin diğerlerinden tamamen ayrıldığı, tüm süreçlerin bellek ya da kaynak sızıntısı olmadan güvenle kapatılabildiği ve iki süreç arasında tehlikeli işaretçilerin paylaşılmasının engellendiği temel altyapı olmadan pek fazla şey yapamaz.
Joe’nun makalesinde gösterdiği şey, verilmiş bir Lego blokları kümesiyle güvenilir sistemlerin, hatta bir ölçüde güvenilir dağıtık sistemlerin nasıl kurulabileceğiydi.
Bunu doğru düzgün uygulamak için Erlang VM gerekir; başka VM’lerde tamamen uygulanamaz. Alttaki tesisat yoksa denetim ağaçları sızıntı yapar. Java’da kaynakları tutan bir thread’i öldürüp her şeyin her zaman yolunda gitmesini bekleyemezsiniz; farklı süreçleri izleme imkânları da yetersizdir.