# 高度なElixirキャッシュ設定

<.localized_link href="/guides/features/cache/elixir">Elixirキャッシュのセットアップガイド</.localized_link>から始めましょう。以下のオプションは、プロジェクトに追加のコンパイル入力、依存関係の例外、または異なるチェックアウトパスがある場合にのみ必要です。

## 依存関係の選択

`cache: true`はサポートされている依存関係を自動的にキャッシュします。キーワードリストでも同じ自動選択が行われ、任意の上書きが可能です。

```elixir
cache: [exclude: [:a_dependency_with_compile_time_side_effects]]
```

代わりに特定の依存関係にのみキャッシュを制限するには:

```elixir
cache: [dependencies: [:nimble_options, :nimble_parsec]]
```

`dependencies: :all`はサポートされているすべての依存関係を明示的に選択し、`dependencies: []`はnoneを選択します。`exclude:`はいずれの選択よりも優先されます。`cache`を省略するか、`false`または`nil`に設定すると、キャッシュが無効になります。

サポートされている依存関係はHexまたはGit経由で取得され、Elixir/Erlangソースを使用した標準的なMixコンパイラパイプライン、またはrebar3の通常の純粋なBEAMコンパイルを使用します。Mix依存関係は、その上流の依存関係がrebar3を使用している場合、アーティファクトを再利用することもできます。Rebar設定、ソース/ヘッダーファイル、およびrebar3実行可能ファイルのフィンガープリントがキーに含まれます。パッケージ化された`ebin/*.app`メタデータと受動的なMakefileもサポートされています。Rebar設定スクリプトはルックアップ前にMixによってすでに評価されており、そのソースと有効な設定がキーに入ります。アプリケーションソーススクリプト(`*.app.src.script`)は後でrebar3によって評価され、ビルドプラグインやコンパイルフックと同様に、監査済みのビルダー契約が必要です。欠落しているrebar3ランチャーは、Mixの通常のインストーラーを使用してフィンガープリント作成前にインストールされます。コールドワーカーはそのインストーラーへのアクセスを依然として必要とします。

カスタムトレーサー、ソースシンボリックリンク、アンブレラルート、およびローカル依存関係を含むチェーンは正常にコンパイルされます。Tuist自身のブートストラップ依存関係も、コールドワーカー上で正常にコンパイルされます。構造的な適格性は、任意のコンパイルコードがハーメティックであることを証明するものではありません。

## 追加の入力を宣言する

ソースツリー、ロックされた依存関係ID、上流ソース、BEAMおよびリソース、依存関係の実行時/オプション/アプリケーションフラグ、解決されたアプリケーションメタデータ、明示的な有効なアプリケーション設定、コンパイラオプション(レキサーおよびパーサーオプションを含む)、Mix環境とターゲット、およびツールチェーンIDは自動的に含まれます。依存関係固有の`env:`および`system_env:`がキーに入り、その環境下で設定が評価されます。アプリケーションのデフォルトはVMロード状態ではなく、ソース入力を通じて入ります。

ソースツリーの外部のファイルを読み取る依存関係や、コンパイル時に追加の環境変数を読み取る依存関係については、それらの入力を宣言してください。

```elixir
cache: [
  inputs: ["schemas", "config/compiler-input.json"],
  env: ["YOUR_COMPILER_OPTION"]
]
```

Elixirマクロとモジュール本体は、任意のファイル、環境、周囲のコードパス、ネットワークレスポンス、および時間を読み取ることができます。Tuistはこれらの読み取りを自動的に検出しません。未宣言の入力を変更すると、古い出力が生じる可能性があります。時間に依存したコンパイルやネットワーク依存のコンパイル、あるいはすべてのビルドで実行しなければならない副作用を持つ依存関係を除外してください。より厳密な制御が必要な場合は、`dependencies:`を使用して監査済みの依存関係許可リストを作成してください。

