====== SambaとZFSで大量のファイルを扱う時はcase-sensitiveを最適化する ======
[[blog:2017:2017-08-04|同一フォルダに大量のファイルがあるとSambaが超遅くなる]]問題、自分なりの知見が得られたのでメモ。結果だけ知りたい人は最後までスクロールしてくだしあ。
===== おさらい =====
ことの発端は、数万個のファイルがあるフォルダをSambaのファイルサーバにコピーすると、速度が10kB/s前後まで低下する現象に見舞われた。速度はコピーが進むごとに低下し、比例してsmbdのCPU占有率が上がるというのが特徴。サーバ上で直接コピーすると何の問題もない。
調査を進めると、Sambaはファイル名の重複チェックのため、ファイル作成時に作成先フォルダ内の全ファイル名を検査することが分かった。この時に行われる、ファイル名の大文字/小文字変換と比較処理がボトルネックとなっているようだ。Windowsでは歴史的にファイル名の大文字/小文字を区別しない((ファイルシステム上は区別して保存されている。この差はAPIで吸収され傍目からはcase-preservingのように見える))が、UNIX系ではOS/ファイルシステム共に大文字/小文字を区別することが多い。このWindowsとUNIXの違いをSambaで吸収してやる必要があるわけだ。
プロファイルしたわけでもソースコードを見たわけでもないので確証はないが、Sambaのドキュメントに「ディレクトリに大量のファイルがある特殊なケースでは、case sensitiveをyesにせよ」(抄訳)と書かれており、ファイルコピーの進捗にあわせ指数関数的に速度が落ちる(CPU負荷が上がる)という挙動から、当たらずとも遠からずだと思う。
では''case sensitive = yes''にすれば万事解決かといえば、そう簡単な話ではない。
前述の通りWindowsはFS的にはcase-sensitiveないしcase-preservingだが、表面的にはcase-insensitiveとして振る舞う。この仕様に胡坐をかき、ファイルパスを内部的に大文字または小文字に変換して扱うアプリケーションが少なくない。例えば、本来のファイルパスは''C:\Data\FILE.dat''にもかかわらず、アプリケーション内部では''c:\data\file.dat''として扱うことが往々にある。(自分もそういう処理をつい書いちゃうんだけど(;'∀'))。
ローカルのファイルに対してなら、Windowsがよしなに取り計らってくれるので問題にはならない。
しかし、共有フォルダのアクセスに関しては、忖度することなくリクエストされたファイルパスをそのままサーバに渡しているようだ。ゆえにサーバ側でそのあたりが考慮されてないと、意図したファイルが見つからないという事になる。Sambaの''case sensitive''オプションは、まさにその辺の制御に関するオプションなのだ。
''case sensitive=yes''にするということは、''\\Server\Data\FILE.dat''と''\\Server\data\file.dat''は別のファイル扱いになるということで、手抜きアプリからすれば、case-insensitiveなら見えていたファイルが見つからなくなる。これでは使い物にならない((実際、某有名3Dモデリングソフトでアセットファイルが突然見えなくなるという現象に遭遇している))
===== ではどうするか =====
そこでZFSの''casesensitivity''プロパティの登場だ。
その名の通り、ファイル名の大文字/小文字の取り扱いに関するプロパティで、デフォルト値は''casesensitive''である。
これを''caseinsensitive''にしてやれば、大文字/小文字を区別しないようにファイルシステムが処理するようになる。"insensitive"とはなっているけど、挙動としては"preserving"のようだ。ちなみに、''mixed''というのもあるが、使いどころがよくわからない挙動なので指定しない方が無難(一応、Windowsを想定した挙動らしいんだけど…)。
残念ながら、というか性質を考えたら仕方ないけど、本プロパティはデータセット作成時にしか指定できない。既に問題が起きてしまっている場合は、''caseinsensitive''なデータセットを新たに作り、''casesensitive''の方からファイルコピーで移行するしかない。(zfs send/recvではプロパティが引き継がれるので意味がない。)
そのうえでSamba側の''case sensitive=yes''としてやれば、smbdの負荷問題は解決する。
Sambaがファイル作成時にファイル名を全舐めしてしているのは、対象フォルダに対する変更をSamba側で検知する術がないからだと思われる。Sambaがファイルを作成しようとしたまさにその時、同名のファイルがサーバのローカルで作られたりする可能性があるから、キャッシュではファイル名のユニーク性を担保できず、都度確認せざるを得ないのだろう。
一方、ファイルシステムレベルなら、ファイル名の一意性やアトミック性の保証が効率的に行えているだろうという目論見。
===== 結論 =====
結論としてはSambaとZFSで以下の設定を行うと幸せになれる。ZFSじゃない人はスマン…
* ZFS
* ファイル共有用にcasesensitivity=caseinsensitiveなFSを作る
zfs create -o casesnsitivity=caseinsensitive ztank/path/to/cifs
* Samba
* ファイル名の扱いをcase-sensitiveにする
case sensitive = yes
case preserve = yes
short preserve case = yes
(2021-05-17 追記)
上記設定について、以前はcase preserve, short preserve caseをnoとしていたが、yesの方が良さそうである。これらはファイル新規作成時のファイル名の大文字・小文字の取り扱いのオプションで、noだと大文字ないし小文字に変換されていまう(default caseの指定に因る。デフォルト値はlowerなので小文字になる。)
===== 参考サイト =====
* [[https://www.samba.org/samba/docs/current/man-html/smb.conf.5.html#NAMEMANGLINGSECT|smb.conf — The configuration file for the Samba suite]]のNAME MANGLINGセクション