start

RAID-Z Expansionがマージされてた&ファイル名の最大長拡張PRが出来てた

もはや旧聞に属する話だが、2023年11月9日のRAID-Z Expansionのプルリクがついにmasterにマージされていた。コンセプトが発表されてから足掛け7年、実に長かった。なかなかインパクトのある機能だし仕方ないか。関係者の皆様お疲れ様でございました。

とはいったものの、実際にリリースされて使えるようになるのは、まだ先と思われる。リリース時期に関する最新情報は見つけられなかったのだけど、2021年時点のロードマップではOpenZFS 3.0で対応予定となっている。どんなに早くても2.3かなー?と思いつつ、どちらのバージョンのブランチもまだ存在しないのが現状。

加えてFreeBSDで使うには、ベースシステムに取り込まれるのを待たなきゃならんわけで、1年以上先なんじゃね?という気がしなくもない(まぁportsから入れればいいんだけども)。首を長くして待ちましょー

ついでにプルリク一覧を眺めていたら、ファイル名/ディレクトリ名の最大長を1023バイトに拡張する新しい機能フラグの実装があった⇒Longname: files/directories name upto 1023 bytes by tuxoko · Pull Request #15921 · openzfs/zfs

ZFSのファイル名/ディレクトリ名の最大長は255バイトとされているが、これはFILE_MAXだかPATH_MAX定数由来の制約らしい。これはUNIX系のファイルシステムにおける典型的な最大長で、通常はほとんど困らないサイズである。

ところが、ファイル名の文字コードとしてUTF-8を使うと話が変わってくる。UTF-8では1文字あたりのバイト数が1~4バイトと可変なので、ワーストケースで63文字しか格納できない。日本語の文字種はおおむね3バイトになるため、85文字で打ち止めとなる。

これでもFreeBSD、Linux単体で使う分にはさほど問題にならないと思うが、Windowsとファイル共有するとだいぶ困ったことになる。 というのもWindows (NTFS)の最大長はUTF-16で255文字1)なので、ZFSの255バイトでは全然足りんのです。 まぁ、日常的に85文字を超えるファイル名を使うことは、少なくとも自分は無いけれど、それでも1年に2~3回くらいは困る場面があるんだよね…

上記PRとは別に(?)最大長拡張の議論もなされており、開発のコアメンバーも認識はしている模様。互換性はどうなるんだとか、最大255文字で決め打ちしてるアプリで扱おうとした時にどうなるんだとか、色々気になる点はあるが、是非とも実現されてほしいところ。


1)
260文字という話もある。さらに近年は32768文字に拡張された

今度こそRAIDZ Expansionが来る?

RAIDZ ExpansionのPR作成の報を打ってから早2年、今度こそRAID-Z Expansionが来そうな雰囲気?である。

当時のPRは閉じられており、6月末にどういう訳か新しいPRに移行して作業が行われている。見てないけどOpenZFSリーダー会議で移行議論の概要が見られるようだ。

今回は着々とレビューとコミットが積み上げられている。RAIDZ Expansion自体は問題なく動いているようで、既に自前ビルドで使っている勇者もいるっぽい。今度こそマージされてほしい。

RAIDZプールに追加したスペシャルvdevは削除できない

OpenZFS 2.1.4時点で、冗長性レベルを問わずRAID-Zプールに追加したスペシャルvdevを削除することは出来ないようだ。恐らく、トップレベルvdev削除の制限に起因する仕様と思われる。

次のようなRAID-Z1とスペシャルvdevから成るプールがあるとする。

# zpool status ztank
  pool: ztank
 state: ONLINE
config:

        NAME        STATE     READ WRITE CKSUM
        zdata       ONLINE       0     0     0
          raidz1-0  ONLINE       0     0     0
            da1     ONLINE       0     0     0
            da2     ONLINE       0     0     0
            da3     ONLINE       0     0     0
        special
          mirror-1  ONLINE       0     0     0
            da4     ONLINE       0     0     0
            da5     ONLINE       0     0     0

errors: No known data errors

ここで、スペシャルvdevを削除しようとzpool removeすると、次のようにエラーとなる。

# zpool remove ztank mirror-1
cannot remove mirror-1: invalid config; all top-level vdevs must have the same sector size and not be raidz.

トップレベルvdevのmirror-1を一気に削除するのがマズいのかと思い、次のように構成メンバを個別に削除してってもダメ。

# zpool detach ztank da5
# zpool remove ztank da4
cannot remove .....

なお、RAIDZ以外の構成なら特に制限なく削除可能で、同じトップレベルvdevであるslogやL2ARCの場合は、RAIDZでも(以前から)削除可能である。

家鯖でスペシャルvdevを本格運用すべく色々準備してたけど、削除できないのはちょっと厳しいなぁ…VMであらかじめ実験しといて良かったぜ……slog/L2ARCは削除できるんだから、将来的にできるようになるのかなぁ………スペシャルvdevの場合、明示的に本体プールの方にデータを書き戻す必要があって難しいのかなぁ…………

当面はpL2ARCを使うとするかー。

Special vdevが消失したプールとzpool -Fオプション

プールのメタデータを丸っと引き受けるというZFSのSpecial vdevの特性から、対応する物理デバイスの故障などでSpecial vdevが死ぬと、プールそのものが使えなくなりそうってのは容易に想像ができる。

実際どうなるか仮想マシンベースで確認してみると、やはり使えなくなった。それもzpool listの結果にプール自体が出てこなくなるという、割と重篤な扱い。プール名を指定 or プール探索でインポートしようとすると、以下のようになってインポートできない。

# zpool import -a -N
cannot import 'ztest': I/O error
        Destroy and re-create the pool from
        a backup source.

存在しないプールのインポートではcannot import 'znotexists': no such pool availableって感じなので、明らかに扱いが違う。

Special vdevが消失したプールの復旧は基本的に無理っぽい感じ。

一応man zpool-importを見てみると、(いつの間にか)プール回復に関するオプション-F, -X, -Tが追加されていた。それぞれの効果を抄訳してみた。

-F インポート不可能なプールのための回復モード。最後のわずかなトランザクションを破棄することで、プールがインポート可能状態への復帰を試みます。このオプションを使うことで、損傷を受けたすべてのプールが回復するとは限りません。成功した場合、破棄されたトランザクションに関連するデータは、回復不能なほどに失われます。プールがインポート可能またはインポート済みの場合、このオプションは無視されます。
-n 回復オプション(-F)と共に使用します。インポート不可能なプールが再びインポート可能になるかどうかを判定しますが、実際にプール回復は行いません。プール回復モードの詳細は、上記の-Fオプションをご覧ください。
-X 回復オプション(-F)と共に使用します。有効なtxgを見つけるための非常手段を取るか否かを指定します。これは、もはや一貫性が保証されていないtxgへ、プールがロールバックされることを許可します。矛盾したtxgでインポートされたプールは修復不能なチェックサムエラーを含むかもしれません。プール回復モードの詳細は、上記の-Fオプションをご覧ください。警告:このオプションはプールの健全性に対し極めて危険な可能性があり、最終手段として用いるべきです。
-T ロールバックに使用するtxgを指定します。暗黙的に-FXオプションを含みます。プール回復モードの詳細は、上記の-Xオプションをご覧ください。警告:このオプションはプールの健全性に対し極めて危険な可能性があり、最終手段として用いるべきです。

-F < -X < -Tの順で強力(危険)になる雰囲気。で、それぞれを指定して、先のSpecial vdevが無くなったプールのインポートを試みたのが以下。

# zpool import -F ztest
cannot import 'ztest': I/O error
        Destroy and re-create the pool from
        a backup source.

# zpool import -FX ztest
cannot import 'ztest': one or more devices is currently unavailable

# zpool import -T ztest
invalid txg value
usage:
        import [-d dir] [-D]
        import [-o mntopts] [-o property=value] ...
            [-d dir | -c cachefile] [-D] [-l] [-f] [-m] [-N] [-R root] [-F [-n]] -a
        import [-o mntopts] [-o property=value] ...
            [-d dir | -c cachefile] [-D] [-l] [-f] [-m] [-N] [-R root] [-F [-n]]
            [--rewind-to-checkpoint] <pool | id> [newpool]

-Tはtxgを指定してやらないとダメな予感。usageにもmanにもそれらしいことは書いてないんだけど…実際にどんな値を指定したらいいのか皆目見当もつかない。

その後、Special vdev用の仮想ディスクを戻してみると、問題なくプールのインポートができた。ただし自動インポートはされず、手動で行う必要があるようだ。(上記の-Fとかでプールを操作したためかもしれないが未確認。)scrubで健全性に問題がないことも確認。

そんなわけでSpecial vdevの冗長性には十分気を付ける必要がありそうだ。

ZFSのSpecial vdevを試してみる

階層化ストレージのZFS版ともいえるSpecial vdevとSpecial Allocation Classについて、1年程前に当サイトでも解説した。いつか試そうと思いつつ延び延びになっていたが、いよいよ導入の機運が高まってきたので簡単にテストした。

まずはコマンドの確認から。以下、da0p1をノーマルvdev(プール本体のストレージ)、da6p1をスペシャルvdevとする。