標準的なコンパイラ/実行時変数(`ERL_COMPILER_OPTIONS`、`ELIXIR_ERL_OPTIONS`、`ERL_AFLAGS`、`ERL_FLAGS`、`ERL_ZFLAGS`、`SOURCE_DATE_EPOCH`、`ERL_LIBS`、`MIX_PATH`、および`MIX_ARCHIVES`)は自動的にキャプチャされます。ツールチェーンIDには、Elixirビルド、利用可能な場合の完全なOTPバージョン、ERTS、コンパイラバイトコード、OS/バージョン、アーキテクチャ、ワードサイズ、およびバイトオーダーが含まれます。有効なコンテキストは各ルックアップの前にキャプチャされます。依存関係ごとの入力モデルは、1回の準備内でのルックアップ計画のために共有される場合があります。コンパイルは計画をクリアし、復元はインストールされたアプリケーションのレコードを無効化します。段階的なインストールと公開は、ルックアップ計画を再利用せずに常に新しい入力をチェックします。ルックアップミスもまた、コンパイル前に新しいキーをキャプチャします。入力の変更は、以前の入力用にビルドされたコードをインストールするのではなく、段階的なヒットを拒否します。

キーは決定論的な項エンコーディングとソートされたファイルシステムトラバーサルを使用します。順序付けられていないビルダー宣言と宣言された入力セットはソートされ重複除去されます。順序付けられたコンパイラオプションはその順序を保持します。重要なのはファイルの内容、名前、実行ビット、および欠落しているパスであり、ファイルシステムタイムスタンプではありません。ファイル宣言は所有権、タイムスタンプ、またはその他のファイルシステムメタデータをモデル化しません。これらの値から出力を導出するビルダーは除外してください。上流コンパイラマニフェストは増分管理として除外されます。それらの内容を意図的に読み取るコードは、それらを追加の入力として宣言する必要があります。正規表現設定はそのソースとオプションを使用し、VMローカルのコンパイル済み参照を使用することはありません。他の不透明な実行時値はキャッシュをバイパスします。更新されたキーモデルは、移行なしに古いエントリを無効化します。

決定論的なキーエンコーディングは、決定論的なコンパイラ出力とは別物です。最近のElixirバージョンで、 otherwise identical buildsの間で上流BEAMメタデータが変更される場合、SDKブートストラップ、シード、およびキャッシュ消費の前に`ERL_COMPILER_OPTIONS='[deterministic]'`を設定することを検討してください。既存のコンパイラオプションとマージし、設定を変更する際に依存関係の出力を一度再ビルドしてください。SDKはこれを自動的に設定しません。その正確な値はキーの一部のままです。完全な上流バイトは依然としてハッシュされ、正規化または削除されることはありません。これはクロスチェックアウトや任意のビルダーの再現性保証ではありません。このコンパイラオプションはまた、`module_info(:compile)`からソース/オプションを省略するため、採用する前にカバレッジ、デバッグ、およびソース検出ツールを検証してください。コンパイラフラグは順序付けされていないコンパイル時ジェネレーターを修正しません。SDKにはMint 1.10.2以降(セキュリティバックポートを含む)が必要です。このリリースでは、HTTP/2フレーム関数を生成するために使用されるマップをソートします。SDKをアップグレードする際は、古いMintロックエントリを更新してください。他の依存関係やコンパイラパスは、`deterministic`が有効であっても、変動するブートストラップBEAMを生成する可能性があります。

レキサーおよびパーサーの出力は、ロックされたGitツリーまたはHexパッケージファイルマニフェストに存在しない場合にのみ、派生ファイルとして扱われます。出荷済みの`.erl`ファイルと不透明なディレクトリ/サブモジュールの内容は通常の入力のままです。再生成された出荷済みファイルは、同じステージ後の入力整合性を通過する必要があります。OTPの`parsetools`からのジェネレーターテンプレートもツールチェーンIDに入ります。並行ビルド間で依存関係ソースのチェックアウトを分離しておいてください。ジェネレーターとネイティブビルダーは`deps/`に書き込む可能性があります。ネストされた/カスタムのrebarジェネレーターレイアウトは、依然として監査済みの`generated`宣言を必要とする場合があります。

## ネイティブおよびカスタムビルダー

