厳密な順序での ZFS スナップショットの列挙
本エントリは Solaris Advent Calendar 2016 の 20 日目です。
動機
既存の ZFS なファイルシステムから、別プールに複製を作成する場合、zfs send
+ zfs receive
を使うのが一般的です。
しかし、そもそも zfs send
で指定するスナップショットは、どうやって特定すればよいでしょうか?
例えば、「最新」のスナップショットを使って zfs send
を実行する場合、手動での実行なら「目視判定」でも構わないでしょうが、スクリプト等で自動化するなら、何らかの新旧判定基準が必要です 。
新旧判定に属性情報を使用するにしても、zfs get all
出力を見る限りでは、使えそうなものが見当たりません。「作成日」を保持する creation
属性は、単位が「秒」なので、厳密な順序関係を判定する用途には不適切です(まぁ、実運用上は問題無いケースの方が多いと思いますが……)。
zfs list
でのスナップショット列挙は、名前等ではなく、作成順で整列されているように見えますので:
- スナップショット整列時の判定方法
- 整列保証されたスナップショット一覧を取得する方法
といったあたりを調査してみようと思います……というのが、Tokyo OpenSolaris 勉強会 2016.12 静岡合宿での私自身の目標でした。
結局合宿の間には調査し切れなかったのですが、折角なので調査結果を公開しておきます。
動作確認に使用したサンプルコードは Bitbucket 上で公開しています。本ブログエントリに関するサンプルは、libzfs_zfs_iter ディレクトリ配下にあります。
調査結果
libzfs には、指定 ZFS オブジェクト配下の要素を列挙するための、以下の API が存在します。
zfs_iter_snapshots() は、指定 ZFS オブジェクトのスナップショットを列挙するために、 zfs_iter_filesystems() はスナップショット以外の要素 (filesystem のみでなく volume も含む) を列挙するために使用されます。
zfs_iter_snapshots_sorted() を使用した場合、列挙されるスナップショットは、非公開属性 createtxg
の昇順での整列が保証されます。
createtxg
属性は、当該 ZFS プールにおける「ローカルな」通し番号と思われます。
createtxg
属性は zfs get all
では表示されませんが、 zfs get createtxg
のように明示的に指定した場合はアクセス可能です。
zfs send
における処理は:
zfs send -I snapshot
処理において、中間スナップショットを適切な順序で取得するために zfs_iter_snapshots_sorted() を使用- -i や -I でスナップショットが指定された場合に、
zfs send
対象として指定されたスナップショットの方が、新しいものであることの確認にcreatetxg
属性を使用
ちなみに、zfs_iter_filesystems() の整列保証バージョン API は提供されていません orz
zfs_iter_*() API の肝は以下を伴った ioctl() 呼び出しです。
- スナップショット列挙の場合は
ZFS_IOC_SNAPSHOT_LIST_NEXT
- それ以外の場合は
ZFS_IOC_DATASET_LIST_NEXT
いずれの場合の ioctl() 呼び出しでも、ZFS オブジェクトは未整列な順序で列挙されます。そのため、zfs_iter_snapshots_sorted() における整列保証は、ユーザ空間で実装されたものです。
zfs_iter_snapshots_sorted() におけるスナップショットの整列列挙は、以下の手順で実現されています。
- 与えられた ZFS オブジェクトの大小比較に
createtxg
属性を使用する関数を伴って、AVL ツリー領域を初期化 - 列挙された ZFS オブジェクトを上記 AVL ツリーに追加する関数を伴って、 zfs_iter_snapshots() を呼び出す
- この時点で、zfs_iter_snapshots() 実行中に AVL に追加された ZFS オブジェクトは、自動的に整列された状態になっている
- 整列済みスナップショットが保持された AVL ツリーを走査しつつ、zfs_iter_snapshots_sorted() に指定されたコールバック関数を呼び出す
個々の zfs_iter_*() API 実装の詳細に関しては、onnv や illumos ソースツリー中の usr/src/lib/libzfs/common/libzfs_iter.c を参照してみてください。