【テクニカル・上級編】【排他ロック競合の指数バックオフ回避】FileSystemObject書込みエラーを防止するジッター付き再試行ロジックの実装 – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク

VBScriptを掌握する:FileSystemObjectの排他ロックを「指数バックオフ」で制圧する

VBScriptを「過去の遺物」と呼ぶ者は、OSの深淵を知らぬ素人だ。Windowsという巨大なシステムにおいて、WSH(Windows Script Host)は今なお、軽量かつ強力な自動化の急先鋒であり続けている。

しかし、現場で多くのエンジニアが躓くのが「FileSystemObject (FSO) の排他ロック問題」だ。複数プロセスが同一のログファイルを叩く際、発生する「エラー70:書き込み権限がありません(Permission Denied)」。これを力任せのループや固定時間のWaitで解決しようとするのは、インフラを理解していない証拠である。

今日は、分散システムにおける「指数バックオフ(Exponential Backoff)」の概念をVBScriptに移植し、ジッター(Jitter)を加えて競合を物理的に回避する、極限の堅牢性を備えたコードを授ける。

なぜ「単純な再試行」ではいけないのか

単純な `Sleep 1000` を繰り返す再試行は、いわゆる「群れ現象(Thundering Herd Problem)」を誘発する。複数のプロセスが同時にロックされ、同時に1秒後に再試行し、また同時に衝突する。これではシステム負荷は一向に減らない。

解決策は明確だ。「再試行の間隔を指数関数的に伸ばし、そこにランダムな揺らぎ(ジッター)を加える」こと。これにより、プロセスは時間軸上で互いに散らばり、高確率で競合を回避できる。

実装:指数バックオフ・ジッター付きログ出力クラス

以下は、メモリリークを徹底的に排除し、リソース解放を厳密に管理した実装例だ。

‘ クラスとしてカプセル化し、オブジェクトの生存期間を制御する
Class FileLogger
Private fso
Private Sub Class_Initialize()
Set fso = CreateObject(“Scripting.FileSystemObject”)
End Sub

Private Sub Class_Terminate()
‘ オブジェクトの明示的解放:ガベージコレクタを待たず即時破棄する
Set fso = Nothing
End Sub

Public Sub WriteLog(filePath, message)
Dim retryCount, maxRetries, waitTime
retryCount = 0
maxRetries = 5

Do While retryCount <= maxRetries On Error Resume Next Dim ts ' ForAppending (8) でオープン Set ts = fso.OpenTextFile(filePath, 8, True) If Err.Number = 0 Then ts.WriteLine Now & " : " & message ts.Close Set ts = Nothing On Error Goto 0 Exit Sub Else ' エラー70 (Permission Denied) の場合のみリトライ If Err.Number = 70 Then Err.Clear ' 指数バックオフの計算: 2^retryCount 100ms ' ジッターの追加: 0~100msの乱数を加算 Randomize waitTime = (2 ^ retryCount 100) + Int(Rnd 100) WScript.Sleep waitTime retryCount = retryCount + 1 Else ' 致命的なエラーは即時終了 Err.Raise Err.Number, "FileLogger", Err.Description End If End If On Error Goto 0 Loop Err.Raise 70, "FileLogger", "最大リトライ回数を超過しました。" End Sub End Class ' --- 使用例 --- Dim logger Set logger = New FileLogger logger.WriteLog "C:\Logs\System.log", "プロセスが正常にタスクを完了しました。" Set logger = Nothing

コードの深層解説:なぜこのロジックなのか

1. 指数バックオフの設計: `2 ^ retryCount` を採用することで、試行回数が増えるごとに待機時間を倍増させている。これにより、競合が激しい環境下でのオーバーヘッドを最小化している。
2. ジッター(Jitter)の重要性: `Int(Rnd 100)` が肝だ。複数のプロセスが完全に同期して再試行を開始することを物理的に防ぐ。この「揺らぎ」こそが、分散システムにおける安定稼働の秘訣である。
3. オブジェクトのライフサイクル: VBScriptはCOMオブジェクトの管理が極めて重要だ。`Class_Terminate` で `fso` を明示的に `Nothing` にしているのは、長期間稼働するプロセスにおいてメモリ断片化を防ぐための定石である。
4. On Error Resume Next の局所化: 私はグローバルでのエラー抑制を推奨しない。必要な箇所だけに絞り、`Err.Clear` を徹底することで、予期せぬバグの隠蔽を回避している。

アーキテクトからの提言

VBScriptはレガシーだが、その設計思想はWindowsの深部に直結している。もし君のシステムが頻繁に「書き込みエラー」を吐くのであれば、それはコードのせいではなく、設計の甘さが招いた結果だ。

このロジックは、ログ書き込みだけでなく、データベース接続や外部APIの呼び出しといった、外部リソースへのアクセス全般に応用可能である。

「動けばいい」というコードを書く段階は卒業し、「なぜこのコードが壊れないのか」を説明できるエンジニアであれ。それが、この過酷なIT現場を生き抜くための唯一の道だ。

タイトルとURLをコピーしました