監査済みのMix、rebar3、またはMakeビルダーの場合、依存関係ごとのコントラクトを宣言します。

```elixir
cache: [
  builders: [
    bcrypt_elixir: [
      platform: "build-image@sha256:YOUR_IMMUTABLE_IMAGE_DIGEST",
      outputs: ["priv/bcrypt_nif.so"],
      tools: ["cc", "make"],
      env: ["YOUR_BUILDER_OPTION"]
    ]
  ]
]
```

`platform`は互換性のあるネイティブビルド環境を識別します。コンパイラー、SDK、システムヘッダー、共有ライブラリーをカバーする不変のイメージ/ツールチェーンIDを使用し、`linux`のような再利用可能なラベルは使用しないでください。Elixir/OTPおよび通常のプラットフォームチェックは引き続き適用されます。これはビルダーの明示的な監査であり、ネイティブ入力や必要な副作用の自動検出ではありません。無効な明示的ビルダーコントラクトは、キャッシュをサイレントに無効にしたままにするのではなく、対処可能な設定エラーを発生させます。

`outputs`は、依存関係のアプリケーションビルドディレクトリに対する相対パスとして必要なパスを一覧表示します。ランタイムで実際に必要なライブラリー/実行可能ファイルを一覧表示してください。出力が欠落している場合、MixがBEAMを最新とみなしていても復元またはコンパイルがトリガーされ、不完全なスナップショットは拒否されます。追加の環境変数はビルダーの`env`で、外部ファイル/ディレクトリはその`inputs`で、消費プロジェクトからの相対パスとして宣言します。ネイティブキーには、依存関係の有効な`system_env`オーバーライドを使用して、`PATH`、`CC`、`CXX`、`CFLAGS`、`LDFLAGS`、`SDKROOT`、`RUSTFLAGS`などの標準ビルド環境変数が自動的に含まれます。解決されたMixプロジェクト設定もネイティブビルダーキーに含まれます。不透明な関数値の設定はキャッシュできません。

オプションの`tools`名はその有効な`PATH`上で解決されます。相対ツールパスは依存関係ソースディレクトリからの相対パスです。実行可能バイトと解決されたパスがキーに入り、利用できない宣言済みツールはキャッシュをバイパスします。ランチャーまたはコンパイラーラッパーのフィンガープリントは、そのSDK、サブプロセス、またはシステムライブラリーをカバーしません。それらをカバーするには、不変の`platform`と追加の`inputs`が必要です。`MAKEFLAGS`はカスタムビルダーが読み取れるため、そのまま保持されます。継承されたGNU makeジョブサーバー識別子により、実行間で安全なキャッシュミスが発生する可能性があります。それらの値がキーから削除されると想定するのではなく、分離されたキャッシュジョブで安定したビルドフラグを使用してください。

一部のビルダーは、`_build/`だけでなく`deps/`の下にもファイルを生成します。それらのパスを、その依存関係のソースディレクトリからの相対パスとして`generated`に一覧表示します（例：`generated: ["priv/native"]`）。ソース入力から除外されるのは、それらの生成されたパスのみです。元のリソースやソースファイルを除外しないでください。それらは入力として残す必要があります。パスはリテラルであり、グロブやトラバーサルはありません。コントラクト、プラットフォーム、追加入力、環境値、およびカスタム依存関係コンパイルコマンドはすべてキーに関与します。`compile: false`はコンパイルからのオプトアウトのままです。

監査済みのスナップショットは、ソース-backedな`ebin`、`priv`、`include`、`src`リソースを復元されたアプリケーション内に具体化します。したがって、ネイティブライブラリー、ポート実行可能ファイル、ヘッダー、および生成されたリソースは、依存関係ソースを変更することなく、コールドチェックアウト後も存続します。実行可能モードと含まれる相対リソースシンボリックリンクは保持されます。エスケープ、絶対パス、および循環リンクは拒否されます。すべてのアップストリームアプリケーションリソースはダウンストリームキーに入ります。これには、通常の`priv`リソースやカスタム出力ディレクトリが含まれ、ネイティブライブラリーやBEAMだけではありません。プロデューサーソースパスを直接読み取るコードは再配置されません。アプリケーションアーティファクトの外側に必須の副作用を持つビルダーは、依然としてそれらのステップを必要とします。

