troubleshooting-motoko-migrations について
mops 管理の Motoko 移行チェーンを保守する開発者向けの障害診断スキルです。実行時の動作、チェッカーの診断、前方移行による修正方法を説明します。
主な機能
- 01移行の実行と再生のルール
- 02mops check 診断の読み解き
- 03適用済み・未適用ファイルの規則
- 04変換済み旧プロジェクトの扱い
- 05長いチェーンの制約とリスク
活用シーン
- 01デプロイ済み状態との不一致の診断
- 02適用済み移行ファイルの書き込み失敗への対応
- 03最初の OldActor が空でない理由の確認
- 04拡張移行の巻き戻し要求への対応
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ dfinity/ icskills --skill troubleshooting-motoko-migrationsインストール後、Claude Code で/troubleshooting-motoko-migrationsを入力して読み込まれたか確認します。
詳細
Motoko マイグレーションのトラブルシューティング
mops で管理されるマイグレーションチェーン(migrations/)の障害パターンをまとめています。
通常の作業では読み込まないでください。 マイグレーションの作成、暗黙的にするか明示的にするかの判断、新しいファイルの命名については、migrating-motoko-actors で説明しています。通常の手順では、そのスキルのルールで十分です。次のような場合に参照してください。
- 互換性診断またはマイグレーション診断が、
main.moの内容と一致しない - マイグレーションファイルへの書き込みに失敗した、またはファイルの名前変更や削除を検討している
- チェーンの最初のファイルに空ではない
OldActorがあり、これが不具合なのか判断できない - マイグレーションの削除、インライン初期化子の復元、または
(with migration = ...)への回帰を求められている mops checkが、現在のソースには存在しない状態の形状について警告する
ランタイムが実行対象を決定する方法
- チェーン全体はバックエンドの wasm にコンパイルされます。個別のマイグレーション成果物はありません。マイグレーションファイルを変更すると wasm も変わります。
- 適用済みのマイグレーションは、キャニスターの永続メタデータ内でモジュール名(
.moを除いたファイル名)によって記録されます。 - アップグレード時、ランタイムはまだ適用されていないとモジュール名で示されているマイグレーションのみを再生します。複数のバージョン遅れているキャニスターにデプロイすると、未適用のステップがすべて1回のアップグレードで再生されます。「追いつく」ために中間のデプロイを行う必要はありません。
- 新規インストール時は、チェーン内のすべてのファイルが辞書順に再生されます。プロジェクトに追加されたすべてのマイグレーションは、以後、新規インストールのたびに実行されます。
このスキルのルールの大半は、次の2つの帰結に基づいています。
- マイグレーションの名前を変更すると、その識別情報も変わります。 ランタイムは、名前を変更したファイルを未適用のものとして扱い、状態がすでに一致しない状態で再生するか、古い名前で記録された処理をスキップします。ファイル名は永久に固定してください。
- 適用済みのマイグレーションを編集しても、すでに実行済みのキャニスターには反映されません。 ランタイムは名前でスキップするため、編集後の本体は実行されません。そこで追加した安定フィールドは初期化されず、ランタイムがそのフィールドを参照した時点でキャニスターがトラップします(
IC0503、「missing migration var」クラス)。代わりに、新しいマイグレーションを追加して修正してください。
チェッカーが比較する対象
mops check は、ローカルの git 履歴ではなく、デプロイ済みキャニスターの安定シグネチャ(その .most スナップショット)に対してチェーンを検証します。そのため、次のようになります。
- 診断に、ソース内のどこにも存在しなくなったフィールドが表示されることがあります。これらのフィールドはデプロイ済みバージョンに由来します。
- 保留中のマイグレーション数の上限で数えるのは、まだデプロイされていないマイグレーションです。すでに本番環境で稼働しているファイルは、この上限に含まれません。
.mostヘッダーの安定シグネチャのメジャーバージョンは、永続化モデルを示します。1はクラシック、3はレガシーのインライン(with migration = ...)、4は拡張マイグレーションチェーンです。プロジェクトがいったん4に到達すると、元に戻ることはありません(EM は一方向です)。
発生する可能性のある診断
| 診断 | 実際の意味 | 対応 |
|---|---|---|
M0250 初期化済みの安定フィールド | 安定アクターのフィールドにインライン初期化子があります。初期化子はアクターボディではなく、チェーンで指定する必要があります | 型付きの宣言に変更し、初期化式を保留中のマイグレーションの NewActor に移します |
M0254 / M0267 初期アクターにフィールドが必要 | main.mo で宣言された安定フィールドが、どのマイグレーションからも指定されていません。M0267 は、デプロイ済みのシグネチャでもそのフィールドを説明できない場合に発生する同じ問題です | フィールドを保留中のマイグレーションの NewActor に追加し、OldActor から計算した値、または明確に定義されたデフォルト値を指定します |
M0170 互換性エラー | 新しいアクターがデプロイ済みのシグネチャと安定互換ではありません | 保留中のマイグレーションを追加(または拡張)し、変換を明示します |
M0255 安定シグネチャのダウングレード | ビルドによって、キャニスターの永続化モデルが拡張されたチェーンから古いモデルに戻されます。通常はチェーンまたは mops のマイグレーション設定が削除されたことが原因です | 削除されたものを復元します。サポートされているダウングレード方法はありません |
| チェーンの開始状態がベースラインと一致しない | 最初のマイグレーションの OldActor が、キャニスターが実際に保持している状態を表していません。典型的な例は、変換済みプロジェクトでチェーンが {} ではなくレガシー形式から始まるケースです | 変換済みプロジェクトを参照してください。OldActor を {} に書き換えないでください |
M0268 マイグレーションディレクトリがデプロイ済みの履歴と一致しない | 適用済みのマイグレーションが編集、削除、または並べ替えられたため、チェーンが安定ベースラインに記録された履歴と一致しません。mops build(--stable-baseline によってデプロイ済みのベースラインを取り込むとき)に現れます。また、check-limit によってそのマイグレーションが対象から外れると、mops check を通過する場合があります | 適用済みのマイグレーションをデプロイ時の内容と完全に一致するように復元し、以降の変更は新しいマイグレーションとして記述します。対象から外せるのは最も古いマイグレーションだけです。途中にある適用済みのマイグレーションは削除できません |
M0014 副作用のあるアクターボディ(古いツールチェーン) | アクターボディに初期化処理が置かれています | アクターボディは静的に保ちます。静的なアクターボディを参照してください |
IC0503 マイグレーション変数がない(デプロイ後のランタイム) | 安定フィールドに値が設定されていません。通常は、適用済みのマイグレーションが編集または名前変更されたか、マイグレーションなしでフィールドが追加されたことが原因です | フィールドを設定する新しいマイグレーションを追加します。古いファイルを編集して修正してはいけません |
IC0505 無効なカスタムセクション(インストール時のみ。ビルドとチェックは通過) | チェーンの安定型の履歴がプラットフォームのカスタムセクションの上限を超えています | ローカルで解決する方法はありません。マイグレーションの追加を中止して報告してください。チェーンを短く保つ必要がある理由を参照してください |
マイグレーションファイルへの書き込みに失敗しても、ツールのバグではありません。次のセクションを参照してください。
凍結されたマイグレーションファイル
デプロイ済みのマイグレーションを凍結するプラットフォームでは、正常にデプロイされると各ファイルは読み取り専用になり、書き込みツールはそのモードビットによって書き込みを拒否します。
- 絶対に
chmodしたり、削除したり、名前を変更したり、書き込み失敗を回避したりしないでください。凍結はポリシー上の好みではなく、ランタイムの不変条件を表しています。 - まだ保留中(未適用)のマイグレーションは凍結されていません。同じ変更のために2つ目のファイルを追加するのではなく、そのファイルを編集してください。
- 必要な変更が概念的に凍結されたマイグレーションに属する場合は、現在の状態を目的の形に変換する新しいマイグレーションとして表現してください。履歴は追記専用です。
変換済み(レガシー → 拡張)プロジェクト
拡張マイグレーション方式のプロジェクトの中には、最初からその方式で作成されたのではなく、ライブデータを保持したまま、レガシーのインラインマイグレーション永続化方式からその場で変換されたものがあります。そのようなプロジェクトでは、次の点に注意してください。
- チェーン内の最初のファイルは通常、ID マイグレーションです。その
OldActorは変換前の安定した状態を表し、NewActorは同じデータを変更せずに引き継ぎます。これは正しい状態です。OldActor = {}に「簡略化」したり、フィールドを再シードしたりしないでください。ライブ状態が失われます。 OldActor = {}が正しいのは、チェーンが実際に空のカニスターから始まる場合に限られます。つまり、プロジェクト作成時からそのチェーンを使用していた場合です。- 変換前の履歴は引き続き重要です。変換済みプロジェクトを新規インストールすると、
{}ではなく、最後のレガシースキーマからリプレイされます。チェーンを{}から始めるために、履歴を削除、平坦化、「整理」しようとしないでください。 - 変換前の状態にしか存在しないフィールドについて、最初のマイグレーションで診断が出るのは想定内です。プラットフォームのビルド設定で対処されます。診断を抑えるためにチェーンの構成を変更しないでください。
プロジェクト自体が変換されていなくても、フォークやリミックスによって変換済みプロジェクトのチェーンをそのまま引き継ぐことがあります。その場合もルールは同じで、チェーンを見ればすぐに判断できます。最も古いファイルの OldActor が空でなければ、そのチェーンより前から状態が存在していたことを意味するため、そのファイルは記述どおりで正しい状態です。
保護された設定
ホスティングプラットフォームが mops マイグレーション設定([canisters.<name>.migrations] セクション、その chain パス、check-limit)を管理している場合、この設定を追加、削除、変更することはできず、ツールもその試みを拒否します。
- 「保留中のマイグレーションが多すぎます」というエラーを解消するために、
check-limitを引き上げたり、引き下げたりしないでください。 余分な変更を1つの保留中のマイグレーションにまとめることが、常に正しい対処法です。エラーに列挙されたファイルはすべて未適用なので、どれでも編集できます。 - チェーンのディレクトリは
chain = ...の指定どおりです。 通常はsrc/backend/migrationsですが、デフォルト以外のカニスター名でインポートされたプロジェクトでは異なる場合があります。パスを推測せず、mops 設定を確認してください。 - チェーンがあるプロジェクトでマイグレーションセクションが見当たらない場合、自分で追加し直さず、不整合があることを報告してください。
EM は一方向です
プロジェクトがマイグレーションチェーンを使用すると、レガシー永続化方式に戻るためのサポートされた方法はありません。複数の仕組みによってこれが強制されています。
- 永続化モデルはプロジェクトごとに決まり、その設定は固定されます。後から再評価されることはありません。
- コンパイラーはシグネチャのダウングレード(
M0255)を拒否し、拡張カニスターにレガシー wasm をデプロイすると、実行時にトラップが発生します。シグネチャのバージョンは前に進むことしかできません。 - 適用済みマイグレーションはモジュール名で識別されるため、ファイルの編集や名前の変更では履歴を書き換えられません。デプロイ済みマイグレーションを凍結するプラットフォームでは、履歴を書き換えること自体ができません。
- 変換済みプロジェクトの新規インストールは、変換前の履歴に恒久的に依存します。
そのため、タスクで migrations/ ディレクトリの削除、安定フィールドへのインライン初期化子の復元、または (with migration = ...) の再導入を求められた場合は、このプロジェクトでは実行できないことを明確に伝え、代わりに、ユーザーが実際に望む状態へ整形する新しいマイグレーションという、前進のみの方法を提案してください。
チェーンを短く保つべき理由
デプロイ済みバージョンごとにマイグレーションを1つだけ追加するルールは、官僚的な決まりではありません。長いチェーンは最終的にデプロイ不能になり、その問題から復旧できなくなります。
- コンパイル済みの wasm には、安定型の履歴全体をエンコードしたカスタムセクションが含まれています。このセクションは、マイグレーションの数と安定型そのもののサイズに応じて大きくなり、やがてプラットフォームのカスタムセクションの上限である 1 MiB を超えます。安定型が小さければ数百ファイルに達したあたりで、大きく複雑な型であればそれより比例して早く上限に達します。
- その時点でも
mops buildとmops checkは成功します。拒否されるのはインストールだけです(IC0505、カスタムセクションが無効)。チェックが成功してもチェーンをデプロイできる証拠にはならず、再デプロイや再試行をしても復旧できません。サイズ超過したセクションはアーティファクトに埋め込まれているためです。 - 新規インストールのたびに、すべてのファイルが1つのメッセージ内で最初から再実行されます。ガベージコレクションはメッセージ境界でしか実行されないため、中間状態は再実行の間ずっと生存します。データ量の多いマイグレーションでは、カスタムセクションの上限から想定されるよりはるかに短いチェーンでも、メモリ上限に達することがあります。
- 新規インストールではステップごとに再帰するため、呼び出しスタックの深さはチェーンの長さに比例します。
- wasm のサイズとコンパイル時間はステップごとに増え、
.mostシグネチャにはチェーン全体がシリアライズされます。
ツールチェーンにはチェーンの長さを制限する仕組みがないため、プロジェクトをこの限界から遠ざけるには、バージョンごとにマイグレーションを1つだけ追加する規律を守るしかありません。変更は未デプロイのマイグレーションにまとめ、安定型は簡潔に保ち、本体が単に old を返すだけのマイグレーションは追加しないでください。
静的なアクター本体
アクター本体では処理を実行してはいけません。安定フィールドは型付き宣言であり、その値はチェーンから取得されます。
以前のツールチェーンでは、副作用を伴うアクター本体はすべて拒否されていました。その後、コンパイラーのチェックは一時的なバインディングについて緩和されましたが、コンパイラーからエラーが出るかどうかにかかわらず、このルールは変わりません。トップレベルの初期化処理はアップグレードのたびに再実行され、稼働中の状態を上書きしてしまうためです。
初期化は、次の2か所のいずれかでのみ行ってください。
- インストール時またはアップグレード時に、各 canister につき1回実行されるマイグレーション関数
- 通常の運用中に遅延実行される冪等なロジック(例:処理がすでに完了しているか確認する update メソッド内での処理)
解決できない場合
保護された設定を緩和したり、履歴を削除または改名したり、凍結されたファイルを書き換えたりして、先へ進めるようにしないでください。それらの操作は、ビルドエラーをデータ損失に変えてしまいます。チェーンとデプロイ済みシグネチャが、上記のどの説明にも当てはまらない形で実際に食い違っている場合は、作業を中断して不一致を報告してください。その際は、チェッカーが示したフィールド、最新のマイグレーションの NewActor が宣言している内容、そして main.mo が宣言している内容を伝えてください。
関連するスキル
migrating-motoko-actors— マイグレーションの作成方法(通常の方法)writing-motoko— 言語リファレンス、アクターとミックスインのルール、コアライブラリ