fluent-plugin-dstatで取得した値が不正になる問題を解決

shun0102/fluent-plugin-dstat at v0.3.2で1秒周期にデータ収集していたら取得した値が明らかに不正になる時があったので、それを解決してPull Requestv0.3.3としてマージされた。

問題を確認した環境

  • td-agent 2.3.1 (fluentd 0.12.20)
  • fluent-plugin-dstat 0.3.2

問題の内容

dstatから取得した値がリソースなどに依らず全て不正な値となることがある。 常時ある程度の負荷(CPU負荷20%程度)がかかっている環境で1秒周期で収集した場合、発生頻度は5~15分に1度程度。

不正な値となる原因

fluent-plugin-dstat は 子プロセスとして dstat プロセスを生成し dstat/tmp/dstat.csv というファイルに収集したデータを書く。 fluent-plugin-dstat はそのファイルを0.5秒周期で監視し変化があった時、ファイルの内容を解析しJSON化する。

このように一時ファイル経由でのデータ授受をしているが、読みだす側のプロセス fluent-plugin-dstat と書き込む側のプロセス dstat 間で同期(排他)を取っていない。 つまり何らかの要因(主にOS)で書き込み側のバッファがflushされた時、読み込んだデータは不完全な(カラムが欠落した)行を含む可能性があるが、それが考慮されていなかった。

対策

ファイルから読み込んだデータが改行で終わっていなかったら、その行はJSON化せず内部に保持し、次に読み込んだ先頭行と連結するようにした。

これで不正値になることはなくなりかなり安定した。が、ほぼ一定の周期でデータが数点抜ける。

ソースを見ると fluent-plugin-dstat は100行読み込む度に /tmp/dstat.csv をクリアしていた。 同期なしに2つのプロセスが同一のファイルに書き込むので、データ抜けが起こるのも当然だった。

確かに誰かが一時ファイルをクリアしないとファイルが肥大化し続けてしまう。

そもそも dstat が標準出力に吐いてくれればファイルの肥大化など気にする必要もないのだが、解析に適したCSV形式の出力先に標準出力や標準エラー出力を選ぶオプションはなかった。

やりたいことは単なるデータの受け渡しで、しかも dstat はLinux限定のコマンドなので、一時ファイルとしてfifo(名前付きパイプ)を使うことにした。そうするとファイルのクリア処理自体が不要となる。

解決!と思ったら・・・

まだほぼ一定の周期(15分程度)でデータが数点抜ける・・・。調べるとほぼ一定の周期で dstat が死んで fluent-plugin-dstatdstat プロセスを再生成してた。

dstatが死ぬ原因と対策

fluent-plugin-dstatIO.popendstat プロセスを生成していたが、ここで作成されるパイプが満杯になると dstat が死ぬようだった。パイプのサイズは大体 65536 バイト。対策は簡単。パイプが満杯にならないよう標準出力と標準エラー出力を捨てるようにすればいい。

結果

これらを全て解決してPull Requestv0.3.3としてマージされた。