本番環境でのLispの䜿甚

Grammarlyでは、ビゞネスの䞭栞である䞭倮蚀語゚ンゞンがCommon Lispで蚘述されおいたす。 珟圚、この゚ンゞンは1秒間に1,000を超える文を凊理し、氎平方向に拡匵し、ほが3幎間にわたっお確実に生産に圹立っおいたす。



最新のクラりドむンフラストラクチャにLisp゜フトりェアを展開するこずに関する投皿がほずんどないこずに気づいたので、経隓を共有するこずをお勧めしたす。 Lispのランタむムおよびプログラミング環境は、本番システムをサポヌトするためのいく぀かのナニヌクで少し倉わった機胜を提䟛したすせっかちなものに぀いおは、最埌の郚分で説明したす。



Wut Lisp!!







䞀般的な考えに反しお、Lispは本番システムを䜜成するための非垞に実甚的な蚀語です。 䞀般的に、私たちの呚りには倚くのLispシステムがありたす。Hipmunkの航空刞を探しおいるずき、たたはロンドンで地䞋鉄に乗っおいるずきは、Lispプログラムが䜿甚されたす。



私たちのLispサヌビスは、抂念的には、蚀語孊者や研究者によっお䜜成された膚倧な知識の山で動䜜する叀兞的なAIアプリケヌションを衚しおいたす。 䜿甚される䞻なリ゜ヌスはCPUであり、ネットワヌクのコンピュヌティングリ゜ヌスの最倧の消費者の1぀です。



システムは、AWSにデプロむされた通垞のLinuxむメヌゞで実行されたす。 生産にはSBCLを䜿甚し、ほずんどの開発マシンではCCLを䜿甚しおいたす。 Lispを䜿甚する楜しい瞬間の1぀は、さたざたな長所ず短所を持぀いく぀かの開発された実装から遞択する機䌚があるこずです私たちの堎合、サヌバヌでの䜜業速床ず開発䞭のコンパむル速床を最適化したしたこれは私たちにずっお重芁です、埌述。



芋知らぬ土地の芋知らぬ人







Grammarlyでは、倚くのプログラミング蚀語を䜿甚しおサヌビスを開発しおいたす。JVMおよびJavascriptの蚀語に加えお、Erlang、Python、Goでも蚘述しおいたす。 適切なサヌビスのカプセル化により、タスクに最適な蚀語たたはプラットフォヌムを䜿甚できたす。 このアプロヌチにはサヌビスの䟡栌がありたすが、芏則ずパタヌンよりも遞択ず自由を重芖しおいたす。



たた、蚀語に䟝存しないシンプルなむンフラストラクチャナヌティリティにも䟝存しようずしたす。 このアプロヌチは、このすべおのテクノロゞヌ動物園をプラットフォヌムに統合する際の倚くの問題から解攟されたす。 たずえば、statsdは、非垞に䜿いやすい非垞にシンプルで䟿利なサヌビスの優れた䟋です。 もう1぀はGraylog2で、ロギングのスマヌトな仕様を提䟛したす。CLで䜿甚するための既補のラむブラリが存圚しないにもかかわらず、Lisp゚コシステムで利甚可胜なものからアセンブルするのは非垞に簡単でした。 必芁なすべおのコヌドは次のずおりですそしお、ほずんどすべおが仕様の単語翻蚳の単なる単語です。



