サーバを運用していると時々全然メモリが余っているはずなのにスワップが起きてしまうことがあります。この場合よくあるケースは非常に大きなログファイルがあってそのログファイルのために大量のページキャッシュが利用されていることです。
ログファイルなんて滅多に読まないので、そんなのにメモリ使うぐらいだったらもっと別のアプリケーションにちゃんと割り当ててくれよと思うのですが、なかなかうまくいかないものです。
そこで効果的なのがposix_fadivse
をラップして指定したファイルのページキャッシュを解放するツールです。
私の場合、今まではnocacheというツールをちょこっと改造して使っていましたが、最近もうちょっと便利な風に改造したいと思うようになってきました。例えば、
- 指定したディレクトリ内すべてのファイルのページキャッシュの確認・解放がしたい
- いちいちfindと組み合わせたりcronの設定書きたくない
といった要件です。nocacheはCで書かれてることもあってがっつり改造するには不向きです。いや、Cは書けるんだけど最近はもうこういうツールはGoでいいよなぁと思ってるだけなんですが。(もちろんposix_fadvise
を呼び出す部分はFFI(Goの場合はcgo))
cachectl
というわけで書きました。
名前は若干ネタですが、やってることはいたってまじめです。(ISUCON4でCache-Controlに気付かなかった皆さんお元気ですか?僕も気付きませんでした)
ビルド手順はこんな感じです。ビルドが成功するとbinディレクトリにcachectl
とcachectld
というファイルが出来ます。
git clone https://github.com/cubicdaiya/cachectl.git
cd cachectl
make bundle
make
cachectl
は-f
オプションにファイルパスを与えるとそのファイルの内容がページキャッシュにのっているかチェックできます。
$ bin/cachectl -f conf/cachectld.toml
conf/cachectld.toml 's pages in cache: 0/1 (0.0%) [filesize=0.2K, pagesize=4K]
$
ディレクトリも指定できます。
$ bin/cachectl -f cachectl
bin/cachectl -f cachectl
cachectl/activepages.go 's pages in cache: 0/1 (0.0%) [filesize=1.2K, pagesize=4K]
cachectl/conf.go 's pages in cache: 0/1 (0.0%) [filesize=0.8K, pagesize=4K]
cachectl/const.go 's pages in cache: 0/1 (0.0%) [filesize=0.1K, pagesize=4K]
cachectl/purge.go 's pages in cache: 0/1 (0.0%) [filesize=1.1K, pagesize=4K]
cachectl/stat.go 's pages in cache: 0/1 (0.0%) [filesize=0.7K, pagesize=4K]
cachectl/version.go 's pages in cache: 0/1 (0.0%) [filesize=0.2K, pagesize=4K]
$
ページキャッシュを消す場合は-op purge
を指定します。(↓の例だと元々ページキャッシュにのってないですが)
$ bin/cachectl -f conf/cachectld.toml -op purge
Before purging conf/cachectld.toml 's page cache
conf/cachectld.toml 's pages in cache: 0/1 (0.0%) [filesize=0.2K, pagesize=4K]
After purging conf/cachectld.toml 's page cache
conf/cachectld.toml 's pages in cache: 0/1 (0.0%) [filesize=0.2K, pagesize=4K]
$
また、ファイルの末尾だけページキャッシュにのせたままにしたい場合は-r
で割合を指定できます。例えばページキャッシュをファイルの末尾1割を除いて削除する場合は0.9
を指定すればよいわけです。
cachectldで定期的にページキャッシュを削除
cachectld
は定期的に指定ファイルのページキャッシュを削除するデーモンです。まずはこんな感じのTOMLファイルを用意します。
[[targets]]
path = "/vagrant/cachectl.go"
purge_interval = 30
[[targets]]
path = "/vagrant/cachectld.go"
purge_interval = 20
[[targets]]
path = "/vagrant/cachectl"
purge_interval = 5
filter = "\\.go$"
rate = 0.9
purge_interval
がキャッシュを削除する間隔(秒)です。あとはこれを起動しておくだけで定期的にページキャッシュを削除してくれます。
$ bin/cachectl -c conf/cachectld.toml
とはいえログローテートのタイミングでログのページキャッシュ消されたらそれはそれで困るのでこのあたりはなんかうまい方法考えたい。(disabled_hour_range
みたいなパラメータ追加するか)