cryptofoundry

cryptofoundry へのお問い合わせ

構築または自動化したい内容をお聞かせください。

Ecosystem & Integrations

ADAMANT IPFS Node v0.1.0: ストレージ制限、ライフサイクル対応GC、決定論的レプリケーション

ADAMANT IPFS Node v0.1.0では、本番環境向けのストレージライフサイクルを導入しました。これにより、ディスク使用量の制限、失敗したアップロードのクリーンアップ、永続コンテンツと再利用可能なキャッシュの識別、ADAMANTノードセット全体への決定論的なレプリカ配置、不足レプリカの修復が可能となり、アップグレード中も既存のすべてのCIDが保持されます。

導入の背景

従来の実装では、リクエスト制限が判明する前にブロックをブロックストアへストリーミングする可能性がありました。中断や拒否されたアップロードによってブロックが残存し、成功したアップロードは有効期限ポリシーなしでピン留めされ続け、明示的なレプリケーションのクォーラムや修復プロセスが存在しませんでした。そのため、アップロードが消費するディスク容量、どのファイルが永続的でどれが再利用可能か、インポート中にリクエストが切断された場合の影響、CIDに対する責任ノード、確定済みコンテンツを削除せずにノードが容量を解放できるか、クラスターアップグレード後に既存ファイルが利用可能かといった点について、信頼性の高い回答が困難でした。

ストレージ前の受付処理

アップロードは、無制限にディスク容量を消費する前に拒否されます。同時アップロード数は storage.maxConcurrentUploads によって制限され(レスポンス 429)、リクエストの合計サイズは storage.maxRequestSizeBytes (413) で制限されます。これは、チャンク化されたリクエストでは最終サイズが宣言されないため、Content-Length と実際にストリーミングされたバイト数の両方に対して適用されます。storage.diskReserveBytes によって強制されるディスク予約により、空き容量が不足している場合は 507 が返されます。リクエストごとのファイル数は maxFileCount (400)、個々のファイルサイズは uploadLimitSizeBytes (400) で制限されます。同時リクエストはディスクをアトミックに予約するため、複数のアップロードが同じ空き容量を重複して消費することはありません。

各リクエストは、作成したブロックを追跡するアップロードセッションを所有します。パーサーによる拒否、インポート失敗、ルーティングエラー、厳密なクォーラムの失敗、またはクライアントの切断が発生した場合、新しく作成されたブロックのみが削除されます。既存のブロック、他の同時アップロードによって保持されているブロック、およびピン留めされたブロックは保護されます。

明示的なファイルライフサイクル

/adm/files 配下のデータストアベースのレジストリが、すべての既知のCIDのライフサイクルとストレージアカウンティングを記録します。temporary 状態は確認またはトランザクションの完了を待機中のアップロードを表します。confirmed はポリシーによって保護された永続コンテンツを表します。expired は解放済みであり、負荷に応じて再利用可能なコンテンツを表します。pinned および heldLocally は、論理状態とは別に追跡されます。

ライフサイクルの移行、ピン操作、レジストリへの書き込み、アップロードのクリーンアップ、レプリカの確定、修復、およびガベージコレクションは、CIDごとのロックとストレージ全体のコレクションリースによって調整されます。障害発生時の補償処理では、矛盾した状態のまま放置するのではなく、ピンとレジストリレコードの両方を観測されたベースラインに復元します。

デフォルトの storage.confirmationRequired: false 設定では、アップロードが即座に永続化される既存のAPIコントラクトが維持されます。確認を有効にするデプロイメントでは、放棄されたアップロードに対して設定可能なTTLが適用され、認証済みの確認エンドポイントを呼び出す必要があります。

負荷駆動型・ライフサイクル対応のガベージコレクション

ピンの解放とブロックの削除は、意図的に別の処理として分離されています。解放されたファイルはブロックストア内に残り、読み取りリクエストには引き続き無料で対応可能です。ブロックは、ブロックストアが設定された上限(high watermark)を超えるか、ファイルシステムがディスク予約の閾値に達した場合にのみ削除されます。これにより、有用なキャッシュを破棄して後で再取得する無駄を避けています。