入力と出力が監査されるまで、カスタムビルダーは除外されたままにしてください。コントラクトがあっても、コールドSDKブートストラップコンパイル、64 MiBのアーティファクト制限、または互換性のあるビルド環境の必要性がなくなるわけではありません。

## チェックアウトパス間での再利用

チェックアウトパスはデフォルトでキーに参加します。依存関係がプロデューサーパスに依存していない場合は、異なるチェックアウト場所間での再利用を有効にします。

```elixir
cache: [portable: true]
```

`config/config.exs`内の`__DIR__`から構築されたパスなど、チェックアウトパスを埋め込む構成値は、ポータブルキーを変更しません。Elixirツールチェーンのインストール場所も同様です。アーカイブとrebar3は内容に基づいてカウントされます。

キャッシュされたBEAMは、プロデューサーのデバッグ/ソースパスと、コンパイル時の`__DIR__`、`__ENV__.file`、`File.cwd!()`の値を保持します。ポータブル再利用はこれらを書き換えません。リソースリンクは現在のソースチェックアウトを指します。アップストリームBEAMバイトもキーに入り、パスを埋め込む可能性があるため、再利用には独立して再コンパイルするのではなく、同じキャッシュからアップストリームクロージャーを復元することが必要になる場合があります。

## ストレージとアクセス

| オプション | デフォルト | 目的 |
| --- | --- | --- |
| `remote` | `true` | ローカルミス後に共有リモートキャッシュを使用します。 |
| `upload` | `true` | 新しいバンドルをリモートで公開します。`false`でも読み取りとローカルスナップショットは可能です。 |
| `path` | Tuistのキャッシュディレクトリ（`mix/dependencies/v1`の下） | 別のローカルストアを選択します。 |
| `endpoint` | 自動検出 | アカウントのHTTPS REAPIエンドポイントをオーバーライドします。 |

`upload: false`はクライアント動作を制御し、認証を制御するものではありません。プロジェクトキャッシュの権限はサーバーによって強制されます。プロジェクトのキャッシュを読み取り専用できる資格情報は、アップロードを自動的にスキップします。プロジェクトの`endpoint:`オプションと検出されたエンドポイントは、ループバック（`localhost`、`127.0.0.1`、`::1`）開発エンドポイントを除き、HTTPSが必要です。

空でない`TUIST_CACHE_ENDPOINT`は、`endpoint:`と検出の両方をオーバーライドします。ホストされたランナーは、クラスター内のプライベートキャッシュに到達するためにこれを使用します。このオペレーター制御の環境オーバーライドは、非ループバックホストでのHTTPも許可します。HTTPはプロジェクトスコープのキャッシュトークンとアーティファクトを暗号化せずに送信するため、信頼されたネットワークでのみ使用してください。無効なオーバーライドは、検出を試行せずにローカルスナップショットにフォールバックします。

ローカルスナップショットとリモートバンドルは64 MiBに制限されています。より大きなアーティファクトはキャッシュされずにコンパイルされます。ローカルストアには自動.eviction機能がないため、必要に応じて内容を削除してください。リモートアーティファクトは、Kuraの既存の保持、.eviction、およびプロジェクトクリーンアップルールを使用します。

> [!IMPORTANT]
> コンパイラーマニフェストとBEAMには、シークレットを含むプロデューサーパスとコンパイル時値が埋め込まれる可能性があります。プロジェクトキャッシュリーダーはこれらのアーティファクトにアクセスできます。キャッシュライターは信頼されたコードプロデューサーです。ダイジェストチェックは破損を検出しますが、悪意のある実行可能BEAMを検出するものではありません。キャッシュ書き込み資格情報を信頼できないPRジョブに公開したり、それらの資格情報が利用可能な状態で信頼できない依存関係をコンパイルしたりしないでください。

