現在位置: ホーム / みらくるブログ / Value Cacheの憂鬱【MIRACLE ZBX 2.2】

Value Cacheの憂鬱【MIRACLE ZBX 2.2】

Zabbix 2.2から追加されたValue Cacheですが、いろいろとよくない振る舞いをしてくれます。とりあえずは、使用しないか過剰に大きく設定するかを選択せざるをえません。

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に関する挙動は以下となることがわかります。(途中の説明はかなり端折ってます。)

  1. History CacheからDBへシンクを行うときに、ハッシュテーブルに枠が存在すれば値をストアする。
  2. トリガー評価時に値を参照する。このとき枠が存在しない場合に作成される。

 

件のログはどこで出力されている?

次に件のログがどこで出力されているかを見ていきます。

件のログは、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()が呼び出されている箇所を見てきます。ソースコードを見ていくと、以下となります。

  1. vc_release_space()
  2. zbx_vc_get_value_range()
  3. 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に関しては以下のことが言えます。

  1. ValueCacheSize = 0と設定して、Value Cacheを使用しない。(ただし、2.2.7 - 2.2.9 に関しては0に設定すると正常起動しなくなります。ZBX-2.2.9-2にて修正済みです。)
  2. 枯渇しないぐらい充分大きな値を設定する。(ただし、ソースコードを読んだかぎりでは、概算値を算出することはできません。制御できないログのキャッシュも行うからです。)

 

また、ZBXNEXT-2474では、この問題についても議論されており、trunkリポジトリでは修正されたコードがコミットされています。多分ですが、ZBXではこちらのコードをバックポートすると思います。