開発現場における並行処理の留意点

https://www.youtube.com/watch?v=RR62KqHEVfM

1 comment | 1 point | by WazanovaNews 3年弱前 edited


Jshiike 3年弱前 edited | ▲upvoteする | link

Squareが主催した並行処理システムに関するパネルディスカッション。

まずは、並行処理理論で博士号を取得し、Sqaureで分析システムを担当するGian Perroneがハイレベルでの留意点を挙げています。

  • システム全体で起きていることを自分の頭で完璧に把握しようという姿勢は、正確な並行処理システムをつくる妨げになる。テクノロジーやツールをうまく利用すべき。
  • 並行処理システムを漏れなくテストするというのは相当難しい。
  • 自分で考えて、注意深く対処するというのでは不十分。うまく抽象化に頼るべき。

続いて同社のTamir Dubersteinが、以前データセンタを移行させたときに経験したバグについて、

非同期処理するJavaシステムと、Rubyなのでリアルな並行処理ではなかったが、Javaが実行するたびにプロセスをフォークし、Javaが新しい子プロセスを走らせる仕組みだった。新しいデータセンタに移行したらスループットが急減。straceで確認すると、データベースとのやりとりで子プロセスがタイムアウト & 再接続を繰り返していて、この事象がJavaのランタイムを独占していた。調査を進めると、読み込む内容によって、プログラムを実行すると再現できる場合とできない場合にわかれた。例えば、なぜかbundlerを読み込むとバグが発生しない。どうなっていたかというと、データベースライブラリがガベージコレクションを実装していた箇所で、それがRubyのC拡張のDEALLOCATEで、参照カウントがゼロになると行の最後でMySQLの接続をリリース。親プロセスがフォークして子プロセスが機能するところで、新しい環境では、突然ガベージコレクションが起動して「クローズするよ。」と宣言しているのに、親プロセスがそれを知らないで次のジョブもフォークして、子プロセスに接続が切れているものを渡していた。そこで、同じスレッドでない限りは、ガベージコレクタが共有リソースを邪魔しないように修正した。抽象化で解決できるところでないし、コンパイラがオーナーシップを妨げないようにチェックしてくれる訳ではない。一方、最近だとRustは能動的な方法を採っていて、明示的にオーナーシップという概念がある。メモリのこの部分を誰がオーナーシップを持っているかがはっきりしていて、borrowチェッカーが働いて、別の利用者が煩わせたりしないように担保してくれる。

Square Cashの開発を担当するJochen Bekmannは、

明示的するということはステートの話になる。分散システムにおいては、プロトコル、ネットワークプロトコル、APIなど。ネットワークだとパケットロスしたり、パケットが異なる順番で到着してくるし、ステートマシンと通信していると、期待する振る舞いを明示しないと、ステート変数、オーナーシップなどが絡み、簡単にトラブルに遭ってしまう。常にチェックしないと。特にネットワークプロトコルにおいては、よい実装はしっかり明示したステート表現 や状態遷移図、ステートマシンが必要だと思う。

また、同社でペイメントゲートウェイの開発を担当するRandy Wiggintonは、なんとAppleの6番目の社員だったという業界の大ベテラン。

失敗に備えること。常にすべてがうまくいかないという前提にたつべき。

ペイメントプラットフォーム開発担当のManik Surtaniがフォローとして、

失敗に備えるといっても、すぐに障害が表面化しない場合がある、MySQLのタイムアウトは想定よりもずっと時間がかかる。最近も2時間のケースがあった。分散システムにおいては、コールしたら明示的に期限を決めて対処する工夫が必要。

Jochenが補足して、

テストで全てカバーするのが至難の技としても、ランダムに設定を変えて数万回実行してみるなど、できる限りのトライをしてエッジケースを見つける努力はするべき。

「xは必ずやるべき。yをやってはいけないという言葉を信じるか?例えば、実行するスレッド数は必ずコアの数に収めなくてはいけないとか。」という質問に対してパネリスト達の回答は、

  • 信じない。簡単なプロファイルをすれば自分で確認できるし、するべき。
  • 本当に信じるに足るものでも、自分の頭の中で管理するのは得策ではない。それを強制してくれるツールを使うべき。
  • 並行処理システムは安易に利用されるが、必ず効率的だというのは幻想。あらゆる競合状態が起こりうるし、それを書くのにコストもかけている。結局は、かなりシンプルなスレッドモデル、ワーカーモデルで、一つのロックでジョブをアロケートする仕組みでもっと高いスループットを実現できる場合もある。コミュニケーションコストが大きすぎて、スレッドを増やしてもスループットが改善しないケースもある。

状態遷移図やステートマシンの重要性に賛同する議論の後に、Manik曰く、

一つ問題提起しておきたいのは、immutabilityについて。immutabilityを採用している開発言語やシステムは多くて、どうなっているかわかりやすいという利点は確かにある。メッセージを渡したりする際に、実際にはコピーして、参照は渡していなくて、簡単にできる。しかし、CPUキャッシュも利用しないとなるとパフォーマンス問題になる。ステートを共有している場合に実際に何が起こるかというのを認識するようになってほしい。immutabileであるということは必ずしも表現が明示的ということにはならない。

それに対して、Tamirは、

それは、よくない実装と言えるのでは。immutabilityとかシステム内で何が起きているかではない。自分は値がコピーされているかどうかは気にしていないくて、誰かのステートを妨げていないという確証をくれるツールがほしいだけ。データ構成はmutableだけど、バインディングされるシンボルでimmutableを実現するという言語もある。適切なツールを使えば、どちらの利点も取り込んだうえで解決できる問題だと思う。

RandyとGianは、DBでのステート管理の危険性に言及し、

  • DBは情報を保存するのに最適な場所だが、実務的にはステートを保存するのに使ってしまっている。誰かがステートを変更してしまうと、何が起きているかわからなくなる。記録されてるデータやテーブルの不整合など頭の痛い問題になる。
  • DBの興味深いところは、どれだけ整合性がしっかりしているかというレベルでは、リレーショナルDBがトランザクションについて最強の保証をしてくれる。しかし、例えば、ロックをしたトランザクションを、分散環境において一連のレプリされたDBホストに書き込もうとすると収拾がつかなくなる。本当にやりたいのはアトミックに一つづつスワップ(?)
  • トランザクションがコントロール不能になることはありえて、ロックしているが、実際にはロックしていることに気がつかないなんてこともある。
  • DBテーブルに保存されたデータは、ある意味機能的にはロックになるが、DBの機能にはロックにアクセスすることを防ぐ機能がある。それ以外にも競合状態になるノードもある。まるで共有したスクラッチパッドを使って、コーディネーションシステム、オーケストレーションシステムをつくりあげなくてはいけないという、危ない状況に陥る。

最後にGianは、R. Milnerが1989年にまとめた「Communication and Concurrency」を一読することを勧めています。


2014 Topアクセスランキング


Back