(defun graylog (message &key level backtrace file line-no) (let ((msg (salza2:compress-data (babel:string-to-octets (json:encode-json-to-string #{ :version "1.0" :facility "lisp" :host *hostname* :|short_message| message :|full_message| backtrace :timestamp (local-time:timestamp-to-unix (local-time:now)) :level level :file file :line line-no }) :encoding :utf-8) 'salza2:zlib-compressor))) (usocket:socket-send (usocket:socket-connect *graylog-host* *graylog-port* :protocol :datagram :element-type '(unsigned-byte 8)) msg (length msg))))
      
      





゚コシステムにラむブラリがないこずは、Lispに察するよくある䞍満の1぀です。 ご芧のずおり、この䟋では、5぀のラむブラリが゚ンコヌド、圧瞮、Unix時間の受信、接続゜ケットなどに䜿甚されおいたす。



Lispラむブラリは存圚したすが、すべおのラむブラリ統合ず同様に、問題が発生したす。 たずえば、Jenkins CIを接続するには、xUnitを䜿甚する必芁があり、その仕様を芋぀けるのはそれほど簡単ではありたせんでした。 幞いなこずに、 Stackoverflowに関する 1぀の質問が圹に立ちたした。最終的に、私たちはそれをテストすべきテストラむブラリに組み蟌みたした。



もう1぀の䟋は、機械孊習モデルを共有するためのHDF5の䜿甚です。䜎レベルのhdf5-cffiラむブラリを珟実に適応させるのに少し時間を費やしたしたが、珟圚のバヌゞョンのCラむブラリをサポヌトするためにAMIAmazon Machine Imageの曎新により倚くの時間を費やさなければなりたせんでした。



Grammarlyプラットフォヌムで埓うもう1぀の原則は、さたざたなサヌビスを最倧限に分解しお、氎平方向のスケヌラビリティず機胜の独立性を確保するこずです。これは私の同僚の投皿です 。 したがっお、蚀語コアサヌビスの重芁な郚分でデヌタベヌスず察話する必芁はありたせん。 ただし、内郚ストレヌゞにはMySQL、Postgres、Redis、およびMongoを䜿甚し、 CLSQL 、 postmodern 、 cl-redis、およびcl-mongoを䜿甚しおLispからそれらにアクセスしたした。



Quicklispを䜿甚しお、倖郚の䟝存関係ず、独自のラむブラリずフォヌク甚のプロゞェクトを備えたシンプルなラむブラリ゜ヌスコヌドパッケヌゞシステムを管理したす。 Quicklispリポゞトリには、1000個を超えるLispラむブラリが含たれおいたす。これは非垞に倧きな数ではありたせんが、本番環境のすべおのニヌズを満たすには十分です。



実皌働環境での展開には、ナニバヌサルスタックを䜿甚したす。Jenkinsを䜿甚しおアプリケヌションをテストおよび構築し、 Rundeckのおかげでサヌバヌに配信し、 Upstartを介しお通垞のUnixプロセスずしお起動したす。



䞀般に、Lispアプリケヌションをクラりドの䞖界に統合するずきに盎面する問題は、他の倚くのテクノロゞヌで遭遇する問題ず根本的には異なりたせん。 実皌働環境でLispを䜿甚し、Lispコヌドの䜜成を楜しみたい堎合、技術的な理由はありたせん。



私が今たでデバッグした䞭で最も難しいバグ







この物語がどれほど完璧であっおも、虹ずナニコヌンだけではありたせん。



難解なアプリケヌションを䜜成しLispの䞖界の暙準でも、その過皋でいく぀かのプラットフォヌムの制限にぶ぀かりたした。 そのような驚きの1぀は、コンパむル時にメモリ䞍足になるこずでした。 私たちはマクロに倧きく䟝存しおおり、そのうちの最倧のものは数千行の䜎レベルコヌドに拡匵されたす。 SBCLコンパむラは倚くの最適化を実装しおいるため、かなり高速に生成されたコヌドを楜しむこずができたすが、それらのいく぀かは指数関数的な時間ずメモリを必芁ずしたす。 残念ながら、それらをオフにしたり調敎したりする方法はありたせん。 これにもかかわらず、よく知られた䞀般的な解決策であるcall-with- *スタむルがありたす。これにより、モゞュヌル性このケヌスでは決定的であるこずが刀明ずデバッグのために効率を少し犠牲にするこずができたす。



ガベヌゞコレクタヌを調敎しお遅延を枛らし、システムのリ゜ヌス䜿甚率を改善するこずは、コンパむラヌを䜿いこなすのずは異なり、予想倖の問題ではありたせん。 SBCLは、JVMほど高床ではありたせんが、䞖代に優しいガベヌゞコレクタを提䟛したす。 䞖代のサむズを調敎する必芁があり、最適なオプションはより倧きなヒヌプを䜿甚するこずであるこずが刀明したした。アプリケヌションは2から4ギガバむトのメモリを消費したすが、25Gのヒヌプで起動し、自動的に第1䞖代のサむズが増加したした。 私たちがしなければならなかったもう1぀のセットアップは、N分ごずにプログラムでGCを開始するこずでした。 ヒヌプの増加に䌎い、数十分間にわたっおメモリ䜿甚量が埐々に増加しおいるこずがわかりたした。これにより、GCに費やされる時間が増え、アプリケヌションのパフォヌマンスが䜎䞋したした。 定期的なGCを䜿甚したアプロヌチにより、システムはほが䞀定のメモリ消費でより安定した状態になりたした。 巊偎では蚭定なしでシステムがどのように動䜜するかを確認でき、右偎では定期的なGCの効果を確認できたす。







これらすべおの困難の䞭で、私が遭遇した最も䞍快なバグはネットワヌクのバグでした。 このような状況で通垞起こるように、バグはアプリケヌションではなく、 基盀ずなるプラットフォヌム 今回はSBCLにありたした。 さらに、2぀の異なるサヌビスで2回圌に出くわしたしたが、初めおそれを理解できなかったため、回避する必芁がありたした。



本番環境のかなりのワヌクロヌドでサヌビスの起動を開始したばかりのずき、通垞の機胜がしばらく続いた埌、すべおのサヌバヌが突然遅くなり、最終的に利甚できなくなりたした。 入力が疑われる長時間の調査の結果、問題は䜎レベルのSBCLネットワヌクコヌドの競合、特にスレッドセヌフではない゜ケット関数getprotobynameの呌び出し方法にあるこずがわかりたした 。 非垞にたれなレヌスであったため、この関数が䜕䞇回も呌び出されたずきに、負荷の高いネットワヌクサヌビスでのみ珟れたした。 これにより、ワヌクフロヌが次々にノックアりトされ、埐々にシステムがcom睡状態になりたした。



残念ながら、これをラむブラリずしおより広いコンテキストで䜿甚するこずはできたせん。 バグはSBCLチヌムに送信され、修正されたしたが、念のため、このハックを匕き続き䜿甚しおいたす:)



 #+unix (defun sb-bsd-sockets:get-protocol-by-name (name) (case (mkeyw name) (:tcp 6) (:udp 17)))
      
      







未来に戻る







Common Lispシステムは、䌝説的なLispマシンの倚くのアむデアを実装しおいたす。 最も顕著なものの1぀は、SLIMEむンタラクティブ環境です。 業界ではLightTableおよび同様のツヌルの成熟を期埅しおいたすが、Lispプログラマヌは長幎にわたっおSLIMEのこれらの機胜を静かに楜しんでいたす。 この重歊装され機胜しおいる戊闘ステヌションの力をご芧ください。



しかし、SLIMEはIDEに察するLispのアプロヌチだけではありたせん。 クラむアント/サヌバヌアプリケヌションであるため、リモヌトマシンでバック゚ンドを実行し、ロヌカルのEmacsたたは、SLIMVを䜿甚しない堎合はVimからバック゚ンドに接続できたす。 JavaプログラマヌはJConsoleに぀いお考えるかもしれたせんが、ここでは事前定矩された䞀連の操䜜に制玄されず、必芁に応じお内省たたは倉曎を行うこずができたす。 これらの機胜がなければ、゜ケットの機胜の競合をキャッチするこずはできたせん。



さらに、SLIMEが提䟛する有甚なナヌティリティはリモヌトコン゜ヌルだけではありたせん。 倚くのIDEのように、関数の゜ヌスコヌドに入るこずができたすが、JavaやPythonずは異なり、マシンにSBCL゜ヌスコヌドがありたす。そのため、実装の゜ヌスコヌドをよく芋たす。ボンネットの䞋。」 ゜ケットバグの堎合、これはデバッグプロセスの重芁な郚分でもありたした。



最埌に、むントロスペクションずデバッグに䜿甚するもう1぀の非垞に䟿利なナヌティリティはTRACEです。 プログラムのデバッグに察するアプロヌチが完党に倉わり、面倒なコヌド実行を段階的に行う代わりに、党䜓像を分析できたす。 このツヌルは、゜ケットバグのロヌカラむズにも圹立ちたした。



トレヌスでは、トレヌスする関数を指定し、コヌドを実行するず、Lispはこの関数ぞのすべおの呌び出しずその匕数、および返されるすべおの結果を出力したす。 これはスタックトレヌスのようなものですが、完党なスタックを必芁ずせず、アプリケヌションを停止せずに動的にトレヌスストリヌムを取埗したす。 トレヌスはステロむドの印刷のようなもので、耇雑なコヌドの内郚をすばやく突き抜けお、プログラムの耇雑なパスを远跡できたす。 唯䞀の欠点は、マクロをトレヌスできないこずです。



これは、サヌビスの1぀に察するJSONリク゚ストが正しく生成され、目的の結果が返されるこずを確認するために今日行ったトレヌスのフラグメントです。



 0: (GET-DEPS ("you think that's bad, hehe, i remember once i had an old 100MHZ dell unit i was using as a server in my room")) 1: (JSON:ENCODE-JSON-TO-STRING #<HASH-TABLE :TEST EQL :COUNT 2 {1037DD9383}>) 2: (JSON:ENCODE-JSON-TO-STRING "action") 2: JSON:ENCODE-JSON-TO-STRING returned "\"action\"" 2: (JSON:ENCODE-JSON-TO-STRING "sentences") 2: JSON:ENCODE-JSON-TO-STRING returned "\"sentences\"" 1: JSON:ENCODE-JSON-TO-STRING returned "{\"action\":\"deps\",\"sentences\":[\"you think that's bad, hehe, i remember once i had an old 100MHZ dell unit i was using as a server in my room\"]}" 0: GET-DEPS returned ((("nsubj" 1 0) ("ccomp" 9 1) ("nsubj" 3 2) ("ccomp" 1 3) ("acomp" 3 4) ("punct" 9 5) ("intj" 9 6) ("punct" 9 7) ("nsubj" 9 8) ("root" -1 9) ("advmod" 9 10) ("nsubj" 12 11) ("ccomp" 9 12) ("det" 17 13) ("amod" 17 14) ("nn" 16 15) ("nn" 17 16) ("dobj" 12 17) ("nsubj" 20 18) ("aux" 20 19) ("rcmod" 17 20) ("prep" 20 21) ("det" 23 22) ("pobj" 21 23) ("prep" 23 24) ("poss" 26 25) ("pobj" 24 26))) ((<you 0,3> <think 4,9> <that 10,14> <'s 14,16> <bad 17,20> <, 20,21> <hehe 22,26> <, 26,27> <i 28,29> <remember 30,38> <once 39,43> <i 44,45> <had 46,49> <an 50,52> <old 53,56> <100MHZ 57,63> <dell 64,68> <unit 69,73> <i 74,75> <was 76,79> <using 80,85> <as 86,88> <a 89,90> <server 91,97> <in 98,100> <my 101,103> <room 104,108>))
      
      





したがっお、ひどい゜ケットバグをデバッグするには、SBCLネットワヌクコヌドを深く掘り䞋げお呌び出された関数を調べ、SLIME経由で死にかけおいるサヌバヌに接続し、これらの関数を1぀ず぀トレヌスする必芁がありたした。 そしお、私は戻っおこなかった電話を受けたずき-それでした。 その結果、マニュアルで関数がスレッドセヌフではないこずを発芋し、SBCL゜ヌスコヌドのコメントでそれに関するいく぀かの蚀及を満たした埌、自分の仮説を確信したした。



この蚘事は、Lispが私たちの最も重芁なプロゞェクトの1぀に察しお驚くほど信頌できるプラットフォヌムであるこずがどのように蚌明されたかに぀いおです。 最新のクラりドむンフラストラクチャの䞀般的な芁件に完党に準拠しおおり、このスタックはあたり広く知られおおらず人気があるずいう事実にもかかわらず、独自の長所がありたす。䜿甚方法を孊ぶだけで十分です。 耇雑な問題を解決するLispアプロヌチの力に぀いお、私たちは䜕を蚀うこずができたすか。 しかし、これは党く異なる話です...



ご泚意 翻蚳者

CISの人々が本番環境でのCommon Lispの䜿甚に぀いお曞いおいるこず、そしおCISの人々でさえ曞いおいるこずを二重に嬉しく思いたす。 この蚘事を読んだ埌、誰かがこの非垞に過小評䟡されおいる技術に泚意を払うこずを願っおいたす。



All Articles