Windowsファイル共有(SMB)で「遅延書き込みデータの紛失」によりファイルが破損する

はじめに

業務でJava製のデータベース的なアプリを扱っていて、Windows共有フォルダ上のファイルが破損するという事象に遭遇しました。以下はその時に出力されたイベントログ。

{遅延書き込みデータの紛失} ファイル xxxx のためのデータを一部保存できませんでした。データを損失しました。 このエラーはそのファイルのあるサーバーによって返されました。このファイルをどこか別の所に保存してください。

あれま orz.

その際の調査録です。

先に結論だけ

ちょっと長くなりそうなので、先に調査の結論を。

ファイル共有(SMB)では、ファイル破損が起きる可能性がある

…oplock failure occurs when database files are very large. … often results in a failed open due to a timeout on the second client, perhaps along with a message warning of possible database corruption!

ので、破損が許容できない環境ではoplockを無効化する(IO性能は劣化する)

…If you are having problems of this variety, you can turn off oplocks …

またアプリ側の対処としては、ファイル同期を適切なタイミングで実施する(IO性能は劣化する)

To work around this problem, developers writing database applications that access a network data store should flush file buffers at any time that represents delineation of a transaction…

という感じでした。

 

以下、調査録をつらつらかきます。

環境の諸情報

ざっくりの情報ですが、こんな感じの環境で発生しました。

  • IO負荷が高いアプリケーション(Java製)
  • データの保存先はWindowsファイル共有(SMB3.1)
  • アプリ稼働マシンおよびデータ保存先のOSはどちらもWindows10 Pro

調査に必要だった基礎知識

事象発生時のパケット

以下事象発生までの、SMBパケットの抜粋です。

1. CreateリクエストよりOplock: Leaseでファイルオープン(レスポンスは書かないですが正常レスポンス)

SMB2 (Server Message Block Protocol version 2)
    SMB2 Header
        ...
        Command: Create (5)
        ...
    Create Request (0x05)
        ...
        Oplock: Lease (0xff)
        ...
        ExtraInfo SMB2_CREATE_DURABLE_HANDLE_REQUEST_V2 SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST SMB2_CREATE_REQUEST_LEASE
            Offset: 0x000000d8
            Length: 156
            Chain Element: SMB2_CREATE_DURABLE_HANDLE_REQUEST_V2 "DH2Q"
            Chain Element: SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST "MxAc"
            Chain Element: SMB2_CREATE_REQUEST_LEASE "RqLs"

2. 事象発生直前のReadリクエストは正常に終了(レスポンスは書かないですが正常レスポンス)

SMB2 (Server Message Block Protocol version 2)
    SMB2 Header
        ...
        Command: Read (8)
        ...
    Read Request (0x08)

3. 事象が発生したWriteリクエスト

SMB2 (Server Message Block Protocol version 2)
    SMB2 Header
        ...
        Command: Write (9)
        ...
    Write Request (0x09)

4. 3のWriteリクエストに対するレスポンスは、STATUS_ACCESS_DENIEDで失敗

SMB2 (Server Message Block Protocol version 2)
    SMB2 Header
        ...
        NT Status: STATUS_ACCESS_DENIED (0xc0000022)
        Command: Write (9)
        ...
    Write Response (0x09)

STATUS_ACCESS_DENIEDが出た時間とイベントログで遅延書き込みデータの紛失が出た時間が一致していたため、ここが原因でファイル破損が起きていました。

なぜSTATUS_ACCESS_DENIEDが出るのか

oplock leaseが有効な環境では、短時間で発生したREAD/WRITEはまとめてサーバにリクエストされます。この時同一ファイルへのリクエストはファイルハンドルが共用されることがあります。

ACCESS_DENIEDがでた理由は、直前にREAD_ONLYで開いていたファイルハンドルをWRITE時にも利用していたことが原因でした。ただこのあたりはアプリレイヤの制御ではなく、OSで制御していることなので、OS側(SMBモジュール)の不具合のようです。

WindowsのSMB関連のバグたち

Windowsファイル共有(SMB)でファイル破損を防ぐには?

oplock leaseを無効化することが本事象の対策になります。

まとめ

こんな感じで遅延書き込みエラーにより共有ファイル上のファイルが破損しうるので、皆様気を付けてください。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA