macOSでファイルをたくさん開きたい阿部です。
PGroongaのCIで system call error: Too many open files
というエラーが発生しました。
開いているファイルが多すぎてエラーになったようです。
このエラーを回避してCIが成功するように対応したので、その内容を紹介します。 今回はmacOSのCIで生じたエラーなので、macOSでの回避方法の紹介です。
同じエラーに遭遇した方は参考にしてください!
いきなり結論
sudo launchctl limit maxfiles unlimited unlimited
を実行すると良いです。
これで開けるファイルの上限が unlimited
になります。
(ulimit
コマンドに馴染みがある方であれば、それのmacOS版と思ってください。)
以上で話したいことは終わったのですが、これだけだとさみしいので、 エラーが発生する原因やコマンドで何をやっているかなどをこのあと説明していきます。
エラーが発生する原因
ほとんどのOSではユーザが利用できるリソースが制限されています。 今回は開けるファイルの上限が制限されていました。
具体的にどのような設定になっているかは launchctl limit maxfiles
で確認できます。
エラーになったCI環境では次の結果でした。
$ launchctl limit maxfiles
maxfiles 256 unlimited
すでに unlimited
ではないか!
なんですが、unlimited
なのは「ハードリミット」の方です。
大事なのは 256
の「ソフトリミット」の方です。
「ソフトリミット」の方が今適用されている上限値です。
ですので、今の上限は 256
なのでそれを超えてファイルを開こうとすると
system call error: Too many open files
エラーになります。
「ハードリミット」はシステムの上限のイメージです。
ユーザは「ハードリミット」を超える値を「ソフトリミット」に設定できません。
今回のCI環境では「ソフトリミット」を unlimited
まで設定できるということです。
補足:
launchctl limit
を実行すると他の値も確認できます。
$ launchctl limit
cpu unlimited unlimited
filesize unlimited unlimited
data unlimited unlimited
stack 8372224 67092480
core 0 unlimited
rss unlimited unlimited
memlock unlimited unlimited
maxproc 1333 2000
maxfiles 256 unlimited
launchctl limit maxfiles unlimited unlimited
は何をしているのか
sudo launchctl limit maxfiles unlimited unlimited
は「ソフトリミット」と「ハードリミット」の値を
unlimited
に設定しています。
汎用的に表現すると sudo launchctl limit maxfiles "設定したいソフトリミットの値" "設定したいハードリミットの値"
ということです。
(「ハードリミット」の値はrootでないと変更できません。)
sudo launchctl limit maxfiles unlimited unlimited
を実行したあとに launchctl limit maxfiles
で確認すると「ソフトリミット」が
unlimited
になったことが確認できます。
$ launchctl limit maxfiles
maxfiles unlimited unlimited
この設定は一時的なものでサーバの再起動などで、設定がリセットされます。
補足: 永続的に設定する
このエラーに対応したプルリクエストでコメント をいただきましたが、ファイルを作成して設定することでマシンを再起動したあとも設定が適用されます。
/Library/LaunchDaemons/limit.maxfiles.plist
というファイルを以下のような内容で作成します。
設定値は適宜変更してください。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>limit.maxfiles</string>
<key>ProgramArguments</key>
<array>
<string>launchctl</string>
<string>limit</string>
<string>maxfiles</string>
<string>524288</string>
<string>2048576</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>ServiceIPC</key>
<false/>
<key>HardResourceLimits</key>
<dict>
<key>NumberOfFiles</key>
<integer>64000</integer>
</dict>
<key>SoftResourceLimits</key>
<dict>
<key>NumberOfFiles</key>
<integer>64000</integer>
</dict>
</dict>
</plist>
すぐに適用したい場合は以下のコマンドを実行します。
sudo launchctl load -w /Library/LaunchDaemons/limit.maxfiles.plist
このファイルを作成するとマシンを再起動しても設定が適用されます。
補足: デーモンの場合は再起動する
今回のエラーを出力したのはPostgreSQLでした。
そのようなデーモンの場合は sudo launchctl limit maxfiles unlimited unlimited
で設定を変更したあとに、
プロセスを再起動する必要があります。
補足: 気軽に実行しない
unlimited
に設定しているので、環境によっては注意して設定してください。
今回はCIなので気軽に実行して設定しましたが、普段遣いのマシンだったり、サーバだったりの場合は unlimited
になることで
悪影響が出る場合があります。
補足: そもそも launchctl
とは?
systemd
や systemctl
に馴染みのある方であれば、それのmacOS版という理解で良いです。
参考: Macの「ターミナル」でのlaunchdを使ったスクリプトの管理
まとめ
macOSでは sudo launchctl limit maxfiles unlimited unlimited
でファイルをたくさん開けるようになります!