Value Cacheの憂鬱【MIRACLE ZBX 2.2】
Value Cacheって何なの?
Zabbix 2.2からValue Cacheが追加されました。Zabbix LLCによる説明では下記のようになっています。
トリガーの条件式、計算/集計されたアイテム、複数のマクロをより速く計算するため、Zabbix 2.2から、値キャッシュオプションがZabbixサーバでサポートされています。
値キャッシュを使用して、データベースに直接SQLコールを発する代わりに、ヒストリデータにアクセスできます。ヒストリの値がキャッシュに存在しない場合は、存在しない値がデータベースに要求され、それに応じてキャッシュが更新されます。
枯渇時の挙動に問題
当社のサポートのお客様から何件か質問がありました。どちらも、「枯渇した(使用量が大きくなった)ときの挙動に問題がある。」といったものです。
少なくとも、下記のログが延々と出力されて止まることがありません。
value cache is fully used: please increase ValueCacheSize configuration parameter
ソースコードから追ってみる
問題を調査していくために、ソースコードを追ってみます。(ZBX-2.2.9-1を使用します。)
Value Cacheに関する実装は、src/libs/zbxdbcache/valuecache.c に記述されています。
Value Cacheに値を挿入する関数は、zbx_vc_add_value()となります。
2499 int zbx_vc_add_value(zbx_uint64_t itemid, int value_type, const zbx_timespec_t *timestamp, history_valu e_t *value)
2500 {
...
2514 if (NULL != (item = zbx_hashset_search(&vc_cache->items, &itemid)))
2515 {
...
2527 if (item->value_type != value_type || FAIL == (ret = vch_item_add_value_at_head(item, &record)))
2528 item->state |= ZBX_ITEM_STATE_REMOVE_PENDING;
2529
2530 vc_item_release(item);
2531 }
...
2514行目において、ハッシュテーブルにて該当するitemidを走査し、存在する場合に2527行目でvch_item_add_value_ata_head()をコールして、ハッシュテーブルに値を入れています。
では、どこでハッシュテーブルに枠を作っているのでしょう?
zbx_vc_get_value_range()とzbx_vc_get_value()に同様な処理があります。(以下はzbx_vc_get_value()のコードです。)
2678 int zbx_vc_get_value(zbx_uint64_t itemid, int value_type, const zbx_timespec_t *ts, zbx_history_record_ t *value)
2679 {
...
2695 if (NULL == (item = zbx_hashset_search(&vc_cache->items, &itemid)))
2696 {
2697 if (0 == vc_cache->low_memory)
2698 {
2699 zbx_vc_item_t new_item = {itemid, value_type};
2700
2701 if (NULL == (item = zbx_hashset_insert(&vc_cache->items, &new_item, sizeof(zbx_vc_item_t))))
2702 goto out;
2703 }
2704 else
2705 goto out;
2706 }
...
2695行目において、ハッシュテーブルにて該当するitemidを走査し、存在しない場合2701行目でハッシュテーブルに枠を作成しています。
以上から、Value Cacheに関する挙動は以下となることがわかります。(途中の説明はかなり端折ってます。)
- History CacheからDBへシンクを行うときに、ハッシュテーブルに枠が存在すれば値をストアする。
- トリガー評価時に値を参照する。このとき枠が存在しない場合に作成される。
件のログはどこで出力されている?
次に件のログがどこで出力されているかを見ていきます。
件のログは、vc_warn_low_memory()から出力されています。
54 #define ZBX_VC_LOW_MEMORY_WARNING_PERIOD (5 * SEC_PER_MIN)
...
749 static void vc_warn_low_memory()
750 {
751 int now;
752
753 now = ZBX_VC_TIME();
754
755 if (now - vc_cache->last_warning_time > ZBX_VC_LOW_MEMORY_WARNING_PERIOD)
756 {
757 vc_cache->last_warning_time = now;
758
759 zabbix_log(LOG_LEVEL_WARNING, "value cache is fully used: please increase ValueCacheSize"
760 " configuration parameter");
761 }
762 }
お客様の報告のとおり、約5分おきに出力されるように実装されています。
次に、vc_warn_low_memory()が呼び出されている箇所を見てきます。ソースコードを見ていくと、以下となります。
- vc_release_space()
- zbx_vc_get_value_range()
- zbx_vc_get_value()
zbx_vc_get_value_range()とzbx_vc_get_value()は同様な処理となっていますので、またzbx_vc_get_value()だけを掲載します。
2678 int zbx_vc_get_value(zbx_uint64_t itemid, int value_type, const zbx_timespec_t *ts, zbx_history_record_ t *value)
2679 {
...
2692 if (1 == vc_cache->low_memory)
2693 vc_warn_low_memory();
vc_cache->low_memoryの値が1のときだけコールされています。
次に、残りのvc_release_space()を見ます。
764 /******************************************************************************
765 * *
766 * Function: vc_release_space *
767 * *
768 * Purpose: frees space in cache to store the specified number of bytes by *
769 * dropping the least accessed items *
...
781 static void vc_release_space(zbx_vc_item_t *source_item, size_t space)
782 {
...
810 /* failed to free enough space by removing old items, entering low memory mode */
811 vc_cache->low_memory = 1;
812
813 vc_warn_low_memory();
...
あえてコメント部分も載せました。名前のとおりキャッシュをリリースするための関数で、キャッシュに充分の空きがない場合に、vc_cache->low_memoryに1をセットした後に、vc_warn_low_memory()をコールしています。ソースは省いていますが、その後に開放処理を行っています。
結果として、Value Cacheを参照したときに、メモリが枯渇したフラグ(vc_cache->low_memory)が立っていた場合(zbx_vc_get_value_range()とzbx_vc_get_value())と、キャッシュをリリースするときに充分に空きがないと判断された場合に、件のログが出力されます。通常運用で頻繁に出力される状況は、参照する場合となります。
vc_cache->low_memory はいつ0リセットされる?
ソースコード調査の結果、なんとvc_cache->low_memoryが一旦1にセットされると、0にリセットされることはありません。
なので、ハッシュテーブルに枠を作るときに、この値が1のときは作成されません。(zbx_vc_get_value()のソースを読み返してください。)
また、vc_release_space()等にてハッシュテーブルから枠が削除されることもあります。
つまり、一旦Value Cacheを使い切るようなことをすると、サーバを再起動しない限り、正しくValue Cacheは使われないことになります!
結論は?
結論としては、Value Cacheに関しては以下のことが言えます。
- ValueCacheSize = 0と設定して、Value Cacheを使用しない。(ただし、2.2.7 - 2.2.9 に関しては0に設定すると正常起動しなくなります。ZBX-2.2.9-2にて修正済みです。)
- 枯渇しないぐらい充分大きな値を設定する。(ただし、ソースコードを読んだかぎりでは、概算値を算出することはできません。制御できないログのキャッシュも行うからです。)
また、ZBXNEXT-2474では、この問題についても議論されており、trunkリポジトリでは修正されたコードがコミットされています。多分ですが、ZBXではこちらのコードをバックポートすると思います。