スペシャルvdev付きプールを作る。vdevタイプとしてspecialを指定し、その後にスペシャルvdevに割り当てるデバイスを指定する。

# zpool create -O atime=off ztest da0p1 special da6p1
$ zpool status ztest
  pool: ztest
 state: ONLINE
config:

        NAME        STATE     READ WRITE CKSUM
        ztest       ONLINE       0     0     0
          da0p1     ONLINE       0     0     0
        special
          da6p1     ONLINE       0     0     0

既存のプールにスペシャルvdevを追加する場合はzpool addで同様に指定する。

# zpool add ztest special da6p1

スペシャルvdevの削除。これはL2ARCやslogの削除なんかと一緒。

# sudo zpool remove ztest da6p1

OpenZFS 2.1.4の時点において、RAIDZプールに追加したスペシャルvdevは削除できないので注意!!(トップレベルvdev削除の制限事項)。

スペシャルvdevデバイスの増減(zpool attach/detach)は可能だが、いざzpool removeしようとするとinvalid config; all top-level vdevs must have the same sector size and not be raidz.エラーとなり削除できない。

slogやL2ARCは削除できるのにどうして……

ただし、remove後にスペシャルvdevからノーマルvdevへ、データの退避が行われる。

$ zpool status ztest
  pool: ztest
 state: ONLINE
remove: Evacuation of /dev/da6p1 in progress since Wed Feb 23 16:50:17 2022
    2.77G copied out of 13.5G at 142M/s, 20.54% done, 0h1m to go
config:

        NAME        STATE     READ WRITE CKSUM
        ztest       ONLINE       0     0     0
          da0p1     ONLINE       0     0     0
        special
          da6p1     ONLINE       0     0     0

$ zpool iostat -v ztest
              capacity     operations     bandwidth
pool        alloc   free   read  write   read  write
----------  -----  -----  -----  -----  -----  -----
ztest       14.4G  9.13T     16     68   215M   220M
  da0p1      932M  9.09T      0     68      0   220M
special         -      -      -      -      -      -
  da6p1     13.5G  36.0G     16      0   215M      0
----------  -----  -----  -----  -----  -----  -----

退避が完了するとプールからデバイスが除去される。

$ zpool status ztest
  pool: ztest
 state: ONLINE
remove: Removal of vdev 1 copied 13.5G in 0h1m, completed on Wed Feb 23 16:51:31 2022
    27.4K memory used for removed device mappings
config:

        NAME          STATE     READ WRITE CKSUM
        ztest         ONLINE       0     0     0
          da0p1       ONLINE       0     0     0

スペシャルvdevは標準でメタデータのみを格納する設定となっている。小ブロックデータの格納を有効にするには、ファイルシステム毎のプロパティspecial_small_blocksを0以外の2の冪数に設定する。

# zfs set special_small_blocks=64k ztest/R/pictures
$ zfs get -r special_small_blocks ztest
NAME              PROPERTY              VALUE                 SOURCE
ztest             special_small_blocks  0                     default
ztest/R           special_small_blocks  0                     default
ztest/R/pictures  special_small_blocks  64K                   local

スペシャルvdevが使われてるかどうかはzpool iostat -vで確認できる。書き込み中に見てみると、しっかりスペシャルvdevも使われていることがわかる。

              capacity     operations     bandwidth
pool        alloc   free   read  write   read  write
----------  -----  -----  -----  -----  -----  -----
ztest       11.7G  9.13T      0  1.36K      0   213M
  da0p1     11.4G  9.08T      0    215      0   207M
special         -      -      -      -      -      -
  da6p1      277M  49.2G      0  1.15K      0  6.22M
----------  -----  -----  -----  -----  -----  -----

スペシャルvdevの有無、special_small_blocksのサイズ、レコードサイズの違いで簡単にテストを行う。

  • テストデータ
    • 72208ファイル、10.3GiB(平均ファイルサイズ:150KiB)
    • Special vdevの効果が出やすいであろう、大量の小サイズの画像ファイル群
  • テストマシン
    • OS: FreeBSD 13.0-RELEASE-p6 on Proxmox VE 7.1
    • CPU: 4 vCPU (Xeon E5-2680v4. オーバーコミットなし)
    • RAM: 32GB
    • HDD:
      • コピー元: 単体 (仮想ディスク50GB/実態はU.2 SSD)
      • コピー先: 単体 (10TB 7200RPM SATA HDD×1)

