SolrのDistributed IDF(分散IDF)のコードリーディングと内容理解

# Distributed IDF
少し(結構)前の話になるのですが、Solr5系でDistributed IDF(分散IDF)が導入されました。
私の担当しているプロジェクトでも、分散IDFどうなの？という話になったのですが、導入に対するデメリットが明らかになっていなかったのと、そこそこ更新リクエストが発生するコレクションを扱っており、メリットも無さそうだったので、そのままになっていました。
仮に導入する場合、個人的に気になっていたのはパフォーマンスの劣化、具体的には検索のレイテンシー(qtime)と更新の処理速度がどれだけ落ちるのかという点でした。
良い機会なのでパフォーマンスを測ろうかと思ったのですが、まず分散IDFのパッチを読んでどんな処理をしているのか、パフォーマンスに影響が出そうな箇所はどこかという勘所をざっくりと把握したいと思います。


# Solrのバージョン
5.5系
今更ですが、私は5.5系を運用しているため

# JIRAとパッチ
[SOLR-1632](https://issues.apache.org/jira/browse/SOLR-1632)
[パッチ](https://github.com/apache/lucene-solr/commit/73a6fca89032399c5317ea9760cacf0d30914a47)


# 分散IDFの実装の種類
分散IDFには4種類があります。
以下は[公式ドキュメント](https://cwiki.apache.org/confluence/display/solr/Distributed+Requests)の直訳になります。

* [LocalStatsCache](https://github.com/apache/lucene-solr/commit/73a6fca89032399c5317ea9760cacf0d30914a47#diff-b42759bce3ece6ba6f964464ca8cb886)
    * ローカルのDF値を使ってスコア計算をする、従来通りの挙動
    * デフォルトではこれが採用されている
* [ExactStatsCache](https://github.com/apache/lucene-solr/blob/73a6fca89032399c5317ea9760cacf0d30914a47/solr/core/src/java/org/apache/solr/search/stats/ExactStatsCache.java)
    * DF値をコレクションをまたがって保持する
* [ExactSharedStatsCache](https://github.com/apache/lucene-solr/blob/73a6fca89032399c5317ea9760cacf0d30914a47/solr/core/src/java/org/apache/solr/search/stats/ExactSharedStatsCache.java)
    * ExactStatsCacheと同じ挙動をするが、後続のリクエストで同じタームに対してはグローバルな統計値が再利用される
* [LRUStatsCache](https://github.com/apache/lucene-solr/blob/73a6fca89032399c5317ea9760cacf0d30914a47/solr/core/src/java/org/apache/solr/search/stats/LRUStatsCache.java)
    * LRU cacheで統計値を保持するように実装されている
    * リクエスト間で値が共有される

# 処理フロー

コードを追ってみるとどうやら分散IDFの取得と更新は検索時にされるようで、インデックスの更新時には特に何もしていないようです。
ざっくりとした処理フローは下記の通りです。

* 分散検索の起点
    * https://github.com/apache/lucene-solr/blob/53981795fd73e85aae1892c3c72344af7c57083a/solr/core/src/java/org/apache/solr/handler/component/SearchHandler.java#L344
    * SearchHandlerからQueryComponetが処理される
    * https://github.com/apache/lucene-solr/blob/73a6fca89032399c5317ea9760cacf0d30914a47/solr/core/src/java/org/apache/solr/handler/component/QueryComponent.java#L657
* 分散IDFの取得
    * https://github.com/apache/lucene-solr/blob/73a6fca89032399c5317ea9760cacf0d30914a47/solr/core/src/java/org/apache/solr/handler/component/QueryComponent.java#L699
* 検索終了時に分散IDFの更新
    * https://github.com/apache/lucene-solr/blob/53981795fd73e85aae1892c3c72344af7c57083a/solr/core/src/java/org/apache/solr/handler/component/SearchHandler.java#L422
    * https://github.com/apache/lucene-solr/blob/73a6fca89032399c5317ea9760cacf0d30914a47/solr/core/src/java/org/apache/solr/handler/component/QueryComponent.java#L745


# 考察と気になった点
大雑把な理解になりますが、分散IDFは下記のような実装になっていました。

* キャッシュとして保存される
* 検索時にグローバルの値を取得しキャッシュされる
* 検索終了時にグローバルの値を更新する

以上を踏まえると、当初気になっていた検索と更新への影響についてですが、更新には直接パフォーマンスを劣化を招くような要因は無さそうでした。
検索についてはキャッシュの取得と更新時に、グローバルな値を保存する場所への問い合わせ(各シャードのリーダーもしくはzookeepr?)が発生するため、そこでオーバーヘッドが発生しそうな感じでした。
グローバルな値をどこで持っているのかが気になります。
各シャードのリーダーが持つ場合、シャード数が多いコレクションではオーバーヘッドも大きくなりそうな印象を受けました。


# まとめ
分散IDFは検索時にオーバーヘッドは発生することが分かりました。
インデックスの更新に関しては特に影響などは無さそうです。
グローバルな値の問い合わせにコストがかかりそうな感じだったので、レイテンシーの要件が厳しい場合は導入する場合は事前のパフォーマンスの検証をしてみる必要がありそうです。
ちなみに、分散IDFを導入されている方はどれくらいいらっしゃるのでしょうか？