彷徨えるフジワラ

年がら年中さまよってます

厳密な順序での 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() におけるスナップショットの整列列挙は、以下の手順で実現されています。

  1. 与えられた ZFS オブジェクトの大小比較に createtxg 属性を使用する関数を伴って、AVL ツリー領域を初期化
  2. 列挙された ZFS オブジェクトを上記 AVL ツリーに追加する関数を伴って、 zfs_iter_snapshots() を呼び出す
  3. この時点で、zfs_iter_snapshots() 実行中に AVL に追加された ZFS オブジェクトは、自動的に整列された状態になっている
  4. 整列済みスナップショットが保持された AVL ツリーを走査しつつ、zfs_iter_snapshots_sorted() に指定されたコールバック関数を呼び出す

個々の zfs_iter_*() API 実装の詳細に関しては、onnv や illumos ソースツリー中の usr/src/lib/libzfs/common/libzfs_iter.c を参照してみてください。