コピー先のプールのスペシャルvdevの有無によって、以下の項目を測定する。

  • コピー元→コピー先へのファイルコピー所要時間(rsync -aX src dst/
  • コピー先でファイルの全走査にかかる時間(find dst > /dev/null
  • コピー先でファイルの全読込にかかる時間(find dst -print0 | xargs -0 cat

ARCの影響を避けるため、プールは都度作成し、rsync後はマシンを再起動する。その後、ファイル走査→全読込の順で実行する。

仮想マシン上での実行のため、結果にはノイズが多く含まれていることに注意。

recsize special_small_blocks コピー時間(秒) ファイル走査時間(秒) ファイル読込時間(秒) スペシャルvdev使用量(MiB) 備考
128k - 284 17.8 539 - Special vdevなし
0 281 2.5 493 319 メタデータのみSpecial vdevを利用
4k - - - 320 sdev容量のみ測定
8k - - - 321
16k - - - 322
32k - - - 336
64k 279 2.6 433 684
128k 280 2.8 203 13824 全データがSpecial vdevに行く
1M - 280 17.6 400 - Special vdevなし
0 285 2.5 358 34
4k - - - 35 sdev容量のみ測定
8k - - - 36
16k - - - 37
32k - - - 52
64k 274 2.7 352 400
128k 276 2.8 286 3102

※special_small_blocks=4k~32kは後から測定したため、スペシャルvdevの容量のみ。グラフには加えていない。1MBずつ増えてて本当かよ?と思ったが、再度試しても同じだったので間違ってるわけではなさそう。

スペシャルvdevの有無でファイルコピー(書き込み。赤線)時間に有意な差は見られなかった。ただし、これはコピー元の読み込みで律速してる可能性が否定できない。コピー先の書き込み状況をiostatを眺めてみると間欠動作となっていた。

ファイルの全走査はスペシャルvdevがあると劇的に高速化されるようだ。iostatを見てみると、スペシャルvdevで読み込み処理が走っており、SSD上のメタデータが使わているものと思われる。期待通りの挙動ですな。

ファイルの全読込も、スペシャルvdevに保存されているデータ量に応じて短縮されており、こちらも想定通り。recsize=128kでspecial_small_blocks=128kとすると、全データがスペシャルvdevに保存されるというのも期待通りの結果だった(special_small_blocksで指定されたサイズ以下のレコードのデータがスペシャルvdevに保存される仕様。)iostatを見てみると、見事に全てスペシャルvdevに書き込まれていることが分かる。

              capacity     operations     bandwidth
pool        alloc   free   read  write   read  write
----------  -----  -----  -----  -----  -----  -----
ztest       13.2G  9.13T      0  2.21K      0   236M
  da0p1         0  9.09T      0      3      0  15.2K
special         -      -      -      -      -      -
  da6p1     13.2G  36.3G      0  2.20K      0   235M
----------  -----  -----  -----  -----  -----  -----

少し意外だったのは、special_small_blocksが0、すなわちメタデータのみをスペシャルvdevに保存した場合でも、ファイル読込性能が向上したという点。今回は小さな大量のファイルが対象だったため、読込み処理におけるメタデータの処理割合が多かったのが要因だろう。

また、スペシャルvdevと直接は関係ないが、レコードサイズでメタデータサイズが大きく変化するというのは新たな発見だった。全くの推測だが、おそらくレコードごとに生成されるチェックサムの総量が影響しているのだろう。10.3GiBを128kiBレコードで割ると約84000レコードで、それぞれにfletcher4(4バイト)のチェックサムが付くと、合計330MiBとなる。同様に1MiBレコードでは41MiBとなり、これらはメタデータのみをスペシャルvdevに保存した際の容量とおおむね一致する。

さらに1MiBレコードではファイル読込が明らかに速くなっており、圧縮率の観点等も考慮すると積極的に128KiB以上のレコードサイズを使っていくのが良さそう。

special_small_blocksの設定をどうするかは悩ましいところ。扱うデータの種類やワークフローはもとより、TXG関連の設定やその時々の負荷量などで、書き込みがスペシャルvdev行きとなるかどうかが変わってくると思われ、見積もるのが難しい。今回は64k/128kの2パターンしか見なかったが、運用においては、より小さな設定値も検討に値するだろう。むしろ、とりあえず4kあたりから始めて様子を見るのがいいのかもしれない。 → 気になったので4k~32kを追試したけど、32k以下は殆ど効果がなさそう。スペシャルvdevの運用としては、容量が見積もりやすいメタデータのみとするか、ある程度余裕を持たせて64kで始めるの2択になるかも。

メタデータだけでもスペシャルvdevの効果は期待できそうなので、SSDに余裕があるならL2ARCよりも優先的に割り当てて良さそうに思う。

  • start.txt
  • 最終更新: 2022-07-27 15:26
  • by Decomo