HN 日本語サマリー

← 一覧へ戻る
プログラミング

ジョブキューは見た目よりずっと複雑だ

Job queues are deceptively tricky (typesanitizer.com)

109 pointsby ingve36 コメント

要約

この記事では、一見単純に見えるジョブキューシステムが持つ隠れた複雑性について解説します。特に、リファレンスリポジトリのバックグラウンドパッキングという具体的な問題を通して、スケジューリング間隔、同時実行制限、そしてそれらが予期せぬ挙動を引き起こす可能性を探ります。システム設計における「キューへの警戒」「制限」「フォールトモデル」といった視点が、この複雑性を理解する上で役立つことを示唆しています。

全文翻訳

ジョブキューは見た目よりずっと複雑だ プログラマーとして、あまり知らなかったシステムを深く掘り下げていくと、表面的には単純に見えるシステムが、実際には根底にある複雑な側面を明らかにすることがあります。言い換えれば、現実は驚くほど詳細に満ちているのです。この記事では、ここ数日間考えているジョブキューについて話したいと思います(そして、この記事を頭の中で下書きしている間に、以前の職場でそれについてもっと長く考えていたことに気づきましたが、それほど明確ではありませんでした)。「ジョブキュー」とはどういう意味か? 私は、バッチジョブの投入、スケジューリング、実行という概念があるシステムを意味します。一般的に、システムはFIFOまたはFIFOライクであることが期待されますが、それは必須ではありません。通常、キューは定期的にジョブをスケジューリングするためのネイティブな方法をバンドルしています。これにより、設定ファイル(JSONやYAMLなど)を使用して提出を指定できます。ジョブキューは、スループット要件が高いがレイテンシ要件はそれほど高くないあらゆる状況で自然に発生します。継続的インテグレーションは一般的な例です。別の例は、データ分析目的の要約です。過去1〜2年間で、システム設計を考える上で役立つと感じたレンズがいくつかあります:キューへの警戒:キューは通常、ほぼ満杯かほぼ空の状態になりがちで、適切なサイジングに関する注意が必要です。また、Marc Brookerのブログから、レイテンシに関するキューの直感に反する動作についても多く学びました。私が読んだキューに関するブログ記事では、焦点は一般的にレイテンシにあり、スループットにはありません。これが、私が「キュー」と「ジョブキュー」を以前に結びつけなかった理由の一部だと思います。制限:これはTigerBeetleのチームが書いたTiger Styleガイドに基づいています。このガイドでは、さまざまなものに対する明示的な制限について説明しています。一般化すると、これは許容誤差の概念と、モジュラー推論のための予算を持つことにつながります。面白いことに、キャリアの初期の頃、Appleで、特に低レベルコードのために、チームがどれだけのメモリ予算を交渉しなければならないかといった議論に、私は少し当惑していたのを覚えています。私は時間の経過とともにそのアプローチへの敬意を深めました。フォールトモデル:大まかに言うと、フォールトモデルは、依存関係におけるエラーと信頼性に関する仮定を記述します。私は、TigerBeetleのCEOであるJoran Dirk Greefの講演やツイート、そしてAlex Millerの著作からこのトピックについて多くを学びました。これらのレンズがシステム設計にどのように適用できるかを、この記事の後半で実証しようと思います。問題:仕事で、私たちは「リファレンスリポジトリ」をパッキングするためのバックグラウンドジョブを持っています。リファレンスリポジトリとは、積極的にリパックされたgitリポジトリのことです。リパッキングに慣れていない場合、これは「より積極的に圧縮された」と考えてください。これらはオブジェクトストレージに保存され、新しいリポジトリは、リファレンスリポジトリをダウンロードし、デフォルトブランチの先端へのデルタ変更を取得することで、マシン上にセットアップできます。非常に大きなリポジトリに関しては、ダウンローダーにとって、このアプローチは、より一般的なgit clone操作を行う場合と比較してレイテンシを削減するのに役立ちます。gitが積極的にリパッキングをどのように行うかの実際の詳細は、この記事ではあまり関係ありません。重要なのは、大まかに言うと、2つの形式のリパッキングが利用可能であるということです:全体的なリパッキング:これは、gitがパッキングの方法に関するベースライン情報を無視し、すべてをゼロから再計算することを意味します。テスト中のリポジトリの場合、これは7時間かかります。git操作に数時間かかるという考えに驚いているなら、(1)この問題がないことを幸せに思い、(2)ここでのサブ操作の一部は、リポジトリサイズに関係なく、明らかにシングルスレッドであるということです。インクリメンタルリパッキング:これは、gitがリポジトリに既に保存されているパッキングに関するベースライン情報を再利用することを意味するため、変更されたものだけをリパックします。したがって、クローンを実行してからフェッチを実行した場合、フェッチからの新しい変更のみがリパックされ、クローン操作から受信した履歴に対してのみ簡単なチェックが実行されます。これは2時間かかるとします。OK。それがセットアップです。私たちは、よりコストのかかる、7時間かかる処理を行うオプションがあります。これにより、より小さなリファレンスリポジトリが得られ、ダウンロードが速くなり、アンタリングが速くなり、ディスク使用量が削減されます。サイズ感を伝えるために、全体的にリパックされたリポジトリは、インクリメンタルにリパックされたリポジトリよりも50〜60%小さくなる可能性があります。私たちは、より安価な、2時間かかる処理を行うオプションがあります。これにより、より最新のリファレンスリポジトリが得られます。つまり、ダウンローダーが最新の変更を取得することに関心がある場合、 subsequently取得するデルタは小さくなり、サーバーへの負荷が軽減されます。しかし、追加のディスク使用量が数百メガバイトまたはギガバイトの場合、それは良くありません。もう1つ注意すべき点は、これがコードリポジトリであるため、下流の消費者は週末よりも平日にずっとアクティブであるということです。両方の世界の最良の選択肢? 上記の二分法を見た後の自然な次のステップは、次のことを提案することです。なぜ週末に全体的なリパッキングを行い、平日にインクリメンタルリパッキングを行うのではないか? これは、平日に最新の状態を保つ(より多くの消費者がアクティブな場所)という利点があり、週末の全体的なリパッキングは、リファレンスリポジトリのサイズがよりゆっくりと成長することを保証します。簡単にするために、リファレンスリポジトリをキー <myrepo>-<timestamp>.tar のような形式でバケットに書き込むとしましょう。インクリメンタルにリパックされたリポジトリのサイズを小さく保つために、次のいずれかを行うことができます:全体的にリパックされたリポジトリを同じキーのスキームで書き込み、インクリメンタルリパッキングジョブがクローン操作からではなく、最後の成功したリパック(全体的またはインクリメンタル)からブートストラップするようにします。全体的にリパックされたリポジトリを、<myrepo>-<timestamp>-packed.tar のような変更されたキーのスキームで書き込み、インクリメンタルリパックをそのキーの場所からブートストラップします。ただし、これは他のコンシューマーが週末に遅れすぎないように更新する必要が生じる可能性があります(インクリメンタルリパッキングが週末に実行されないと仮定します)。最初の方法を選択するとしましょう。それはよりシンプルに見えるからです。では、このスケジューリングをどのように処理するかが問題になります。典型的なジョブキューは、正当な理由から、クライアントに提供するスケジューリング制御を制限しています。多くのノブを提供すると、予期しないスケジューリング決定のリスクが増加します。私が上記の「自然な次のステップ」を書いたとき、私は本質的に制御ループを書くという観点から考えていました。しかし、ジョブキューを使用するとき、私は(定義上)制御ループにアクセスできません。誰かがそのループを書いており、動作をカスタマイズするために利用可能な設定ノブを確認する必要があります。とりあえず、キューが2つの設定ノブを提供するとしましょう:スケジューリング間隔:ジョブは、この間隔で設定されたタイマーで開始されます。同時実行制限:特定の設定で実行中のジョブの最大数。この最適化ジャーニーを開始する前に、これらの設定ノブは次のように設定されていたとしましょう:スケジューリング間隔:9時間。これは7時間のジョブ実行時間から余裕を持たせるためです。同時実行制限:1。ここでは、同じことをする同時実行ジョブがあってもあまり意味がありません。ここでも、自然な次のステップは、「9時間の間隔は7時間実行されるジョブには十分でした。平日のインクリメンタルリパックジョブは2時間かかるので、間隔を3時間に設定しましょう。週末には、全体的なリパッキングジョブが実行されている場合、3時間では完了しないため、タイマーが再度トリガーしても、同時実行制限により他のジョブが実行できなくなります。すべてうまくいくはずです。」残念ながら、親愛なる読者、この単純な推論はうまくいきません。可能なセマンティクスを考えるために、しばらくの間、どのように使用できるかを考えることから離れてみましょう。