テストステ論

高テス協会会長が, テストステロンに関する情報をお届けします.

(dm-lc report) lc-detachのより良い書き方はないだろうか

lc-detachは, LVをキャッシュから切り離すコマンドである. dm-lcではここで, 「ダーティをすべてマイグレートしていないLVは切り離せない」ということにしている. 原理的には, ダーティが残ったままキャッシュから切り離しても適当にやる方法はあるだろうが, 実装が複雑になるし, 運用上, lc-detachを実行することはほぼないだろうなので, もっとも安全に切り離すようにしている. スクリプトは以下.

device.lock() # (1)
if not device.cache_id():
    device.unlock()
    sys.exit("cache not bound")

cache = tools.Cache(device.cache_id())
dirnode.write(cache.lc_node, "flush_current_buffer", 1) # (2)

interrupted = False # (3) 

while True:
    try:
        nr = device.nr_dirty_caches()
        while nr: # (4)
            if interrupted: # (3)
                device.unlock()
                sys.exit()

            print("could not detach the device. %d caches are still dirty remained." % (nr))
            time.sleep(1)

        print("no dirty cache remained in the device")

    except KeyboardInterrupt:
        interrupted = True # (3)
        continue

    finally: # (5)

        load_command = "echo 0 %d lc %d %s 0 | dmsetup reload %s" % \
                (device.size(), device_id, device.backing.path(), device.dm_name())

        os.system("dmsetup message %s 0 remove_sysfs" % (device.dm_name()))
        os.system(load_command)

        device.unlock()
        os.system("dmsetup message %s 0 add_sysfs" % (device.dm_name()))
        break
  • (1) lock/unlockは, suspend/resumeで実装されている. すなわち, クライアントからのI/Oを完全に遮断する. postsuspendでflushすることにした方が安全かなぁ?
  • (2) dm-lcのRAMバッファにたまっているダーティを強制的にフラッシュして, マイグレート対象とする.
  • (3) 全ダーティのマイグレートは時間が超かかるケースがある. 「あっ, やっぱdetachやめるわ」というケースのために, KeyInterruptを受け付ける. interruptedがtrueになると, ロックをといてループから抜ける.
  • (4) nr_dirty_caches()は, dm-lcのsysfsを見て, あるLVがキャッシュ上に保持したままのdirty数カウントを取得する. もしこれが残っている場合は, マイグレーションを継続する.
  • (5) 何もかも終わったらfinallyで, 通常処理を行う.

問題は2箇所ある.

  • (i) この制御構造は複雑すぎる. しかしPythonではこうするしかないようにも思う. 他にもっと良い制御構造があり得るなら, Twitterかあるいはプルリクかパッチで教えて欲しい.
  • (ii) (4)の部分はバグってる. nrは常に更新しなければならない. しかしそれはDRYに反する. nrを遅延計算し続ける方法はないだろうか. 例えば以下のように書けばnrを常に最新の値に遅延計算してくれるとか. 方法があるなら教えて欲しい.
lazy nr = device.nr_dirty_caches()
while nr:
  ...

(追記)
例えば,

nr = lambda : device.nr_dirty_caches()
while nr():
  ...

とか書けばうまく行くんですかね?例えば以下は, 実行ごとに違う値を返す. Haskellでいうと, IO IntとかIO Stringっぽい.

>>> f = lambda : os.system("date")
>>> f()
Sun Aug 18 13:15:25 JST 2013
0
>>> f()
Sun Aug 18 13:15:26 JST 2013
0
>>> f()
Sun Aug 18 13:15:29 JST 2013
0