亡霊と呼ばれた言語で「堅牢性」を実装する:FileSystemObjectの排他制御とリトライ戦略
VBScript。この、Microsoftが過去の遺産として放置した言語で、現代の並行処理という難題に挑む。
多くのエンジニアは、`Scripting.FileSystemObject`(以下FSO)を「手軽なファイル操作ツール」と見なしている。だが、それは間違いだ。FSOは、OSのI/Oを直接操作する薄いラッパーに過ぎない。複数のプロセスが同一ファイルに書き込みを試みる時、VBScriptの貧弱なエラーハンドリングは無防備にクラッシュし、貴重なログを握りつぶす。
今日は、レガシー環境で「絶対にログを取りこぼさない」ための、排他制御と指数バックオフを備えた実装について語ろう。
—
1. なぜFSOの「OpenTextFile」は裏切るのか
マルチプロセス環境で`ForAppending`モードを同時に呼び出すと、OSレベルでの共有違反(Error 70)や、ストリームの衝突が発生する。特にネットワークドライブや、高負荷時のローカルディスクでは顕著だ。
多くの者が「`On Error Resume Next`」でエラーを隠蔽し、そのまま処理を続行しようとする。これは、エンジニアとして最もやってはならない「死の隠蔽」だ。エラーは制御すべき対象であり、無視すべきではない。
2. 指数バックオフによる「待機戦略」
単純なループによるリトライは、リソースの競合を悪化させる(Thundering Herd Problem)。我々は、リトライするたびに待機時間を倍加させる「指数バックオフ(Exponential Backoff)」を採用する。これにより、負荷のピークをずらし、システム全体のI/Oスループットを保護する。
3. 実装:堅牢なログライター・モジュール
以下は、私が長年、基幹システムの保守で使用してきた排他制御付きのログ関数である。
‘——————————————————————————-
‘ プロセス間競合を回避する安全なログライター
‘ @param strPath ログファイルのフルパス
‘ @param strMsg 書き込むメッセージ
‘——————————————————————————-
Sub WriteLogSafe(strPath, strMsg)
Dim fso, ts, i, retryCount, waitTime
Dim success : success = False
Set fso = CreateObject(“Scripting.FileSystemObject”)
‘ 最大5回のリトライ、初期待機時間100ms
retryCount = 5
waitTime = 100
For i = 1 To retryCount
On Error Resume Next
‘ 8: ForAppending, False: Create if not exists, -2: TristateUseDefault
Set ts = fso.OpenTextFile(strPath, 8, True, -2)
If Err.Number = 0 Then
ts.WriteLine Now & vbTab & strMsg
ts.Close
success = True
Err.Clear
Exit For
Else
‘ 競合エラー(70)またはI/Oエラーを検知
Err.Clear
‘ 待機時間を指数的に増加 (100ms, 200ms, 400ms…)
WScript.Sleep waitTime
waitTime = waitTime 2
End If
On Error Goto 0
Next
‘ 明示的なオブジェクト解放
‘ 巨大なループ内でこれを怠るとVBScriptのメモリリークの温床となる
Set ts = Nothing
Set fso = Nothing
If Not success Then
‘ ここでイベントログに書き出すなど、最終的なフォールバックを実装すること
WScript.Echo “Critical Error: Failed to write log after retries.”
End If
End Sub
—
4. アーキテクトの視点:パフォーマンスとメモリの最適化
このコードには、単なるコード以上の「思想」が込められている。
- `Set Object = Nothing` の哲学:
VBScriptのガーベジコレクションを待つ余裕など、高負荷なシステムにはない。`Set ts = Nothing`を呼び出すことで、OSのファイルハンドルを即座に解放する。これを忘れると、ハンドルリークが蓄積し、やがてプロセスが再起動を要求する。
- `On Error`のスコープ制御:
`On Error Resume Next`を関数全体にかけないこと。必要な箇所だけを囲い込み、直後に`On Error Goto 0`で標準のトラップ状態に戻す。これが、予期せぬバグの連鎖を防ぐための鉄則だ。
- WScript.Sleepの活用:
`WScript.Sleep`はCPUサイクルを消費しない。OSのコンテキストスイッチを待つだけの賢い待機方法だ。これをループ内に置くことで、CPU負荷を最小限に抑えつつ、I/Oの静寂を待つことができる。
結論
レガシーな技術とは、古臭いのではない。「極限まで無駄を削ぎ落とし、OSの深層部と対話しなければならない」という制約が、かえってエンジニアの技術を研ぎ澄ませるのである。
モダンなフレームワークでログを書くのは誰にでもできる。しかし、FSOという脆弱なインターフェースを制御し、マルチプロセス環境でも一切の欠損なくログを吐き出し続けること。それこそが、現場を渡り歩いてきた者だけが体得できる「技術の真髄」だ。
次にVBScriptを触る時、そこに「ただ動くコード」ではなく、「止まらないアーキテクチャ」を実装してみせろ。健闘を祈る。
