アップグレード
バージョン間のアップグレード経路、何が壊れるのか、そしてどう対処するのかをまとめます。リリースごとの変更一覧は GitHub Releases にあります。このページが扱うのは、対応が必要な部分だけです。
2.0.0 → 2.1.0
IALS アーティファクトは再学習が必須
2.1.0 は irspack を 0.4.2 から 0.5.2 に更新します。irspack 0.5.0 で IALSModelConfig のシリアライズ状態が 7 要素タプルから 10 要素タプルに変わったため、2.0.0 で学習した IALS アーティファクトは 2.1.0 ではロードできません。 それ以外のアルゴリズムはそのまま引き継がれます。2.0.0 で学習し 2.1.0 でロードした 6 アルゴリズムのうち、5 つ (CosineKNN、TopPop、RP3beta、DenseSLIM、TruncatedSVD) はロードでき、ビット単位で同一のスコアを返します。拒否されるのは IALS だけです。この拒否は過剰な安全策ではなく正当なものです。ガードを迂回して強引にデシリアライズすると、実際に TypeError: __setstate__(): incompatible function arguments が再現します。
影響範囲は見た目より広がります。同梱のチュートリアル用レシピは algorithms: [IALS, TopPop] を探索し、通常は IALS に落ち着きます。つまりチュートリアルから始めたデプロイメントは、誰も IALS を明示的に選んでいないのに IALS アーティファクトを持っています。勝ち残るアルゴリズムはレシピの設定ではなく探索の結果です。レシピから読み取るのではなく、アーティファクトごとに recotem inspect で確認してください。
「ビット単位で同一」というのは既存アーティファクトのロードについての主張であり、再学習についてではありません。同じレシピを 2.1.0 で学習し直して 2.0.0 のモデルと差分を取ると、わずかな浮動小数点のずれ (DenseSLIM でおよそ 8.5e-09、TruncatedSVD でおよそ 3e-15、アイテムの順序は不変) が出ます。これは複数のビルドを許容する依存関係レンジ由来のものです。アップグレードの検証は、いま手元にあるアーティファクトをロードして配信することで行ってください。
2.0.0 のコンテナイメージは起動しなかった
公開済みの ghcr.io/codelibs/recotem:2.0.0 は、どちらのアーキテクチャでも起動できません。コンソールスクリプトがビルドステージの shebang #!/build/.venv/bin/python を保持しており、このパスは最終イメージに存在しないため、エントリポイントが exec /opt/venv/bin/recotem: no such file or directory で失敗します。linux/amd64 と linux/arm64 の両方でダイジェスト指定により確認済みです。2.0.0 のイメージを使っているなら、それは動いていません。2.1.0 で公開されたイメージは正常に起動します。
スキューしたアーティファクトの見え方
serve は正常に起動します。クラッシュはしません。該当の IALS レシピは "loaded": false として登録され、レシピ名・両方の irspack バージョン・対処法を含むエラーが付きます。そのレシピへのリクエストは 503 (RECIPE_UNAVAILABLE) を返し、他のレシピは配信を続けます。recotem_artifact_load_failures_total{reason="version_skew"} が増加し、/v1/health/details は "status": "degraded" を報告します。
Kubernetes では影響範囲がプローブ設定で決まる
/v1/health は件数ベースです。loaded < total になった時点で HTTP 503 の degraded を返すため、バージョンスキューはここに該当します。アーティファクトがロードできないレシピも total には数えられるからです。これは「いずれかのレシピが失敗した」とは別物です。そもそもパースできないレシピファイルは、数えられるのではなくスキップされ、total からも loaded からも除外されます。そのため /v1/health は 200 ok のままで、別建ての skipped カウントだけが手がかりになります。パースできないレシピファイル を参照してください。
2.1.0 のチャートでは、どのプローブも /v1/health を読みません。 チャートは startupProbe と readinessProbe を /v1/health/ready に、livenessProbe を /v1/health/live に向けています。/v1/health/ready はレシピが 1 つでもロードできていれば 200 を返すため、健全なレシピと並んで IALS アーティファクトが拒否されても、Pod は起動し Service に参加し、それ以外を配信します。503 を返すのはスキューしたレシピだけです。スキューしたレシピが唯一のレシピの場合は何もロードされず、再学習するまで startupProbe がコンテナを再起動します。
自前のマニフェストを 2.0.0 のチャートからコピーしている場合、3 つのプローブはすべて /v1/health を向いたままです。 アップグレード前に 3 つとも移してください。startup と readiness を /v1/health/ready へ、liveness を /v1/health/live へ。チャート、examples/k8s/、Kubernetes デプロイメント はいずれもそうしています。移さなければ、拒否されたアーティファクト 1 つですべてのレプリカが Service から外れます。さらに startupProbe の失敗はトラフィックを止めるだけでなくコンテナを再起動するため、新しい Pod が永久に起動できなくなります。
ホットスワップでは代わりに静かに失敗する
これは起動時の話だけです。すでに動いているサーバーにスキューしたアーティファクトが届いた場合、以前ロードしたモデルはメモリに残ります。ウォッチャーはレジストリエントリにロードエラーを注記するだけで loaded フラグをクリアしないため、/v1/health は 200 のままで、どのプローブも失敗せず、Pod も再起動されません。件数ではなくエラー文字列を読む /v1/health/details だけが degraded を報告し、recotem_artifact_load_failures_total{reason="version_skew"} が増加します。フリートは古いモデルを静かに配信し続け、次の非自発的な再起動 (ノードのドレイン、Eviction、スケールアップ) で上記の起動時のケースに変わります。このカウンターにアラートを設定し、/v1/health/details をスクレイプしてください。/v1/health が緑であることはスワップ成功の証拠になりません。運用ガイド ではこれを「いまは degraded、あとで down」と呼んでいます。
degraded になったレシピが自力で回復するようになった
このリリース以前は、last_load_error をクリアするものは 1 つだけでした。別のアーティファクトのロード成功です。そのため、ありふれた 2 つの状況で /v1/health/details がプロセスの寿命いっぱい 503 degraded を返し続けました。その間 /v1/recipes/{name}:recommend は正常に配信していました。1 つは一時的な stat 失敗 (S3 のスロットリング、IAM の伝播待ち、NFS の stale handle や再マウント) で 1 回のポーリングが失敗するケースです。アーティファクト自体は無傷なので次のポーリングはマーカー不変の高速パスに入り、注記をクリアするコードに到達しませんでした。もう 1 つはロールバックです。以前のアーティファクトを戻す操作 (スキューした IALS アーティファクトに対してこのページが示す対処でもあります) は、すでにメモリにあるモデルと同じハッシュのバイト列を復元するため、注記をクリアする前にショートサーキットしていました。どちらの場合も、脱出方法は別のアーティファクトを書くかプロセスを再起動するかのどちらかだけでした。
2.1.0 は、注記がもう成立しないと示せた時点でそれを撤回し、その際に artifact_load_error_cleared (INFO) を記録します。ウォッチャーとレジストリのセマンティクス を参照してください。
アラート設定への影響。 /v1/health/details の degraded が張り付く前提のルール (「Pod を再起動して解消する」というランブック手順や、シグナルが自然に消えないからと長い for: を設定したアラート) があれば、外してください。2.1.0 の degraded は「いま障害が起きている」という意味です。パースできなくなったレシピ YAML と、ディスク上のアーティファクトに対するロード失敗は、実際に直るまで報告され続けます。
Azure URI は双方向に変わった
2.1.0 は、source.path と item_metadata.path が Azure URI 中の @ をどう扱うかを書き直しました。2.0.0 と比べて双方向に挙動が変わっており、片方はこれまでロードできていたレシピを止めます。2.0.0 と 2.1.0 の load_recipe を通した実測は次のとおりです。
| スキーム | 形式 | 2.0.0 | 2.1.0 |
|---|---|---|---|
abfss:// | container@account… (アドレッシング) | 拒否 | 受理 |
abfs:// | container@account… (アドレッシング) | 拒否 | 受理 |
az:// | container@account… (アドレッシング) | 受理 | 受理 |
abfss:// | user:pass@… (資格情報) | 拒否 | 拒否 |
abfs:// | user:pass@… (資格情報) | 拒否 | 拒否 |
az:// | user:pass@… (資格情報) | 受理 | 拒否 |
s3:// | user:pass@… (資格情報) | 拒否 | 拒否 |
破壊的なのは、実際の user:pass@ を持つ az:// の行です。 2.0.0 では az が資格情報チェックから抜けていたため、この形式の URI は黙って読み込まれていました。2.1.0 では終了コード 2 (RecipeError、カテゴリ security) となり、'source.path' contains embedded credentials in the URI. Use environment-based authentication instead. を出力します。
@ の前にコロンがある az:// パスがないか、レシピを grep してください。見つかった場合、対応は 2 つあり、2 つ目は見落としやすいものです。
- レシピを直す。 シークレットを環境変数へ移します。Azure の fsspec バックエンドは
AZURE_STORAGE_*または接続文字列から資格情報を読みます。終了コード 2 を止めるのはこの対応です。 - その資格情報は漏洩したものとして扱う。ストレージアカウントキーをローテーションし、それを含む可能性のあるログアーカイブをパージまたはローテーションする。 2.0.0 ではこの URI が実行のたびに平文でログに書かれていました。目印を入れたシークレットで 2.0.0 を実測したところ、1 回の
recotem trainで 4 回出力されました。ソース取得イベントの INFO で 1 回、さらにエラーテキストの中で再度出力されます。ログ URL からuser:pass@を除去するリダクションヘルパーはhttp/https/ftp/ftpsしかカバーしておらず、その背後にある structlog の DSN スクラバーもazを列挙していませんでした。汎用の高エントロピースクラバーは形状ベースなので、あるキーが捕捉されるかどうかはキー次第です。捕捉されやすさを狙わずに用意した 10 個のシークレットのうち 7 個がすり抜けました。人間が選んだパスワードはすべて、真にランダムなアカウントキーも約半分がすり抜けています。標準 base64 の+と/が、パターンの探す 43 文字の連続を分断してしまうためです。
シークレットを移せば、以降のレシピは直ります。しかし、すでにログ集約基盤に送られたログには何の効果もありません。手順 1 で止めないでください。2.1.0 では同じレシピがログ行に到達すらしません。ロード時点で拒否され、目印付きシークレットの出力回数は 0 です。
これは実際にそうしたレシピを持っていた場合にだけ当てはまります。az:// パスの @ の前にコロンがなければ、それはアドレッシング形式であり、ログには何も出ておらず、ローテーションすべきものもありません。ただしこのギャップがあったスキームは az:// だけではありません。ローテーション不要と結論する前に、次の節を読んでください。
変わった残り 2 行は修正であり、対応は不要です。Azure 自身のドキュメントが使う正規形 container@account.dfs.core.windows.net が、abfs:// / abfss:// では資格情報とみなされて拒否されていました。これを回避するためにパスを書き換えていたなら、今後はドキュメントどおりに書けます。2.1.0 が 3 つの Azure エイリアスすべてに適用するルールは「素の container@account はアドレッシングなので受理、実際の user:pass@ は拒否」です。
gs:// と file:// も同じ経路で漏洩していた
前節は Azure の話として書かれていますが、2.0.0 の資格情報チェックから抜けていたのは az だけではありません。gs は az/abfs/abfss のようにパスワード条件付きだったのではなく、まるごと免除されていました。gs://project@bucket/key が gcsfs の課金プロジェクト指定として正当な形式だからです。userinfo にパスワードまで持つ gs:// URI は、この免除を引き継いで読み込まれていました。file:// にはチェック自体がありませんでした。
2.0.0 と 2.1.0 で、同じレシピの source.path だけを変え、パスワード部分に目印を入れたシークレットを置いた実測は次のとおりです。
| スキーム | 形式 | 2.0.0 | 2.1.0 | 1 回の train でシークレットが出た回数 |
|---|---|---|---|---|
gs:// | project@bucket… (アドレッシング) | 受理 | 受理 | — (シークレットなし) |
gs:// | user:pass@… (資格情報) | 受理 | 拒否 | 2.0.0 で 5 → 2.1.0 で 0 |
file:// | user:pass@… (資格情報) | 受理 | 拒否 | 2.0.0 で 3 → 2.1.0 で 0 |
s3:// | anything@… | 拒否 | 拒否 | 0 (対照群 — s3 には @ のアドレッシング用法がない) |
gs:// の 5 回は、同じ 1 行が 5 回出たわけではありません。シークレットは 4 つの独立した経路と stderr からログに到達します。INFO の csv_source_fetch_start イベント、gcsfs 自身が送出する ERROR レコード (トレースバック付き)、WARN の size_cap_probe_failed イベント、train_error イベント、そして stderr の Training failed: … 行です。どれか 1 つをリダクトするだけでは足りません。だからこそ修正はレシピのロード時点に置かれています。
したがって前節の 2 段階の対処は gs:// にも当てはまります。「ローテーション不要」という記述は az:// についてだけのものです。@ の前にコロンを持つ gs:// パスを使っていたレシピがあれば、次を実施してください。
- シークレットを環境変数へ移す。gcsfs は
GOOGLE_APPLICATION_CREDENTIALSまたは実行環境のサービスアカウントから資格情報を読みます。userinfo が素のプロジェクト名だけのgs://パス (gs://my-project@my-bucket/…、コロンなし) はアドレッシング形式で、いまも受理され、シークレットとしてログに出たこともありません。触らないでください。 - そのキーをローテーションし、ログアーカイブをパージまたはローテーションする。 理由も緊急度も Azure のケースと同じです。
file:// は範囲の狭い話です。拒否の対象は URI のオーソリティ部にある @ に限られます。つまり file:// スキームの直後に user:pass@ が来て、その後にパスが続く形式です。ディレクトリ名やファイル名に @ を含むだけのローカルパスは、素の形式 (/data/user@example.com/x.csv) でもスラッシュ 3 本の形式 (file:///data/user@example.com/x.csv) でも影響を受けず、2.0.0 でも 2.1.0 でも同様にロードして学習できます。変わったのはオーソリティ形式だけで、これは誰かがうっかり書く形ではありません。
time_column 付きの split.scheme: random は分割方法が変わる
これ自体に対応は不要ですが、あなたが見ている数値を動かします。
2.0.0 のパイプラインは、time_column を宣言しているレシピであればどの scheme でも schema.time_column をスプリッターに渡していました。そして irspack は time column を受け取った時点でユーザーごとの新しさによるホールドアウトに切り替わります。つまり random を指定しつつ time_column も宣言したレシピは、黙って time_user 分割になっていました。2.1.0 は random のときに time_column を None に強制します。これはフィールドのドキュメントどおりの挙動です。実測では、ホールドアウトされる interaction は「各ユーザーの直近 20 % に 100 % 含まれる」状態から、一様な 18〜21 % に変わりました。呼び出し箇所を計測すると引数そのものが変わっていることも確認できます。scheme: random と time_column: ts を持つレシピでは、2.0.0 は "ts" を、2.1.0 は None を渡します。
エラーにはならず、終了コードも変わりません。 レシピは有効なままで学習も成功します。目に見える影響は、Optuna の探索がスコアリングする検証セットが別の interaction 集合になることだけです。そのためレシピを何も触っていなくても、アップグレード後の最初の再学習で best_score が動きます。これはリグレッションではなく、追いかけるべきものでもありません。 また同条件の比較でもありません。アップグレード前の best_score と後の best_score は別のホールドアウトに対して計算されているので、差分を取らないでください。レシピリファレンスの training を参照してください。
本当に新しさによるホールドアウトが欲しいなら、明示的に指定してください。split.scheme: time_user が、2.0.0 が偶然与えていたものです。
同梱のサンプルレシピ 6 つのうち 1 つが該当します。 examples/sql-sqlite/recipe.yaml は 2.0.0 以来この組み合わせ (scheme: random と time_column: event_at) を持ち続けています。残り 5 つのうち 2 つは time_user、1 つは scheme 未設定、2 つは time column なしの random なので、実際に影響を受けません。
これが問題なのは、sql-sqlite が SQL データソースのドキュメント の参照先だからです。ここから始めた場合、アップグレード後の最初の再学習で best_score が動くレシピを持っていることになり、その理由はレシピのどこにも書かれていません。自分のレシピも同じ組み合わせがないか grep してください。
終了コードが移動し、うち 1 つは 0 から動いた
この節にレシピの変更は必要ありません。ここに書かれている理由は、これらの数値で分岐するものが 2 つあるからです。スーパーバイザーや CronJob と、recotem validate を実行する CI ステップです。どちらもファイルを何も触らずにアップグレードで挙動が変わり得ます。
最も重要なのは、書き込めないアーティファクトディレクトリが従来は無言の成功だった点です。 2.0.0 では実行が 0 で終了し、recipe_lock_contended_skipping を記録しました。これは本当に並行実行が起きたときと同じ構造化イベントです。そのため、アーティファクト用ボリュームの書き込み権限を失った夜間ジョブ (fsGroup の変更、再マウント、PVC の再プロビジョニング) は、毎晩「成功」を報告しながら何も書かず、serve は既存のモデルを持ち続けました。2.0.0 での実測では、原因は 2 つあるのに結果は同一でした。
| 原因 | 終了コード | 構造化イベント | アーティファクト書き込み |
|---|---|---|---|
| 別プロセスがロックを保持 | 0 | recipe_lock_contended_skipping | なし |
| アーティファクトディレクトリが書き込み不可 | 0 | recipe_lock_contended_skipping | なし |
2.1.0 はこの 2 つを分離します。後者は 8 で終了し、lock_permission_denied としてロックパス、uid/gid、そしてリトライしても解決しないという事実を出力します。前者は変わりません。本当にロックが競合したケースは両バージョンとも 0 で終了して recipe_lock_contended_skipping を記録します。だからこそこの分離は信頼して使えます。
そのため、緑だったジョブが赤に変わることを想定してください。 アップグレード直後に学習 CronJob が終了コード 8 で失敗し始めたなら、2.1.0 が壊したのではありません。2.1.0 は「もっと前からアーティファクトを作れていなかった」と伝えているのです。何かを変える前に、配信中のモデルがどれだけ古いかを確認してください。
その他の移動は、同じレシピを両バージョンで実行した実測です。
| レシピの不具合 | コマンド | 2.0.0 | 2.1.0 |
|---|---|---|---|
output.path のディレクトリが書き込み不可 | train | 0 | 8 |
output.path が既存ディレクトリを指す | train | 1 | 8 |
素のローカルパス / file:// パスでの source.sha256 不一致 | train | 7 | 3 |
training.storage_path が非対応のダイアレクトを指す | train | 1 | 8 |
training.storage_path が userinfo を含む | train | 4 | 8 |
training.cutoff がユニークアイテム数を超える | train | 1 | 4 |
schema.user_column がデータに存在しない | train | 1 | 3 |
item_metadata.sha256 不一致 | validate | 0 | 3 |
item_metadata.path が存在しない | validate | 0 | 3 |
item_metadata.fields の項目がファイルに存在しない | validate | 0 | 3 |
schema.user_column がデータに存在しない | validate | 0 | 3 |
| 存在しないアルゴリズム名 | validate | 0 | 4 |
training.storage_path が非対応のダイアレクトを指す | validate | 0 | 8 |
training.storage_path が userinfo を含む | validate | 0 | 8 |
ここから 2 つの帰結があります。
- 終了コード 1 はそもそもカテゴリではなかった。
trainの 4 行がここから移動しました。終了コード 1 は_EXIT_UNKNOWN、つまりマッピングされていない例外です。これを「recotem がクラッシュした、誰かを呼べ」として扱っていたアラートは、今後は具体的な分類 (8 は設定、4 は学習、3 はデータソース) を受け取ります。それが改善点ですが、要点は、あなたのルールが照合している数値が変わったことです。 recotem validateは実質的に厳しくなった。 2.0.0 ではvalidateが通していたレシピの不具合 7 種が、今後は失敗します。recotem validateを CI ゲートとして使っているなら、何か月も前にマージ済みのレシピでゲートが赤になり得ます。それらのレシピはもともと壊れていました (2.0.0 でもほとんどはtrainで失敗しました)。ただ、最初に気づく場所がゲートになるということです。
source.sha256 の行は、リトライロジックを確認すべき箇所です。Kubernetes デプロイメント は終了コード 7 をリトライ対象 (一時的なネットワーク障害) に、3 をデータソースエラーに分類しています。ローカルパスに対する恒久的に誤った sha256 ピンは 2.0.0 ではリトライ可能として報告されていたため、CronJob は決して一致しないバイト列をリトライし続けていました。2.1.0 は 3 を報告します。
不正な RECOTEM_SIGNING_KEYS も 5 から 8 へ移動しました。これは影響を受けないアーティファクトフォーマットとの関係で後述の このアップグレードで変わらないもの が扱います。コードごとの完全なリファレンスは 終了コードとエラー にあります。
アップグレード手順
- すべてのアーティファクトに
recotem inspectを実行し、"best_class": "IALSRecommender"を報告するものを控える。作業が必要なのはそれだけです。 - train 側を先にアップグレードし、IALS のレシピをすべて 2.1.0 で再学習する。
- 新しいアーティファクトがアーティファクトストアに届くのを待つ。
- serve 側をアップグレードする。
/v1/health/detailsが"status": "ok"を報告することを確認する。
このアップグレードが破る無停止更新の注意点を含む完全なランブックは irspack のバージョンスキュー にあります。
serve を先にアップグレードした場合 (ローリングデプロイの既定の順序です)、古い IALS アーティファクトがディスクに残っているため、そのレシピは loaded: false で起動し、2.1.0 で学習したアーティファクトに置き換わるまで 503 を返します。IALS 以外のレシピはそれ自体としては影響を受けません。これは破損ではなく、はっきり見える停止ですが、ローリングデプロイが既定で行うことなので織り込んでおいてください。
ここで RECOTEM_ALLOW_IRSPACK_VERSION_SKEW=1 に手を出さないこと
このフラグは拒否を警告に落としてペイロードをデシリアライザへ通すだけです。その結果、ガードが置き換えるはずだった素の TypeError でロードは結局失敗します。対処可能なエラーを原因不明のエラーに変えるだけで、得るものはありません。このフラグは単に未検証のアルゴリズム向けであって、既知の IALS の破壊に対するものではありません。
ロールバック
serve とアーティファクトはセットで戻してください。いったん 2.1.0 で再学習したレシピは、2.0.0 の serve でもその IALS アーティファクトをロードできません。破壊は双方向です。アップグレードが確認できるまでアップグレード前のアーティファクトを保持してください。既定の versioning: append_sha とポインタファイルがあるので自然に行えます。削除ではなく参照先の切り替えです。新しい features: ブロックを使うレシピはそもそもロールバックできません。 2.0.0 で動かすには、ブロックを外して再学習する必要があります。
このアップグレードで変わらないもの
署名鍵と鍵ローテーション手順、そしてアーティファクトコンテナ自体 (マジックバイト、FORMAT_VERSION 1、ヘッダーレイアウト) は変わりません。2.0.0 で署名したアーティファクトは 2.1.0 で検証でき、2.1.0 で署名したものは 2.0.0 で検証できます (同じ鍵の場合)。すべてのレシピの recipe_hash は変わりますが、それを条件にしている処理はありません。
例外はレシピ 4 種分あり、それぞれ求められる対応が異なります。 2.0.0 でロードできていた path 形式のうち 3 つは 終了コード 2 で拒否されるようになり、アップグレード前に直す必要があります。4 つ目のレシピ形状は動き続けますが、測っているものが変わります。直すものはありませんが、動いた数値がリグレッションに見えないよう time_column 付きの split.scheme: random を参照してください。
アップグレード前に直すべき 3 つ:
user:pass@を含むaz://— 上の Azure URI は双方向に変わった を参照。シークレットを環境変数へ移し、さらにローテーションしてください。2.0.0 は実行のたびにその URI を平文でログに書いていたため、鍵は漏洩したものとして扱う必要があります。user:pass@を含むgs://、およびオーソリティに@を含むfile://—gs://とfile://も同じ経路で漏洩していた を参照。gs://形式にはaz://と同じローテーションが必要です。2.0.0 は 1 回の実行でそのシークレットを 5 回ログに書いていました。コロンのないgs://project@bucketはアドレッシングであり、いまも受理され、対応は不要です。arrow_hdfs://とasync_wrapper://— fsspec が登録するプロトコルのうち、名前にアンダースコアを含む唯一の 2 つです。RFC 3986 はスキームに_を許さないためurlparseはスキームなしと報告し、2.0.0 の許可リストはこれらを素のローカルパスとして読んで通していました。一方fsspec.openは実際のリモートバックエンドへルーティングしていました。2.1.0 は fsspec と同じ方法でスキームを導出し、同等のhdfs://形式に対して以前からそうしていたのと同様に拒否します。これは許可リストのバイパスだったので拒否が正しい挙動ですが、これに依存していたレシピはロードできなくなります。サポート対象のスキームを使ってください。受理されるスキームの一覧は パスのルール にあります。
この領域で 1 つだけ変わったものがあります。不正な RECOTEM_SIGNING_KEYS は、2.0.0 では 5 (_EXIT_ARTIFACT) でしたが、train・serve・inspect のいずれでも 8 (_EXIT_CONFIG) で終了するようになりました。コンテナフォーマットは変わっておらず、アーティファクトに対して必要な作業もありません。ただし終了コード 5 を「アーティファクトが壊れているので再学習」と解釈して分岐しているスーパーバイザー・CronJob・アラートは、環境変数のタイプミスでは発火しなくなるため、終了コード 8 を覚える必要があります。
irspack 0.4 → 0.5
仕組み、検証済みペアの表、フェイルオープン / フェイルクローズの条件、そして RECOTEM_ALLOW_IRSPACK_VERSION_SKEW という逃げ道は、すべて irspack のバージョンスキュー の 1 か所にまとめてあります。そのルールのうち、アップグレード計画に固有の事実は 2 つです。
- 破壊は双方向です。 0.5.x で学習した IALS アーティファクトも 0.4.x ではロードできません。したがって serve を先に上げる段階的アップグレードはできず、アーティファクトを 0.5.x で再学習したあとに serve だけ 0.4.x へ戻すこともできません。
trainとserveは同時にアップグレードしてください。その場での移行手段はありません。欠けているフィールドは内部の C++ 状態であり、正しく作れるのは再学習だけです。 - 今後の irspack マイナーはすべて、まず拒否から始まります。 ガードは検証済みペアの表を参照するため、将来の 0.5 → 0.6 のアップグレードでは、0.4 ↔ 0.5 を通過した 5 つを含むすべてのアルゴリズムのアーティファクトが、その遷移が検証され行が追加されるまで拒否されます。マイナー内のパッチ更新 (
0.5.0→0.5.3) は影響を受けません。major.minor が一致した時点で表を見る前にショートサーキットします。今回に限らず、irspack のマイナー更新のたびに再学習を見込んでおいてください。
1.x → 2.x
自動マイグレーションはありません。Recotem 2.x は 1.x と名前と推薦というドメインを共有していますが、まったく新しいシステムです。
- モデルは移行せず、学習し直す。 1.x のモデル状態は 2.x の署名付きアーティファクト形式と互換性がありません。レシピを定義して
recotem trainを実行してください。 - データベースとメッセージブローカーを廃止する。 2.x はステートレスで、永続する状態は署名付きアーティファクトファイルだけです。
- API クライアントを更新する。
/predict/{name}からPOST /v1/recipes/{name}:recommendへ。 - 鍵を生成する。
recotem keygen --type signing(serve の認証用には--type apiも) を実行し、RECOTEM_SIGNING_KEYS/RECOTEM_API_KEYSを設定してください。
完全な手順は チュートリアル を参照してください。
