Erlang का सार lightweight process और message passing नहीं है (2023)
(stevana.github.io)- Erlang के भरोसेमंद distributed systems बनाने की क्षमता का मूल कारण सिर्फ lightweight process नहीं, बल्कि बार-बार आने वाले concurrency और failure handling patterns को सामान्यीकृत करने वाले behaviours हैं
- behaviours इंटरफ़ेस की तरह implementation points देते हैं, लेकिन
gen_serverकी तरह concurrency handling को shared component के भीतर छिपाकर application code को sequential logic के अधिक करीब बना देते हैं - Joe Armstrong का शोधपत्र बताता है कि
gen_server,gen_event,gen_fsm,supervisor,application,releaseजैसे छोटे building blocks से AXD301 जैसे बड़े systems बनाए गए supervisorविफल process कोone_for_one,one_for_allजैसी strategies से restart करता है, और “Let it crash!” दर्शन recovery की ज़िम्मेदारी supervisor tree पर डाल देता है- state machine रूप वाले behaviours simulation testing और formal verification को आसान बनाते हैं, जिससे developer concurrency या simulator की जटिलता के बजाय समस्या की semantics पर ध्यान दे सकता है
Erlang किस समस्या को हल करना चाहता था
- Erlang की शुरुआत भरोसेमंद distributed systems बनाने के एक tool के रूप में हुई
- शुरुआत में यह भरोसेमंद distributed systems बनाने के लिए Prolog library था, फिर Prolog dialect बना, और बाद में एक स्वतंत्र भाषा बन गया
- इसका उपयोग Ericsson के telephone switch programming में हुआ, और 80–90 के दशक में इसने करोड़ों users के traffic और कड़े SLA को संभाला
- Ericsson ने 1998 में Erlang के उपयोग पर रोक लगा दी, और development team ने कहा कि अगर रोक लगानी है तो इसे open source कर देना चाहिए; Ericsson ने ऐसा ही किया
- Joe Armstrong Erlang के design और implementation के प्रमुख व्यक्तियों में से एक थे, और उन्होंने 2002 में SICS में अपना PhD thesis शुरू किया, जिसे 2003 में Making reliable distributed systems in the presence of software errors के रूप में पूरा किया
- यह शोधपत्र गणित या सिद्धांत से अधिक Erlang के पीछे के ideas और भरोसेमंद distributed systems बनाने के अनुभव पर केंद्रित है
- Erlang का बड़ा विचार lightweight process और message passing नहीं, बल्कि Erlang में behaviours कहलाने वाले सामान्य components हैं
behaviours: interface और infrastructure का संयोजन
- Erlang behaviours, Java या Go के interfaces की तरह, type signatures के bundles हैं जिनके कई implementations हो सकते हैं
- यदि programmer उस interface का implementation दे, तो उस interface को target करके लिखे गए generic functions का उपयोग किया जा सकता है
- मुख्य अंतर यह है कि behaviours सिर्फ interface नहीं देते, बल्कि साथ में infrastructure code भी देते हैं
- application programmer समस्या की semantics, यानी business logic, लिखता है
- concurrency जैसी infrastructure code behaviour अपने आप उपलब्ध कराता है
- behaviours में अनुभवी विशेषज्ञों द्वारा लिखी गई best practices शामिल होती हैं
- यदि पूरा system behaviours के छोटे set का पुन: उपयोग करे, तो behaviour implementation में सुधार होने पर system भी बिना code बदले बेहतर हो सकता है
- behaviour का उपयोग structure को enforce करता है, जिससे testing और formal verification आसान हो जाते हैं
gen_server: sequential code के पीछे छिपी concurrency
gen_serverका उदाहरण एक key-value store है, जहाँstorekey-value pair सहेजता है औरlookupkey का value ढूंढता है- मुख्य callback
handle_call,storerequest परDictstate को update करता है, औरlookuprequest पर state से key खोजता है - जब इस implementation को
gen_serverको दिया जाता है, तो एक ऐसा server बनता है जो एक साथ आने वालीstoreऔरlookuprequests संभाल सकता है - महत्वपूर्ण बात यह है कि
handle_callखुद पूरी तरह sequential होता है- concurrency सामान्य
gen_servercomponent के भीतर छिपी रहती है - application code state और input लेकर नया state और output बनाने वाले रूप के अधिक करीब हो जाता है
- concurrency सामान्य
- Joe Armstrong का शोधपत्र इसी approach को
gen_server,gen_event,gen_fsm,supervisor,application,releaseतक विस्तारित करता है
AXD301 का उदाहरण और behaviours का पैमाना
- Joe Armstrong ने Ericsson के AXD301 telephone switch को case study के रूप में इस्तेमाल किया
- AXD301 project में ये components शामिल थे
gen_serverके 122 instancesgen_eventके 36 instancesgen_fsmके 10 instances- 20 supervisor
- 6 application
- पूरे system को एक release के रूप में package किया गया
- AXD301 ऐसा system था जिसमें Erlang code 10 लाख से अधिक lines का था
- इतने बड़े system को behaviours के छोटे set से बनाना Erlang की संरचना के पक्ष में एक महत्वपूर्ण प्रमाण है
अन्य behaviours की भूमिका
gen_eventएक generic event manager है- इसमें event handlers register किए जा सकते हैं, और event manager जब संबंधित message पाता है तो handler चलाता है
- Joe Armstrong ने इसका उदाहरण error logging जैसे उपयोगों से दिया
- उदाहरण logger हाल की पाँच error messages को record और report कर सकता है
gen_fsmएक state machine behaviour है, जिसका नाम बाद मेंgen_statemकर दिया गया- यह protocol implementation के लिए अधिक उपयुक्त है
- protocols अक्सर state machines के रूप में निर्दिष्ट किए जाते हैं
- लेखक का मानना है कि किसी भी
gen_serverकोgen_statemके रूप में, और उलटा भी, implement किया जा सकता है
applicationsupervisor tree और application delivery के लिए आवश्यक अन्य तत्वों से बना होता हैreleaseएक या अधिक application को package करता है- इसमें upgrade handling code भी शामिल होता है
- यदि upgrade विफल हो जाए, तो system को पिछले stable state पर rollback करने में सक्षम होना चाहिए
supervisor और “Let it crash!”
supervisorएक ऐसा process है जो देखता है कि अन्य processes सही ढंग से चल रहे हैं या नहीं- यदि monitored process विफल हो जाए, तो supervisor उसे predefined strategy के अनुसार restart कर सकता है
- उदाहरण की
{one_for_one, 5, 1000}strategy का अर्थ है- यदि
packet_assembler,kv,simple_loggerमें से कोई एक विफल हो जाए, तो सिर्फ वही विफल process restart होगा - यदि 1000 seconds के भीतर 5 से अधिक बार restart की ज़रूरत पड़े, तो supervisor स्वयं विफल माना जाएगा
- यदि
permanent, 500, workerका मतलब है कि वह process हमेशा चलना चाहिए, और restart की कोशिश करते समय supervisor उसे graceful shutdown के लिए 500ms देता हैone_for_allstrategy में, एक process के विफल होने पर सभी child processes restart होते हैं- supervisor दूसरे supervisors की निगरानी भी कर सकता है, और यह ज़रूरी नहीं कि वे उसी computer पर चल रहे हों
- यह संरचना Docker container स्तर पर नहीं, बल्कि thread/lightweight process स्तर पर काम करती है, इसलिए इसे “बस Kubernetes” कहना कठिन है
- “Let it crash!” का मतलब है कि विफल process के restart होने की अपेक्षा की जाती है
- program सिर्फ normal path को व्यक्त करता है
- यदि normal path में समस्या आए, तो program खुद recovery करने की कोशिश नहीं करता, बल्कि crash हो जाता है
- फिर supervisor tree में ऊपर का कोई दूसरा program उसे संभालता है
- AXD301 की दीर्घकालिक स्थिरता के व्यवस्थित प्रमाण इकट्ठे नहीं किए गए थे, लेकिन Joe Armstrong के शोधपत्र में लिखा है कि एक बड़े ग्राहक ने 11-node system को 99.9999999% reliability के साथ चलाया था, ऐसा एक PowerPoint आँकड़ा मौजूद था
- यह संख्या कैसे निकाली गई, इसका documentation नहीं था
- अन्य downtime reports के आँकड़ों के साथ भी यह caveat था कि उनकी गणना की विधि स्पष्ट नहीं है
यदि behaviours को दूसरी भाषाओं में implement किया जाए
- सिर्फ lightweight process और message passing होने से Erlang अपने आप भरोसेमंद systems के लिए उपयुक्त नहीं हो जाता
- अधिक ईमानदार व्याख्या यह है कि behaviours जो structure देते हैं, और वही structure किस तरह भरोसेमंद software तक ले जाती है
gen_serverकी interface signature को इस रूप में देखा जा सकता हैInput -> State -> (State, Output)- यानी input और current state लेकर नया state और output बनाना
- इस sequential signature को concurrent request handling में बदलने का एक तरीका ऐसा हो सकता है
- HTTP server request को
Inputमें बदलकर queue में डाल दे - event loop queue से input निकाले और sequential implementation को दे
- output को client response के रूप में वापस लिख दे
- कई
gen_serverको support करने के लिए हर server को नाम दिया जा सकता है, और request में नाम तथा input दोनों शामिल किए जा सकते हैं
- HTTP server request को
gen_eventको इस तरह implement किया जा सकता है कि queue के किसी खास event type पर callback register किए जा सकेंsupervisorको सरल रूप में ऐसे सोचा जा सकता है किgen_serverfunction call को exception handler में wrap किया जाए, और exception होने पर supervisor को सूचित किया जाए- यदि supervisor उसी computer पर न चल रहा हो, तो चीज़ें अधिक जटिल हो जाती हैं
applicationऔरreleaseमहत्वपूर्ण हैं क्योंकि configuration, deployment, और upgrade कठिन समस्याएँ हैं, हालांकि यहाँ उनकी गहराई में चर्चा नहीं की गई
behaviours और testability
- Erlang behaviours की संरचना simulation testing और formal verification की ओर ले जाती है
- हाल की दिलचस्पी FoundationDB-style distributed system simulation testing में रही है
- simulation testing में system को एक simulated world में चलाया जाता है, और simulation network message delivery के समय को पूरी तरह नियंत्रित करता है
- FoundationDB ने simulation testing के लिए actors वाले C++ dialect, या अपनी खुद की programming language, बनाई
- लेखक का मानना है कि सिर्फ इस state machine type के साथ भी काफी दूर तक जाया जा सकता है
Input -> State -> (State, [Output])[Output]output sequence है
- simulator arrival time के अनुसार priority queue में messages को manage करता है
- एक message निकाला जाता है
- clock को उस message के arrival time तक आगे बढ़ाया जाता है
- message को receiving state machine को दिया जाता है
- output messages के लिए नए arrival times बनाकर उन्हें फिर queue में डाला जाता है
- यदि सब कुछ deterministic हो और arrival times किसी seed से generate हों, तो कई interleavings को explore किया जा सकता है और reproducible failures पाए जा सकते हैं
- यह तरीका Jepsen से तेज़ है, क्योंकि messages memory के भीतर process होते हैं, और timeout का इंतज़ार करने के बजाय clock सीधे अगले arrival time तक बढ़ा दी जाती है
- ऐसी state machine संरचना को “network normal form” कहा गया है, और इससे यह अनुमान निकलता है कि network पर receive/send करने वाले सभी programs को इस रूप में refactor किया जा सकता है
gen_serverऔरgen_statemका लगभग एक जैसा type structure होना इस बात का संकेत है कि यह structure मनमाना नहीं है
formal verification और state machine संरचना
- Joe Armstrong ने एक talk में कहा था कि distributed leader election को सही तरह implement करना कठिन है
- यदि simulator हो, तो यह समस्या काफी सरल हो सकती है
- जैसे विमान निर्माण में wind tunnel मदद करता है, उसी तरह production से पहले unstable network या power loss जैसी extreme conditions की testing की जा सकती है
- यह simulator behaviours के लिए generic या parameterized हो सकता है
- developer को simulator अलग से लिखने की ज़रूरत नहीं पड़ेगी
- जटिलता
gen_serverकी concurrency code की तरह behaviour के पीछे छिपी रहेगी
- FoundationDB की testing के बारे में Kyle “aphyr” Kingsbury ने ट्वीट किया था कि FoundationDB की testing उनकी Jepsen testing से कहीं अधिक कठोर लगती है
- formal verification भी आसान हो जाती है यदि program state machine के रूप में लिखा गया हो
- Lamport का TLA+ model checking कार्य मानता है कि specification state machine है
- Kleppmann ने दिखाया कि state machine structure का उपयोग करके structural induction से state explosion समस्या को कैसे संभाला जा सकता है
- Erlang behaviours से मिली यह संरचना उन समस्याओं को आसान बनाने में फिर से उपयोग की जा सकती है जिन्हें Joe Armstrong ने कठिन बताया था
संबंधित कार्य और संदर्भ सामग्री
- Erlang से ideas लेने वाले संबंधित कार्यों में शामिल हैं
- Martin Thompson के LMAX Disruptor और aeron से ideas लेकर behaviours चलाने के लिए तेज़ event loop बनाना
- state machine type में async I/O जोड़ने का काम
- supervisors को अधिक विस्तार से implement करने का काम
- state machines के hot code swapping
- संदर्भ सामग्री के रूप में Joe Armstrong का PhD thesis, OTP design principles,
gen_server,gen_event,gen_statem,supervisor,application, और release documentation दिए गए हैं - actor model और Erlang के संबंध में निष्कर्ष यह है कि Erlang developers ने actor model जाने बिना Erlang बनाया, और Carl Hewitt का शोधपत्र Erlang process और actor model के बीच के अंतर को document करता है
- Akka में “actors” और supervisor trees हैं, लेकिन Erlang behaviours के अन्य समकक्ष वहाँ नहीं दिखते, और Akka का “behavior” concept भी Erlang behaviours से अलग है
1 टिप्पणियां
Hacker News की राय
Erlang और BEAM की हैरान करने वाली बात उसकी capabilities की गहराई है। मूल लेख में Behaviour/Interface मुख्य impression था, लेकिन मेरे लिए बड़ी बात यह है कि complex systems बनाते समय यह दूसरी languages की तुलना में कहीं कम development resources में संभव हो जाता है
OTP में खुद भी बहुत कुछ शामिल है। मैंने Elixir को iOS devices पर चलाने के लिए compile करने का काम किया था, और Erlang के release process के अलावा ei library का इस्तेमाल करके C में Node compile कर पाया, जो Erlang, Elixir, Gleam वगैरह जैसे दूसरे Erlang nodes के साथ एक सामान्य distributed network की तरह communicate कर सकता था
साथ ही Erlang की rpc library के जरिए C से Elixir application functions call करना भी संभव था। encoding/decoding overhead है और FFI तेज़ होगा, लेकिन यह latency budget के भीतर पर्याप्त रूप से आ गया, और ऐसी capability जिसे मैंने पहले कभी सुना भी नहीं था, कुछ दिनों में खड़ी की जा सकी
बड़ा point यह है कि modern technology stacks जिन कई समस्याओं से जूझते हैं, Erlang ने उन्हें scale और implementation cost के लिहाज से दशकों पहले ही हल कर दिया था। HN में Erlang/Elixir के लिए clickbait जैसी मोहब्बत दिखती है, लेकिन वह actual adoption में नहीं बदली, और ऐसी companies हैं जो Erlang stack में by default मुफ्त मिलने वाली चीज़ों को implement करने में पैसा जला रही हैं
जिस project पर काम किया वह backend data pipeline था और processed data भी ज्यादा नहीं था, लेकिन core bug को ठीक-ठीक isolate करना अविश्वसनीय रूप से मुश्किल था
उस process में Node.js की कई characteristics पता चलीं, और Elixir/Erlang/OTP से तुलना करने पर मैं इस नतीजे पर पहुंचा कि Node.js design के स्तर पर कम reliable है
मैंने Ruby भी काफी की है और Python भी थोड़ा इस्तेमाल किया है, लेकिन current-generation language platforms में से कई reliable distributed systems बनाने में struggle करते हैं। BEAM VM और OTP platform ने वह हिस्सा पहले ही solve कर लिया है
उदाहरण के लिए, ज्यादातर BEAM systems में worker की भूमिका निभाने वाला GenServer आखिरकार simple parameters के साथ कई functions call करने की structure ही है
इसलिए उन functions को सीधे call करके, parameters manually pass करके और output verify करके test किया जा सकता है। async code संभालने के लिए complex test system set up करने या test में काम पूरा होने का इंतजार करते हुए अटकने की जरूरत नहीं होती
juniors अक्सर यह बात miss कर देते हैं, लेकिन समझ में आ जाए तो काफी liberating महसूस होता है
मैंने कुछ लोगों को, मुख्यतः managers को, हमारे experience के आधार पर किताब लिखने की बात करते देखा है। हमारे काम के सफल होने की वजह में वे किस हिस्से को core मानते हैं, यह मुझसे अलग होता था और यह हमेशा frustrating लगा। जिन्हें मैं essential मानता था, वे अक्सर उन्हें nice-to-have तक छोटा कर देते थे
यहां भी कोई कह रहा है कि lightweight processes और message passing secret sauce नहीं हैं, लेकिन वह यह miss कर रहा है कि Erlang को communicating sequential processes (CSP) के रूप में देखने वाला perspective उसकी characteristics से अलग नहीं किया जा सकता। फिर भी वह बार-बार CSP को secret sauce का हिस्सा बताता है
उदाहरण के लिए, application programmer sequential code लिखता है और concurrency behaviour के अंदर छिपी रहती है, या business logic sequential होने से नए team member के लिए शुरू करना आसान होता है, या supervisor और “let it crash” philosophy reliable systems बनाती है—ऐसी बातें
Behaviour दिलचस्प है और 80s में common था, और 2000s में भी कुछ लोग जिस problem को solve कर रहे थे उसे solve करता है, लेकिन Erlang में यह goal भी है और means भी। यह उन दूसरी characteristics को implement करने का तरीका है, लेकिन Erlang को फिर भी Erlang जैसा रहने के लिए ऐसा ही होना जरूरी था या नहीं, यह मुझे नहीं पता
CSP वह दिशा है जिसने occam और दूसरी languages के जरिए Go channels को inspire किया। unbuffered channels पर synchronize करने का तरीका सबसे साफ difference है, और actor model के mailboxes पर pattern matching जैसी differences भी हैं
CSP और actor model की बहस ऊपर से similar दिखती है, लेकिन असली implications काफी अलग हैं, इसलिए यह काफी interesting है
मुझे actor model ज्यादा सही लगता है, लेकिन लोगों के हिसाब से यह काफी अलग हो सकता है
मैं यह जानने आया था कि Ericsson ने Erlang इस्तेमाल करना क्यों बंद किया, और Joe को क्यों निकाला गया
संक्षेप में देखें तो Ericsson ने नए projects को Java पर shift किया, और शायद इसी वजह से Erlang peripheral हो गया। इसके बाद Joe और उनके colleagues ने 1998 में Bluetail बनाई, जिसे Nortel ने acquire कर लिया
Nortel एक telecom giant था जिसकी value Toronto Stock Exchange की लगभग एक-तिहाई थी। 2000 में इसका stock price $125 प्रति share तक गया, लेकिन 2002 में $1 से नीचे गिर गया। यह dot-com crash का हिस्सा था, और telecom spending में तेज गिरावट के साथ Nortel को खास तौर पर बड़ा झटका लगा
Joe की layoff को “डूबते जहाज में उनका department पहले पानी में डूब गया” जैसी स्थिति मानना सुरक्षित लगता है। Nortel ने 60,000 लोगों को, यानी कुल workforce के दो-तिहाई से ज्यादा, निकाल दिया था। वह layoff Joe के अपना काम न कर पाने का संकेत नहीं था, न ही business unit की inefficiency दिखाता था; वह एक desperate large-scale कदम था
“fired” में strong value judgment होता है, और यह nuance देता है कि कोई गलती थी इसलिए cause के साथ निकाला गया। असल में ऐसा हुआ भी हो, तो भी original post के author को यह पता नहीं हो सकता, और पता होना भी जरूरी नहीं है
lightweight processes और message passing की वजह से Erlang को फिर से देखना शुरू किया था, और अब तक behaviour मेरे लिए गौण ही था
प्रोजेक्ट का मकसद visual flow-based programming (FBP) को Erlang में लाना है। FBP ऐसा लगता है जैसे Erlang के लिए ही बना हो, इसलिए यह देखकर हैरानी हुई कि इसका कोई मौजूदा implementation दिखा नहीं
FBP के लिए मैं मुख्य रूप से Node-RED इस्तेमाल करता था, इसलिए बुनियादी idea है Node-RED frontend को Erlang backend से जोड़ना और सभी nodes को processes बनाना। Node-RED frontend nodes के बीच message passing को model करने के लिए अच्छा है, इसलिए Erlang processes और messages से बहुत सरल 1:1 mapping संभव है
कुछ basic features implement कर दिए हैं, और features धीरे-धीरे जोड़ने के लिए flows को unit tests के रूप में बनाना शुरू किया है। Node.js backend वाले Node-RED के साथ 100% compatible हो जाए तो अच्छा होगा। ज्यादा जानकारी GitHub repository में है → https://github.com/gorenje/erlang-red
कुल मिलाकर Erlang इस काम के लिए हैरान करने वाली हद तक फिट बैठता है, और आश्चर्य है कि किसी ने ऐसा कुछ पहले क्यों नहीं किया। क्या शायद पहले से कुछ था?
[1] = https://jpaulm.github.io/fbp/index.html
मेरे लिए Erlang/Elixir की ताकत actor model implementation, Prolog से आई matching, immutability, या behaviour अपने-आप में नहीं, बल्कि Joe की उस इच्छा में है जिसमें वे दिखाना चाहते थे कि कम चीज़ों से ज्यादा किया जा सकता है
यह एक अच्छी तरह designed और verified computation system है, और इसमें ऐसी consistency है जो दूसरी languages तो छोड़िए, “web” दुनिया में भी दुर्लभ है। यह perfect नहीं है, लेकिन काफी प्रभावशाली है
अफसोस है कि software world में simplicity जिन चीज़ों को संभव बनाती है, उसकी पहचान और adoption काफी कम है। complexity लोगों को expert बनाती है, managers को बड़ी teams और ढेर सारी meetings justify करने देती है, और experts को expert बने रहने देती है
Erlang उस समय विकसित हुआ था जब कंपनियां कम लोगों और सीमित performance के साथ software solutions implement करने की कोशिश कर रही थीं। बाद के दशकों में इस क्षेत्र में पैसा बहने लगा, तो “कम का मतलब ऐसे तरीके से ज्यादा है जो सबके लिए अच्छा हो” वाला मूल्य कम आकर्षक दिखने लगा
Erlang/BEAM में सबसे दिलचस्प concept यह है कि partial recovery शुरू से ही built-in है
unexpected state मिलने पर पूरे process को मार देने या चलते रहकर damage accept करने के बजाय, यह संभव सबसे fine-grained level पर ज्ञात normal state में वापस लौटता है
इस idea पर बहुत पहले “microreboots” नाम से research हुई थी और यह “crash-only software” से भी related था, लेकिन केवल Erlang/BEAM ने इसे operating system का first-class concept बनाया
Erlang ऐसे issues से निपटने के लिए अच्छा model देता है, लेकिन इसका मतलब यह नहीं कि सोच-विचार पूरी तरह बंद कर दें। यह सोचकर अंदर जाएंगे कि बस restart होने दें और सब ठीक हो जाएगा, तो हाथ जल सकते हैं
Erlang, OTP, BEAM behaviour से ज्यादा प्रदान करते हैं। VM एक virtual kernel के करीब है, जिसमें supervisor, isolated processes, और distributed mode है जो कई physical/virtual machines को एक resource pool की तरह handle करता है
OTP database Mnesia, caching के लिए atomic counters और ETS tables जैसे कई उपयोगी modes भी देता है। runtime bytecode hot reloading भी support करता है, जिससे system को रोके बिना patches apply किए जा सकते हैं। syntax screen reader-friendly नहीं है, लेकिन पढ़ने लायक है
कई machines के resources को एक pool की तरह handle करने की क्षमता में BEAM जैसा platform याद आता है तो वह शायद Apache Mesos[1] ही है
करीब एक साल पहले मेरी personal consulting company ने backend language के रूप में Erlang अपनाया। उसके बाद TCP-based stack को QUIC में बदलने या Rust patches integrate करने जैसे कामों के जरिए BEAM के internals को explore करना शुरू किया
kernel panic या power loss न हो तो fail न होने वाले lightweight, high-throughput systems के लिए यह सचमुच शानदार choice है। अभी हम film/game production tracker और pipeline manager जैसे बहुत busy और highly concurrent software बना रहे हैं, और private hospital management service R&D की भी तैयारी कर रहे हैं
[1]: https://mesos.apache.org/
Elixir side में जो चीज़ें maintained नहीं हैं, उनके लिए internal bindings या implementations हमें खुद maintain करने पड़ सकते हैं
साथ ही syntax sugar बहुत है, और users में चीज़ों को DSL interface के रूप में abstract करने का एक अजीब जुनून भी दिखता है
“भाषा और लाइब्रेरी डिज़ाइनर lightweight processes और message passing के आइडिया ही क्यों कॉपी करते हैं, Erlang behaviour के पीछे की संरचना क्यों नहीं अपनाते?” इस सवाल की एक वजह है
Erlang behaviour के function signatures, Erlang की दूसरी सुविधाओं—खासकर immutability के असामान्य उपयोग के तरीके—से गहराई से जुड़े हैं। सर्वर को अलग से init call की ज़रूरत भी इसी वजह से होती है, और state management को भी उसी तरह काम करना हो तो उसका एक बहुत साफ़-साफ़ तय रूप चाहिए
लेकिन दूसरी भाषाओं में वही लक्ष्य हासिल करने के लिए लगभग हमेशा Erlang जो करता है उसे जस का तस कॉपी नहीं करना चाहिए। अगर कोई “gen_server को दूसरी भाषा में port किया” कहकर Erlang जैसा बिल्कुल वही interface दिखाता है, तो मैं उसे ऐसा port मानता हूँ जिसमें Erlang क्या कर रहा है इसकी गहरी समझ नहीं है
जब मैंने Go में supervisor tree का आइडिया लाया था[1], तब भी मैंने इसे Go के हिसाब से किया। आधुनिक Go में “supervisable” चीज़ का सही interface Erlang जैसा signature नहीं, बस यह है
type Service interface {
Serve(context.Context)
}
Go में बस यही सब है, और इतना ही इस्तेमाल करना चाहिए। दूसरी भाषाओं में यह अलग हो सकता है। Go में channels हैं, इसलिए “handle_event/2” की ज़रूरत नहीं है, और उनका ही इस्तेमाल करना चाहिए। इसलिए नहीं कि channels बेहतर या खराब हैं, बल्कि इसलिए कि वह भाषा इसी तरह काम करती है
दूसरी भाषाओं में दूसरी चीज़ें इस्तेमाल की जा सकती हैं, और दूसरे infrastructure में “handle_event/2 को call” करने के बजाय Kafka या cloud event bus पर भेजा जा सकता है। मुख्य बात event-driven system बनाना है, Erlang की exact implementation को कॉपी करना नहीं
Erlang community में यह समस्या है कि Erlang जिस exact तरीके से काम करता है उसमें कोई बेहद खास चीज़ है, और अगर आप अलग तरीके से करते हैं तो वह तुरंत गलत और भरोसे लायक नहीं है—इस पर जरूरत से ज्यादा भरोसा किया जाता है। 2005 में शायद ऐसा रहा हो, लेकिन 2025 में नहीं
कभी Erlang लगभग अकेला समझ में आने वाला जवाब था, लेकिन 2025 की समस्या जवाबों की बाढ़ में रास्ता निकालना है। reliable software के लिए Erlang से सीखने की मैं जोरदार सिफारिश करता हूँ, लेकिन Erlang ने जो exact तरीका अपनाया उसे दूसरी भाषाओं में आंख मूंदकर port करने का मैं कड़ा विरोध करता हूँ। लगभग हर दूसरी भाषा के संदर्भ में यह गलत जवाब है। दूसरी immutable भाषाएँ भी संरचना में इतनी अलग हैं कि उन्हें जस का तस कॉपी नहीं किया जा सकता
[1]: https://jerf.org/iri/post/2930/
Elixir को पेशेवर तौर पर इस्तेमाल करने वाले के रूप में, Erlang के बारे में मेरे लिए सबसे दिलचस्प सवाल यही है
k8s, Kafka, और AWS के distributed systems products की बड़ी सफलता देखकर साफ़ है कि reliable concurrent systems design में मदद करने वाले tools की मांग बहुत बड़ी है। फिर Erlang/Elixir उस हिस्से को क्यों नहीं पकड़ पाए?
दोस्तों के साथ इस पर अक्सर चर्चा करता हूँ, लेकिन जवाब नहीं पता
मुझे लगता है Erlang ने network message handling को abstract किया होगा। ऊपर से, long-running servers जैसी चीज़ों के लिए सुझाए गए pattern के 3–4 variants तो मैंने देखे हैं
port करते समय idiomatic building blocks इस्तेमाल करने चाहिए—इससे मैं पूरी तरह सहमत हूँ। लेकिन भाषाओं में ऐसे छिपे mechanisms हो सकते हैं जो porting के दौरान मूल्यवान सार को खो देते हैं। इसे एक तरह का programming language anti-relativism कह सकते हैं
“Channel? बस उसे X में wrap कर दो” सुनने में जितना लगता है, उससे कहीं ज्यादा interoperability के लिए नुकसानदेह हो सकता है। उदाहरण के लिए Go का http.Handler देखें: यह सरल है, लेकिन standard library में होने के practical implications बहुत बड़े हैं। बिना पहले से coordination किए भी आम तौर पर एक-दूसरे के साथ compatible middleware ecosystem बन जाता है
io.Reader और उसके साथी भी ऐसे ही हैं। ऐसे बेहद सरल interfaces implementation से भी ज्यादा मूल्यवान माने जा सकते हैं
मेरा अनुमान है कि अगर Erlang ने reliable distributed systems के लिए कई interfaces सही पकड़े थे, तो शायद वही चीज़ पूरी व्यवस्था को संभव बनाने वाला तत्व रही हो
सहमत नहीं हूँ। interface एक मामूली concept है जिसे किसी भी भाषा पर लगाया जा सकता है। जिन भाषाओं में official interface syntax नहीं है, उनमें भी program space में इसकी नकल की जा सकती है
BEAM के सफल होने की वजह यह है कि वह एक single node पर 10 लाख processes चला सकता है, complex distributed state machines को आसानी से express कर सकता है, और system के कुछ हिस्सों को बिना downtime restart कर सकता है। इसके अलावा भी बहुत-सी वजहें हैं
मुझे सच में नहीं लगता कि behaviour/interface सबसे मुख्य हिस्सा है
isolated process heap चाहिए तो OS processes इस्तेमाल कर लो, supervision tree चाहिए तो Kubernetes इस्तेमाल कर लो, message passing Java में दो threads और shared queue से बना सकते हो, hot code loading Java में भी संभव है, low-latency processing के लिए LMAX Disruptor को tune कर लो तो शायद Erlang को हरा भी दे—कुछ ऐसा
लेकिन इन सबको एक platform या library में इकट्ठा करना ही मुख्य बात है। OS processes भारी होते हैं, और server पर 20 लाख चलाना आसान नहीं है। green threads या promises इस्तेमाल करें तो isolated heap खो देते हैं
Kubernetes भी कुछ हद तक इस्तेमाल हो सकता है, लेकिन nested supervision trees को अच्छी तरह handle नहीं करता। संभव तो होगा, लेकिन code के अलावा pods, controllers, volumes जैसी तमाम चीज़ें आ जाएँगी
कई भाषाओं की actor libraries से message passing किया जा सकता है, लेकिन receive में pattern matching करना या दूसरे node के दूसरे thread को भेजना transparently integrate नहीं हो पाता
hot code loading भी संभव है, लेकिन runtime data structures और state को कैसे handle किया जाए यह समस्या है। Erlang इसी को केंद्र में रखकर बनाया गया है, और gen_server में state immutable और explicit होती है, इसलिए उसमें सिर्फ code ही नहीं बल्कि state itself को upgrade करने वाला callback भी होता है
मैं यह नहीं कहूँगा कि BEAM इस समस्या को खत्म कर देता है, लेकिन लगता है कि वह line की slope को कम कर देता है। deployment, automatic recovery, और load balancing सहित self-consistent idioms और features modules के बीच friction को घटाते हैं
यह ऐसा system बनाने देता है जिसमें 12 engineers आसानी से 30 endpoints manage कर सकें, और surface area फिर भी power law follow कर सके
मैं इस लेख की बात से सहमत नहीं हूँ। Behaviour सिस्टम के बेस आर्किटेक्चर की वजह से संभव होता है
Behaviour कोई interface नहीं है, बल्कि Java जैसी भाषाओं के abstract object के ज्यादा करीब है। यह सहयोगी interface के पीछे छिपी बुनियादी और self-contained functionality को implement करता है, लेकिन उस underlying infrastructure के बिना यह ज्यादा कुछ नहीं कर सकता, जो यह सुनिश्चित करता है कि हर process दूसरे process से पूरी तरह अलग रहे, सभी processes को memory या resource leak के बिना सुरक्षित रूप से बंद किया जा सके, और दो processes के बीच खतरनाक pointers share न किए जा सकें
Joe ने अपने paper में दिखाया था कि दिए गए Lego blocks के सेट से भरोसेमंद system, और आगे चलकर कुछ हद तक भरोसेमंद distributed system, कैसे बनाया जा सकता है
ऐसी चीज़ को सही तरीके से implement करने के लिए Erlang VM चाहिए, और इसे किसी दूसरे VM में पूरी तरह implement नहीं किया जा सकता। नीचे की plumbing न हो तो supervision tree leak करने लगेगा। Java में आप resources पकड़े हुए thread को kill करके यह उम्मीद नहीं कर सकते कि सब कुछ हमेशा ठीक चलेगा, और अलग-अलग processes को monitor करने के तरीके भी पर्याप्त नहीं हैं