## ヒット不足のトラブルシューティング

- パッケージをインストールした後、`mix tuist.setup`を実行し、生成されたMix設定をコミットします。セットアップは他のエイリアスとTuistオプションを保持します。カスタマイズされたコンパイルアクションをサイレントに削除するのではなく、それを拒否します。
- 生成されたコンパイルエイリアスを保持してください。直接`mix tuist.compile`タスクを検出すると、キャッシングが検査する前に依存関係がコンパイルされる可能性があります。
- 選択されたサポート対象の依存関係について、すでに最新で有効な出力は、理由が`up_to_date`であるローカルヒットとしてカウントされます。これらは遡及的に公開されず、アーティファクトキー、転送バイト数、復元レイテンシ、または節約された推定時間は持ちません。スナップショットの復元をテストするには、キャッシュにデータが入った後、`mix deps.clean NAME --build`を使用してサポート対象の依存関係のビルドのみをクリーンし、再度コンパイルしてください。
- `MIX_ENV`、`MIX_TARGET`、コンパイラー設定、および宣言された入力を一致させてください。ポータブルな再利用が有効になっていない限り、パスの変更もキーを変更します。
- サポートされていない依存関係は、ミスではなく**キャッシュされていません**として表示されます。これらはヒット率から除外されます。ルックアップも最新のローカルヒットもないビルドのレートは0%ではなく、利用不可となります。
- 資格情報の欠如、利用できないエンドポイント、および破損したスナップショットはコンパイルにフォールバックします。コンパイラーのエラーは依然としてビルドを失敗させます。
- CASレコードはビルドレポートの後に到着する可能性があります。新しく取り込まれた転送を確認するには、更新してください。ローカルヒットとレガシービルドにはリモートCAS出力がありません。

サーバーは、`up_to_date`ローカルヒットを記録するために、コンパイルレポート契約`0.2`をサポートする必要があります。古いサーバーがキャッシュデータやスキーマ列挙値を明示的に拒否する場合、パッケージはこれらの新しい再利用観察なしでレポートを一度再試行します。コンパイルの詳細と他のキャッシュ結果は保持されますが、サーバーがアップグレードされるまで新しいローカル再利用は利用できません。サーバーを先にデプロイすることで、この追加のリクエストを回避できます。契約バージョンはサポートをネゴシエートせず、認証エラー、トランスポートエラー、およびその他の検証メッセージは再試行されません。列挙エラーは古いサーバーではそのフィールドを特定しません。保持された無効なフィールドは、単一の再試行でも依然として失敗します。

## 手動コンパイル統合

セットアップがプロジェクトの設定を安全に編集できない場合は、カスタムコンパイルアクションを保持しながら、このエイリアスとこれらの関数をMixプロジェクトモジュールに手動で追加してください:

```elixir
# In aliases/0:
compile: &compile/1

# In the same Mix project module:
defp compile(args) do
  if "--no-compile" not in args do
    compile_tuist_ex()
  end

  Mix.Task.run("deps.loadpaths", ["--no-compile", "--no-deps-check"])
  Mix.Task.run("tuist.compile", args)
end

defp compile_tuist_ex do
  deps =
    ["tuist_ex"]
    |> Mix.Dep.filter_by_name(Mix.Dep.load_and_cache(), include_children: true)
    |> Enum.reject(&(Mix.Dep.ok?(&1) and &1.scm.fetchable?()))
    |> Enum.map(&Atom.to_string(&1.app))

  if deps != [], do: Mix.Task.run("deps.compile", deps)
end
```

これらの関数は、コールドな並列ビルドを含む他の依存関係よりも前にTuistをコンパイルして読み込み、Tuistとその依存関係がすでにコンパイルされている場合、またはコマンドが`--no-compile`で実行されている場合はその作業をスキップします。同じ関数はHex、Git、およびパスインストールでも動作し、スクリプトパスは必要ありません。

エイリアスが`deps.compile tuist_ex --include-children`で始まる場合、すべての`mix compile`はTuistの依存関係を再ビルドし、プロジェクト全体を再コンパイルする可能性があります。これを置き換えるには、`mix tuist.setup`を再度実行してください。

