待たせずに、より大きなワークスペースへ移行する
Mythexのプロジェクトには、それぞれワークスペースが割り当てられます。これは、エージェントがコードを書き、アプリのプレビューが動く小さなクラウドコンピュータです。新しいワークスペースのメモリは1GBから2GBに増やしていましたが、古いワークスペースは1GBのままでした。確認した時点では、その約4分の1がそうでした。最初の修正では、次にプロジェクトを開いたときに、そのプロジェクトをより大きなワークスペースへ移すようにしました。ところが、本番環境で初めて動いた日には、1人のユーザーが7分も待たされることになりました。そこで、移行に厳しい時間制限を設け、最終的には、プロジェクトを開く処理から移行そのものを切り離しました。
古いワークスペースが小さいままだった理由
サンドボックスのプロバイダーは、ワークスペースのサイズを作成時に決め、一時停止と再開のあいだもそのサイズを保ちます。テンプレート(新しいワークスペースを作るための設計図)を変更しても、その変更は変更後に作られたワークスペースにしか適用されません。既存のワークスペースのサイズは変わりません。
これは問題でした。小さいワークスペースの1つは、プレビューの起動中にgit pushを実行したところメモリが不足し、11分間応答しなくなりました。その結果、公開が失敗しました。
最初の試み:起動時にプロジェクトを移動する
ワークスペースは、誰も使っていないときに一時停止し、プロジェクトを開くと再開します。起動時であれば、まだ誰も作業していないため、プロジェクトを移動するのに適したタイミングに思えました。
移動の流れは次のとおりです。
- サイズを確認します。 ワークスペースが2GB未満であれば移動します。サイズが不明であれば何もしません。
- 公開処理を妨げないようにします。 公開処理はワークスペースのファイルを読み取るため、公開が実行中であれば移動をスキップします。
- プロジェクトをパッケージ化します。 プロジェクト内のすべてをアーカイブします。Gitの履歴(プロジェクトのすべてのチェックポイント)と、オーナーのものであるアプリの
.envファイルも含みます。ただし、インストール済みの依存関係(node_modules)は除外します。これは再インストールできるためです。 - そのまま転送します。 古いワークスペースからアーカイブを、新しい2GBのワークスペースへストリーミングします。そのため、アーカイブ全体を当社のサーバーのメモリに載せる必要はありません。
- 切り替える前に比較します。 両方のワークスペースで、ファイル数、最新のチェックポイントの内容、チェックポイント数を数えます。3つすべてが一致した場合にのみ切り替えます。
- 失敗した場合は古い方を残します。 エラーや不一致があれば、新しいワークスペースを破棄し、従来どおり古いワークスペースでプロジェクトを開きます。
比較の1つの細部は、見た目以上に重要でした。その確認は管理者アカウントとして実行されますが、Gitは別のユーザーが所有するリポジトリの読み取りを拒否します。その結果、両側とも何も出力しませんでした。そして「何もない」と「何もない」が一致したため、履歴を一切比較せずに確認が通ってしまったのです。ディレクトリを信頼するようGitに指示し、同じ問題が再び起きないようテストを追加しました。
初日に起きたこと
本番環境で初めて動いた日、あるプロジェクトのパッケージ化が、起動処理の中で5分のコマンドタイムアウトに当たりました。オーナーは、ワークスペースが開くまで7分待たされました。さらに、移動は失敗したため、プロジェクトは小さいままでした。次にプロジェクトを開いたときにも移動が試みられ、また待たされることになったでしょう。
自分のプロジェクトを開く際にユーザーを待たせる移行は、割に合いません。起動時に行うという前提は、誰にも気づかれないことが前提だったのです。
2回目の試み:厳格な時間予算
同じ日に、4つの点を変更しました。
- 移動全体で1分。 サイズの確認、パッケージ化、新しいワークスペースの作成、転送、展開、比較といったすべての工程が、60秒という1つの時間予算を共有します。時間切れになった場合は、古いワークスペースでプロジェクトが開きます。
- 待つだけでなく、作業そのものも止める。 当社側で諦めるだけでは不十分です。ユーザーがこれから使うワークスペースの中で、アーカイブ処理がまだ動いているかもしれません。そのため現在は、予算が尽きる少し前に、ワークスペース内で各アーカイブコマンドを強制終了します。諦めた後に新しいワークスペースが届いた場合は、破棄します。
- クリーンアップのために、ユーザーを待たせない。 一時的なアーカイブの削除は、バックグラウンドで行うようになりました。
- 開くたびに再試行しない。 移動できなかったプロジェクトは、24時間そのままにします。そのため、失敗するのは1日に最大1回の遅延で済み、開くたびに遅延することはありません。
3回目の試み:起動処理から切り離す
これで最悪ケースは1分になりました。しかし1分であることに変わりはなく、そもそも移動はユーザーの操作の経路上にある必要がないと気づきました。
現在は1GBのワークスペースは新たに作られません。そのため、これは継続的な処理ではなく、対象のワークスペースの一覧が決まった一度限りの作業です。起動処理の中で行うと、遅い移動のたびに実際のユーザーが待たされることになり、移動できないワークスペースがあるとわかるのは、誰かがちょうど待たされた後だけでした。
そこで、起動処理からは移動を完全に取り除きました。プロジェクトを開く処理は、以前と同じように動きます。残っている小さなワークスペースは、監視付きのバッチとして処理されます。誰かが待つ必要はありません。移動できないワークスペースがあれば、バッチのレポートに表示されるため、所有者の目の前ではなく、そこで確認できます。
得られた教訓
- サイズの変更が常に可能とは限りません。 プラットフォームが作成時にサイズを固定する場合、デフォルトを上げても効果があるのは新しく作られるものだけです。古いものへの対応も計画しておきましょう。
- 「まだ誰も使っていない」は、誰かが待っているという意味でもあります。 製品の利用開始時に行われる作業も、人が見ている作業です。
- ユーザーの操作の経路上にあるものには時間予算を設け、予算が尽きたら作業を止めます。 当社側でタイムアウトしても、別の場所でコマンドが動き続けるなら、問題は移動しただけです。
- 一度限りの移行は、一度限りのジョブとして行いましょう。 日常の操作の中に隠すと、書くのは楽になりますが、動かすコストが高くつきます。
- 安全確認そのものをテストしましょう。 何も比較していない比較は、本当に一致した比較とまったく同じに見えます。
Mythexで開発している方は、特にすることはありません。目指すのは、メモリ不足で止まるワークスペースを減らすことと、プロジェクトを開く速さを以前と同じに保つことです。ワークスペースの一時停止と再開の仕組みは、ドキュメントの非公開ワークスペースで説明しています。別のメモリ不足の事例については、進捗ラベルに潜んでいたメモリ不足のクラッシュをご覧ください。