1. キャッシュされた解決

キャッシュされた解決 

キャッシュされた解決は、依存関係解決のスケーラビリティパフォーマンスに対処するために、0.13.7以降に追加されたsbtの実験的機能です。

セットアップ 

キャッシュされた解決を設定するには、プロジェクトのビルドに次の設定を含めます

updateOptions := updateOptions.value.withCachedResolution(true)

グラフとしての依存関係 

プロジェクトは、libraryDependencies設定を使用して独自のライブラリ依存関係を宣言します。追加したライブラリも、それらの推移的な依存関係を取り込みます。たとえば、プロジェクトがdispatch-core 0.11.2に依存している場合、dispatch-core 0.11.2はasync-http-client 1.8.10に依存し、async-http-client 1.8.10はnetty 3.9.2.Finalに依存するなどです。各ライブラリを、依存ノードに向かう矢印を持つノードと考えると、全体の依存関係をグラフ、特に有向非巡回グラフとして考えることができます。

このグラフのような構造は、Apache Ivyから採用されたもので、推移的に上書きルールと除外を定義できますが、ノードの数が増加するにつれて、依存関係を解決するのにかかる時間が大幅に長くなります。詳細については、このページの後半にある動機セクションを参照してください。

キャッシュされた解決 

キャッシュされた解決機能は、最後のcompile以降に変更されたソースのみを再コンパイルするインクリメンタルコンパイルに似ています。Scalaコンパイラとは異なり、Ivyには個別のコンパイルの概念がないため、それを実装する必要がありました。

キャッシュされた解決機能は、完全な依存関係グラフを解決する代わりに、すべての関連サブプロジェクトに表示される各直接依存関係に対して1つずつ、ミニグラフを作成します。これらのミニグラフは、Ivyの解決エンジンを使用して解決され、結果はすべてのビルドで共有される$HOME/.sbt/1.0/dependency/(またはsbt.dependency.baseフラグで指定されたもの)の下にローカルに保存されます。すべてのミニグラフが解決された後、競合解決アルゴリズム(通常は最新バージョンを選択)を適用して結合されます。

プロジェクトに新しいライブラリを追加すると、キャッシュされた解決機能は、$HOME/.sbt/1.0/dependency/の下にあるミニグラフファイルをチェックし、以前に解決されたノードをロードします。これにより、ごくわずかなI/Oオーバーヘッドが発生し、新しく追加されたライブラリのみが解決されます。意図されたパフォーマンスの向上は、2番目と3番目のサブプロジェクトが最初のサブプロジェクトから解決されたミニグラフを利用して、重複した作業を回避できることです。次の図は、すべて同じJSONファイルのセットにアクセスするプロジェクトA、B、Cを示しています。


fig1

実際のスピードアップはケースバイケースで異なりますが、多数のサブプロジェクトがある場合は大幅なスピードアップが見られるはずです。ユーザーからの最初のレポートでは、260秒から25秒への変化が示されました。あなたの結果は異なる場合があります。

注意点と既知の問題 

キャッシュされた解決は実験的な機能であり、いくつかの問題が発生する可能性があります。それらを見つけたら、GitHub Issueまたはsbt-devリストに報告してください。

最初の実行 

最初に実行するとき、キャッシュされた解決は、すべてのミニグラフを解決して結果をファイルシステムに保存する必要があるため、遅くなる可能性があります。システムが認識していない新しいノードを追加するたびに、ミニグラフが保存されます。2回目以降の実行は高速になるはずですが、完全な解決updateと2回目以降の実行を比較することは公平な比較ではない可能性があります。

Ivyの忠実性は保証されていません 

Ivyの動作の一部は、特にMavenエミュレーションに関して理にかなっていません。たとえば、Maven公開ライブラリによって導入されたすべての推移的な依存関係を、元のpom.xmlにそうするように書かれていなくても、force()として扱うように見えます。

$ cat ~/.ivy2/cache/com.ning/async-http-client/ivy-1.8.10.xml | grep netty
    <dependency org="io.netty" name="netty" rev="3.9.2.Final" force="true" conf="compile->compile(*),master(*);runtime->runtime(*)"/>

Maven分類子が異なる同じライブラリへの複数の依存関係に関するいくつかの問題もあります。これらの場合、通常のupdateとまったく同じ結果を再現することは理にかなっていないか、完全に不可能です。

SNAPSHOTと動的な依存関係 

ミニグラフにSNAPSHOTまたは動的な依存関係のいずれかが含まれている場合、グラフは動的と見なされ、単一のタスク実行後に無効になります。したがって、グラフにSNAPSHOTがある場合、エクスペリエンスが低下する可能性があります。(これは将来改善される可能性があります)

updateOptionsという設定キーは、updateタスクを使用した管理された依存関係の解決の詳細をカスタマイズします。そのフラグの1つはlatestSnapshotsと呼ばれ、チェーンされたリゾルバーの動作を制御します。0.13.6まで、sbtはチェーンに沿って最初に見つかった-SNAPSHOTリビジョンを選択していました。latestSnapshotsが有効になっている場合(デフォルト:true)、チェーン上のすべてのリゾルバーを調べて、公開日を使用して比較します。

トレードオフは、ビルドに多くのリモートリポジトリがある場合、またはサーバーから離れた場所に住んでいる場合は、おそらく解決時間が長くなることです。そのため、無効にする方法は次のとおりです

    updateOptions := updateOptions.value.withLatestSnapshots(false)

動機 

sbtは内部でApache Ivyを使用してライブラリの依存関係を解決します。sbtは、長年にわたって独自の依存関係解決エンジンを再発明する必要がないというメリットがありましたが、特に複数のサブプロジェクトと大規模な依存関係グラフを持つプロジェクトでは、スケーラビリティの課題がますます大きくなっています。sbtの解決のスケーラビリティには、いくつかの要因が関与しています

  • グラフ内の推移的ノード(ライブラリ)の数
  • 除外および上書きルール
  • サブプロジェクトの数
  • 構成
  • リポジトリの数とその可用性
  • 分類子(IDEで使用される追加のソースとドキュメント)

上記の要因のうち、最も影響が大きいのは推移的ノードの数です。

  1. ノードが多いほど、バージョン競合の可能性が高くなります。競合は通常、同じライブラリ内で最新のバージョンを選択することで解決されます。
  2. ノードが多いほど、除外および上書きルールを確認するためにバックトラックする必要性が高まります。

除外および上書きルールは推移的に適用されるため、新しいノードがグラフに導入されるたびに、親ノードのルール、祖父母ノードのルール、曾祖父母ノードのルールなどを確認する必要があります。

sbtは、コンフィギュレーションとサブプロジェクトを独立した依存関係グラフとして扱います。これにより、異なるコンフィギュレーションやサブプロジェクトに対して任意のライブラリを含めることができますが、依存関係解決が遅い場合、線形的なスケーリングが問題となります。以前にライブラリ依存関係の結果をキャッシュする試みはありましたが、libraryDependenciesが変更されると、依然として完全な解決が必要でした。