## 仕組み

Elixirのキャッシングは、別のMixストレージサービスではなく、Bazelで使用されている共有REAPI ActionCache/CASパイプライン、認証、およびKura署名付きアナリティクスを使用します。モデル化された入力キーは、完全なコンパイルバンドルの生バイトSHA-256ダイジェストを含むActionResultを参照します。Tuistは、段階的なインストールの前に、ファイル、アプリケーションメタデータ、宣言されたBEAM、およびSCMマニフェストを検証します。破損したエントリは、コンパイルが成功した後に修復されます。

OTP 28以降では、リモートセッションは少なくとも1 KiBのByteStream転送に対してzstd圧縮をネゴシエートします。圧縮不可能なアップロード、古いOTPランタイム、および圧縮をサポートしないサーバーは、identityトランスポートを使用します。ダイジェスト、アーティファクト制限、および報告されたバイト数は、圧縮されていないコンテンツを指します。デコードされたバイトは、インストール前に境界チェックと検証が行われます。各依存関係は単一のCASバンドルとして残り、小さなアーティファクトに対するファイルごとまたはチャンクごとのリクエストを回避します。

リモート資格情報/接続は遅延解決されます。アイドル状態のビルドとローカルヒット（すでに最新の出力を含む）はリモートエンドポイントに接触しません。コンパイラーのエラーを含むすべての終了時に、1つのセッションが閉じられます。クライアントRPCには30秒のデッドラインがあります。Mixは、ロックチェック、ローカル依存関係のコンパイル、およびサポートされていない作業を担当します。

ダッシュボードのレイテンシ/バイト数は、ローカルとリモートの出自を保持し、利用不可の値と測定されたゼロを区別します。リモートタイミングはクライアントCAS転送を測定し、ローカルタイミングには検証とインストールが含まれますが、どちらもコンパイルは含みません。CAS Outputsは、ローカルコピーやアクション入力キーではなく、成功したKura署名付き転送を使用します。スループットはバイト数を測定時間で割った値であり、圧縮サイズは利用できません。回避された推定コンパイル時間は、プロデューサーの元のコンパイル期間であり、測定されたエンドツーエンドのビルド節約時間ではありません。

独立して準備できた依存関係は、同じHTTP/2接続上で最大4つの同時リクエストのウィンドウでフェッチされます。これにより、デコードされたバンドルのバッファリングは4つのアーティファクトに制限され、それぞれが64 MiBの制限の対象となります。キーの計画、Mixの状態、およびインストールは直列化されたままです。下流のキーは上流のアーティファクトを待ちます。プリフェッチされたバイトは、正確な入力キーの下でのみ消費され、リモートインストールの前に入力が再チェックされます。すでに最新の出力とローカルスナップショットは、投機的なリモートリクエストを引き起こしません。選択されたキャッシュミスのコンパイルとアップロードは直列のままです。並行性は、すべてのビルドが高速になることを保証するものではありません。

## すべての依存関係がコンパイルをスキップできますか？

互換性のあるヒットは、サポート対象の依存関係のコンパイルをスキップできますが、`cache: true`はフェイルオープンであり、コンパイルゼロの保証ではありません。欠落/削除されたエントリ、サポートされていないビルダー、およびコールドSDKブートストラップは依然としてコンパイルされる可能性があります。高いヒット率だけでは、壁時計時間の速度向上を保証しません。

一度ビルドして多数テストするCIの場合、
<.localized_link href="/guides/features/test-sharding/elixir">Elixir test sharding</.localized_link>
を使用して、正確に準備されたビルドを配布し、すべてのワーカーが依存関係ごとのキャッシュヒットからそれを再構築することを期待しないでください。完全なビルド、特にネイティブライブラリを含むビルドを再利用するには、依然として互換性のあるプラットフォーム、ツールチェーン、および構成が必要です。任意のカスタムビルドは、外部入力、出力、および必要な副作用をモデル化せずに、ビルドステップを安全にスキップすることはできません。