コレクターには複数の安全特性があります。このノードが保持する確定済みコンテンツは、決して削除対象として選択されません。確定済みファイルに対する保護が欠落している場合は、削除が開始される前に修復されます。永続コンテンツを検証できない実行は、破壊的なアクションを行う前に中断されます。部分的なGC失敗が発生した場合でもレジストリレコードは保持されるため、次回のパスで安全に再試行できます。ドライランモードでは、ピンやブロックを変更することなく、正確な解放および保持計画をレポートします。スケジュールされたスイープは範囲が限定されており、レジストリ全体を繰り返しスキャンするのではなく、段階的に進行します。

ドキュメント化されたデフォルト値は、上限50 GiB、下限40 GiB、空き容量予約5 GiB、15分ごとのスケジュール実行です。すべての値は設定可能です。スケジュールされたGCはデフォルトで有効ですが、空き容量が安全閾値を上回っている間は削除を行いません。オペレーターは以下のコマンドで計画を確認できます:

curl --fail-with-body \
  -X POST \
  -H "x-api-key: $ADMIN_API_KEY" \
  "https://ipfs.example.org/api/storage/gc?dryRun=true"

既存libp2pネットワーク上のレプリケーション

レプリケーションは追加のHTTPサービスではなく、/adamant/replication/1.0.0 上で実行されます。libp2pハンドシェイクがリモートピアの身元を証明するため、レプリケーションに共有APIシークレット、2つ目のパブリックポート、または個別のクラスターデーモンは不要です。このノードがコンテンツの責任を負う操作は、nodes にリストされたピアからのみ受け付けられます。制御メッセージは長さでフレーム化され、制限が設けられています。レプリカトランザクションは発生元のピアを記録し、そのピアのみが確定処理を行えます。

保持者はCIDに対するランデブーハッシュによって選択されます。同じメンバーシップリストを持つすべてのノードは、中央コーディネーターなしで独立して同じ保持者セットを計算します。デフォルトの配置ポリシーでは、新しいコンテンツに対して4コピー、180日後に3コピー、1年後に2コピーを保持します。この数は実際のネットワークサイズによって上限が設定されるため、3ノードのネットワークで4コピーを要求された場合、すべての利用可能なノードに1コピーずつ配置されます。配置数は最終アクセス時間ではなくファイルの経過時間によって縮小されます。これは、読み取りを追跡するとユーザーがいつファイルを取得したかに関するメタデータが作成されてしまうためです。

厳密なアップロード永続性はオプションです。replication.requireQuorumOnUpload が有効な場合、ローカルの受付とリモートレプリカは1つのロールバック可能なトランザクションを形成します。ピアがコピーをステージングし、発生元が設定された確認クォーラムを検証した後、準備されたすべてのレプリカをコミットまたは中止します。厳密な構成では ackQuorum >= 2 が必要であり、成功した場合は少なくとも1つのリモートコピーが存在することが保証されます。

修復、ハンドオーバー、および取得

修復ジョブは、データを転送する前に、ピアがCIDを既に持っているか、および空き容量があるかを問い合わせます。取り込みは、同時実行数、リクエストサイズ、ディスク予約、タイムアウト、およびピアごとの予算によって制限されます。現在の保持者セット外のノードは、永続コピーを指定された保持者に引き渡し、それらの保持者がファイルを持っていることを確認した後にのみ自身のピンを解放します。もし将来的にリモートの保持者がすべて消失し、ブロックがローカルに残っている場合、ノードは復旧可能な最後のコピーが消滅するのを防ぐため、再び責任を引き受けます。

読み取りも配置情報を使用します。CIDを提供する前に、ノードは有用なBitswapピアが既に接続されていることに頼るのではなく、保持者として期待されるピアに直接接続します。定期的なピアリングにより、起動後も設定されたメッシュが利用可能な状態に保たれます。これはADAMANT Messengerにとって重要です。送信者と受信者は通常異なるインフラストラクチャノードを使用するため、受信者の最初の読み取りは、指定された保持者ではないノードに着地することが一般的だからです。

