munin asyncd でプラグインの実行タイミング制御

覚書
 4/26 update SpoolReaderとUpdateWorkerのパッチをとりあえず追加

■munin-asyncd で plugin 毎に収集タイミングを制御するには
plugin_rates でプラグイン毎の実行タイミングを制御できる
 plugin 実行間隔(秒)
設定しないプラグインはデフォルトの5分おきで収集されるが、載せておくほうが無難か

■munin-async の実行
コマンド制限タイプssh鍵登録
実運用時はfromも最低付ける予定
 command="/~/lib/munin-async --spoolfetch" ssh-rsa AAAAB

■問題点1:実行間隔を5分以上にすると、munin-async 実行時に値の更新のなかったプラグインはグラフへのリンクが切れる
→RRD更新やHTML生成が、state-~.storable を基点にしていて、「値を返してきたもの」を前提として生成するような形になっているため
 →SpoolReader.pm をいじって、更新が無かった場合でも最後の値を返すように修正
 →munin-update時にrrdtool update部でエラーになる
  →UpdateWorker.pm で前回と同値の場合は~.storageとRRDファイルを更新スキップするよう修正

■問題点2:値の更新間隔が600秒以上のプラグインは、rrdファイルのvalueがNaNとなり、グラフに描画されない
→Muninのrrdファイル生成時のデフォルトが step = 300,minimal_heartbeat = 600 のため
 →rrdtool tune で minimal_heartbeat を plugin_rates で設定した値*2くらいに変更するとvalueが乗り微妙だけどグラフが描画される
 →コマンドイメージ rrdtool tune ~.rrd 42:7200
 →UpdateWorkerでminimal_heartbeatの変更も対応できるよう修正するのがよいのだけど、修正量が多くなるのでちょっと見送り。
 →rrdtool でとりあえず変更でしのぐ(・・・にしても変更するのが楽になるようにシェル作るか?)

■SpoolReader.pmのパッチ

--- SpoolReader.pm.org	2013-04-16 16:46:49.000000000 +0900
+++ SpoolReader.pm	2013-04-24 12:08:47.000000000 +0900
@@ -113,14 +113,21 @@
             or die "Unable to open spool file: $!";
 
         my $epoch;
+        my $position = 0;
 
         # wind through to the start of the first results after $timestamp
         while (<$fh>) {
             ($epoch) = m/^timestamp (\d+)/ or next;
+            $position = tell($fh) - length($_);
             logger("Timestamp: $epoch") if $config->{DEBUG};
             last if ($epoch > $timestamp);
         }
 
+        # for last timestamp return
+        if ( eof $fh && $position != 0 ) {
+           seek($fh , $position , 0);
+        }
+
         if (eof $fh) {
             logger("Epoch $timestamp not found in spool file for '$service'")
                 if $config->{DEBUG};

■UpdateWorker.pmのパッチ(コメントは超適当)

--- UpdateWorker.pm.org	2013-04-09 18:13:55.000000000 +0900
+++ UpdateWorker.pm	2013-04-24 12:06:21.000000000 +0900
@@ -898,34 +898,40 @@
 	$current_updated_value = $value;
     }
 
-    DEBUG "[DEBUG] Updating $rrd_file with @update_rrd_data";
-    if ($ENV{RRDCACHED_ADDRESS} && (scalar @update_rrd_data > 32) ) {
-        # RRDCACHED only takes about 4K worth of commands. If the commands is
-        # too large, we have to break it in smaller calls.
-        #
-        # Note that 32 is just an arbitrary choosed number. It might be tweaked.
-        #
-        # For simplicity we only call it with 1 update each time, as RRDCACHED
-        # will buffer for us as suggested on the rrd mailing-list.
-        # https://lists.oetiker.ch/pipermail/rrd-users/2011-October/018196.html
-        for my $update_rrd_data (@update_rrd_data) {
-            RRDs::update($rrd_file, $update_rrd_data);
-            # Break on error.
-            last if RRDs::error;
+    # 
+    # RRDs::update run "$current_updated_timestamp > $previous_updated_timestamp" only.
+    # If "$current_updated_timestamp <= $previous_updated_timestamp" case should retrun $previous_updated_timestamp ?
+    if ($current_updated_timestamp > $previous_updated_timestamp) {
+        DEBUG "[DEBUG] Updating $rrd_file with @update_rrd_data";
+        if ($ENV{RRDCACHED_ADDRESS} && (scalar @update_rrd_data > 32) ) {
+            # RRDCACHED only takes about 4K worth of commands. If the commands is
+            # too large, we have to break it in smaller calls.
+            #
+            # Note that 32 is just an arbitrary choosed number. It might be tweaked.
+            #
+            # For simplicity we only call it with 1 update each time, as RRDCACHED
+            # will buffer for us as suggested on the rrd mailing-list.
+            # https://lists.oetiker.ch/pipermail/rrd-users/2011-October/018196.html
+            for my $update_rrd_data (@update_rrd_data) {
+                RRDs::update($rrd_file, $update_rrd_data);
+                # Break on error.
+                last if RRDs::error;
+            }
+        } else {
+            RRDs::update($rrd_file, @update_rrd_data);
         }
-    } else {
-        RRDs::update($rrd_file, @update_rrd_data);
-    }
 
-    if (my $ERROR = RRDs::error) {
-        #confess Dumper @_;
-        ERROR "[ERROR] In RRD: Error updating $rrd_file: $ERROR";
+        if (my $ERROR = RRDs::error) {
+            #confess Dumper @_;
+            ERROR "[ERROR] In RRD: Error updating $rrd_file: $ERROR";
+         }
+
+        # Stores the previous and the current value in the state db to avoid having to do an RRD lookup if needed
+        $self->{state}{value}{"$rrd_file:42"}{current} = [ $current_updated_timestamp, $current_updated_value ]; 
+        $self->{state}{value}{"$rrd_file:42"}{previous} = [ $previous_updated_timestamp, $previous_updated_value ]; 
     }
 
-    # Stores the previous and the current value in the state db to avoid having to do an RRD lookup if needed
-    $self->{state}{value}{"$rrd_file:42"}{current} = [ $current_updated_timestamp, $current_updated_value ]; 
-    $self->{state}{value}{"$rrd_file:42"}{previous} = [ $previous_updated_timestamp, $previous_updated_value ]; 
-
+    DEBUG "[DEBUG] return timestamp is $current_updated_timestamp. previous timestamp is $previous_updated_timestamp.";
     return $current_updated_timestamp;
 }