まず、定義について説明しましょう。 オンザフライでの更新とは、通常の動作が中断されないシステムを更新することを意味します。クライアントが動作し、訪問者が移動し、エラー、応答時間の増加、または「会計」サインが見られません。
なぜこれが必要なのですか? この質問をしている場合-あなたは必要ありません。 サインを掛け、夕食に座ってください。
これはどのように行われますか? 難しいです。 なんで? 2つの主な理由があります。
-システムを即座にアトミックに(つまり、2つのHTTPリクエスト間で)更新することはできません。 素朴なアプローチでは、たとえばデータベースが更新されてコードがまだ利用できない場合、ユーザーは少なくとも長い応答時間、またはエラーにさえ気づくでしょう。
-システムの状態と構成は、クライアントとサーバーの両方に存在します。 例:セッションのデータ、フォームフィールドの名前、リンクのアドレス、ユーザーが開いたページのJavaScriptの状態。
一般的な決定
一般的に、ソリューションは次のように定式化できます。バージョンN + 1の
コード とバージョンNおよびN + 1の
状態との互換性を確保してから、
状態を N + 1に更新する必要があります。
実際には、ここでのそのような互換性は、非常に多くの(明らかにそうではない)困難をもたらします。 Ruby On Railsアプリケーションの典型的なケースを見てみましょう。
データベーススキーマの変更
テーブルへ
のフィールドの追加は、理論的には以前のバージョンのコードと互換性があります。 実際にも、特に悪いメタプログラミングがなければ。
古いコードがこのフィールドを使用している場合
、フィールドを
削除すると明らかに非互換性があり、いずれの場合も明らかではありません:ActiveRecordはフィールドのリストをキャッシュし、たとえばINSERT要求ですべてのフィールドをリストします。 出力:最初に、コードを中間コードに更新します。a)削除されたフィールドは使用しません。 b)キャッシュからこのフィールドを削除し、データベースを更新してから、コードを最終的なものに更新します。
フィールドの名前変更はもう少し複雑になります。
-新しい名前でフィールドを作成します
-コードを中間コードに更新します。a)両方の(古いおよび新しい)フィールドからデータを読み取りますb)両方のフィールドにデータを書き込みます
-データを古いフィールドから新しいフィールドに移行します
-古いフィールドを正しく削除するには、前の段落を参照してください。
インデックスの追加と削除は以前のバージョンのコード
と互換性があります。a)明示的なインデックスでヒントを使用しない場合b)インデックスを削除しても古いコードの実行はそれほど遅くなりません。
データのセマンティクスを変更する場合、一般的なケースを特定すること
は困難です。 すべては特定のアプリケーションのサブジェクト領域に依存します。 おそらく、単純で典型的な場合-フィールドタイプの変更-は、名前の変更と同じ方法で実行されます。
クライアントとサーバーの相互作用を変更する
フォームフィールドの名前
を変更するか、より重要な変更を追加コードで処理する必要があります(おそらくコントローラーで)。これは、古いフォームと新しいフォームからの入力値とフィールド値を受け入れることができます。 ブラウザウィンドウは長時間開いたままになる可能性があるため、しばらくの間、このコードをアプリケーションに残しておく必要があります。
セッションおよびCookieのデータのセマンティクスの変更も、両方の形式を理解する別個のコードで処理する必要があります。 セッションは長持ちし、クッキーはさらに長持ちします。 顧客のバスケットデータを失ったり、再度ログインパスワードを入力させたりしたくないですか? (ハブラ、恥を知れ!)
特定のページ/アプリケーションのアクション
のアドレスの変更は、常に下位互換性を保つ必要があります。 古いルートをそのままにして、リダイレクトを割り当てます。 WebアプリケーションのURLは、システムの最も安定した部分である必要があります。これは、ユーザーとユーザーをもたらす検索エンジンによって使用されるパブリックAPIです。 したがって、この記事で説明する部分に問題はありません。
アセットパイプラインを使用する場合、以前のバージョンのコードのアセットを削除する必要はありません。 簡単です。
再起動
コードの互換性がすべてではありません。 新しいコードを作品にどのように入力しますか? Webサーバーまたはアプリサーバーはいくつありますか? オプションを検討してください。
バランサーの背後に複数のサーバーが隠れている場合、さらに読むことはできません-あなたはすでにすべてを知っています:)他の人にとっては、すべても非常に明白です:
暗い夜にシステムの負荷が最も少ない時間
を選択し、各サーバーを順番に更新して、バランサーから削除します更新時。
NginxまたはApache httpdの背後に1つのPassengerサーバーがある場合、Unicornに移動する必要があります。 ゼロダウンタイムでの再起動が宣言されているPassenger 3でさえ、十分に単純です。最初に古いワーカーを殺し、次に新しいワーカーを殺します。 その結果、訪問者は、少なくともアプリケーションの開始時間以上の長い応答時間を取得します。
Unicornを使用して、複数のサーバーのスクリプトを「ミニチュア」で再現できます。
before_fork
、
TTOU
シグナルを古いマスタープロセスに送信する必要があります。この場合、新しいワーカーごとに1つの古いワーカーがオフになります。 最後に、古いマスターに
QUIT
を送信する必要があります。それだけです。 ワーカー数が2倍になるのに十分なメモリがある場合は、再起動の最後に、簡単に行うことができ、古いプロセスを徐々にではなくすぐに表示できます。
ヒント:rubyエンタープライズエディションを使用していない場合でも、
preload_app true
オプションを使用します。そうしないと、エラーのために新しいワーカーが起動時に落ちるのが遅すぎます。
おわりに
もう一度考えてみてください。これが本当に必要なのでしょうか? そう? おそらく同じように、最新の
+100500 TEDの問題をスタブページに挿入し、
cap deploy
を実行してお茶を飲みますか? そうそう...ユーザー、売り上げ、利益...