既存ファイルとCIDの保持

アップグレードによって、保存されているコンテンツの再インポート、書き換え、名前変更は行われません。CID生成は以前のスタックと互換性が維持されるため、既存のメッセージリンクは引き続き同じファイルを参照します。起動時、ライフサイクルレジストリ以前のピンは確定レコードとしてバックフィルされます。DAGサイズはオフラインで測定され、不完全なコンテンツは永続的としてサイレントに登録されるのではなく、報告されます。APIは、バックフィルがバックグラウンドで継続している間にも開始できます。

容量に関する重要な注意点として、レガシーピンの元のアップロード時間は復元できないため、バックフィルされたファイルは初期状態で「新規」として扱われ、最も広い配置階層に入ります。オペレーターは将来のアップロードだけでなく、既存のコーパスに対してもクラスター容量を計画する必要があります。修復プロセスは、すべてを一度にレプリケートしようとするのではなく、制限されたバッチでコーパスを処理します。

現在提供されているのは /adamant/replication/1.0.0 のみであるため、本リリースはクラスター全体での調整されたアップグレードを想定しています。GET /api/storage/metrics はアクティブなプロトコルバージョンを公開し、混在デプロイメントを可視化します。

運用上の可視性とアクセス境界

パブリックな読み取り専用ルートは、ファイル名、CIDインベントリ、ピアトポロジを公開することなく、容量とライフサイクル状態を公開します(GET /api/file/:cid/status、GET /api/storage/metrics、GET /api/storage/policy)。確認、解放、オンデマンドGC、修復、ピン管理、およびlibp2pトポロジ操作などの管理上の変更には、設定された x-api-key が必要です。

ストレージレポートには、ピン留めされたバイト数と再利用可能なバイト数、ファイルシステムの可用性、予約済みおよび使用可能な容量、ライフサイクルカウント、ステージングされたレプリカトランザクション、ジョブステータス、およびレプリケーションの健全性が含まれます。これは、プライベートな運用詳細を公開することなく、アップグレードの検証やその後の収集および修復パスの監視を行うために十分な情報を提供します。

オペレーターが確認すべきデフォルト値

デフォルト値は専用のストレージボリュームに適しており、現在の即時アップロード動作を維持します。合計アップロードサイズは512 MiB、同時アップロード数は32、ディスク予約は5 GiB、一時TTLは24時間、GCスケジュールは15分ごと、修復スケジュールは30分ごと、新規配置は4コピーに設定されています。アップロード確認クォーラムはデフォルトで1(ベストエフォートレプリケーション)であり、厳密なアップロードクォーラムは無効になっています。すべてのオペレーターは、デプロイ前に容量、ウォーターマーク、メンバーシップリスト、および配置階層を確認する必要があります。

検証

マージされた実装は、232個のユニットテスト、102個の統合テスト、本番用TypeScriptビルド、ESLintおよびPrettierチェック、本番依存関係監査、Semgrep SASTおよびSemgrep OSSスキャンを通過しました。また、4ノードネットワーク上でも検証済みです。16個のファイルが新規時に3つの保持者に配置され、次の階層へ移行した後に正確に2つの保持者に収束し、その後64回のノード間読み取りテストにおいて、4つのノードすべてからバイト単位で同一のデータが読み取れることが確認されました。新しいランタイム依存関係は導入されていません。

今後の課題

本リリースは、制限付きストレージとノード間での永続性を確立しましたが、すべての所有権やネットワークメンバーシップの問題を解決したわけではありません。Issue #27 では、元のアップローダーの署名によって承認された削除を追跡します。Issue #28 では、分散型のノード発見とシビル攻撃耐性を追跡します。Issue #29 では、トラフィックアカウンティング、バックオフ、および月間制限を追跡します。コンテンツの暗号化は引き続きADAMANTクライアントプロトコルの責任範囲であり、ストレージノードはCIDによって暗号化されたコンテンツを管理するため、プレーンテキストへのアクセスは不要です。