VBScriptを「おもちゃ」で終わらせない:書き込み競合を制する「指数バックオフ」実装術
業務自動化の現場で、VBScriptは今なお現役だ。しかし、多くのエンジニアが「実行時エラー: 70(書き込み禁止)」や、同時アクセスによるログの欠損に頭を抱えている。
なぜ彼らは失敗するのか? それは「ファイルシステムを排他的なリソースとして捉えていない」からに他ならない。
今回は、FileSystemObject (FSO) の脆弱性を補完し、現代のマルチタスク環境でも通用する「リトライ付きログライター」の設計思想を伝授する。
—
1. なぜ「単純な書き込み」は崩壊するのか
VBScriptで `OpenTextFile` を実行した瞬間、OSはファイルハンドルを確保しようとする。もし別プロセスがそのファイルを掴んでいれば、即座にエラーが返る。
多くの初心者は、ここで `On Error Resume Next` を使い、エラーを無視して終わらせる。しかし、それは「ログの消失」を容認する行為だ。業務システムにおいて「ログが消える」ことは、バグそのものよりも罪深い。
解決の鍵:指数バックオフ (Exponential Backoff)
単なる「1秒待って再試行」のループは、高負荷時にさらなる競合を生む。「待機時間を毎回倍増させる」という指数バックオフのアルゴリズムを導入することで、プロセス間の衝突を確率的に激減させるのがプロの作法だ。
—
2. 堅牢なログライター・モジュール
以下のコードは、単なる関数ではない。排他制御の失敗を許容しないための「プロフェッショナルなテンプレート」だ。
‘ — ログ出力モジュール —
‘ 競合を回避し、ログの安全な追記を保証する
Function SafeLog(strFilePath, strMessage)
Dim fso, ts, i, retryCount, delay
Set fso = CreateObject(“Scripting.FileSystemObject”)
retryCount = 5 ‘ 最大リトライ回数
delay = 100 ‘ 初期待機時間(ミリ秒)
For i = 1 To retryCount
On Error Resume Next
‘ ForAppending(8)で開く。失敗したらエラーが発生
Set ts = fso.OpenTextFile(strFilePath, 8, True)
If Err.Number = 0 Then
ts.WriteLine Now & ” : ” & strMessage
ts.Close
SafeLog = True
Exit Function
Else
‘ 競合発生時はランダム要素を加えた指数バックオフ
Err.Clear
WScript.Sleep delay + (Rnd 50)
delay = delay 2
End If
On Error GoTo 0
Next
‘ リトライ上限に達した場合のハンドリング
WScript.Echo “Critical Error: ログ書き込み失敗 – ” & strFilePath
SafeLog = False
End Function
—
3. 実装の極意:ここが設計の分かれ道
① オブジェクトは最小単位で管理せよ
`FileSystemObject` をグローバルに使い回すのは避けろ。メモリリークや不正な状態の保持を防ぐため、ログ書き込みの関数内で生成し、終わったらスコープを外れるように設計する。VBScriptのガベージコレクションを信じすぎるな。
② 「排他制御」は最後の砦
本来、高度な並列処理が必要ならデータベースやSQL Server Expressなどを選定すべきだ。しかし、VBScriptを選択せざるを得ない現場では、この「リトライ機構」が唯一の防波堤となる。
③ ログの粒度と運用
ログファイルが巨大化すると、ファイルシステムのロック時間も長くなる。
- 日付ごとにファイルを分割する(`log_20231027.txt` 等)
- 1ファイルあたりの上限サイズを設け、ローテーションする
これらを実装することで、I/O負荷を分散させ、競合確率を根本から下げることができる。
—
4. 最後に:エンジニアとしての矜持
VBScriptは古いが、その挙動を深く理解し、泥臭い排他制御をコードに落とし込む作業は、モダンな言語を使う際にも極めて重要な「システム設計の基礎体力」となる。
コードをコピペして動かすだけでなく、「なぜ今、この待機時間が必要なのか」を思考すること。その問いの先に、あなたを「スクリプトを書く人」から「自動化エンジニア」へと進化させる道がある。
さあ、あなたの自動化ツールを、誰にも文句を言わせない堅牢なシステムへと磨き上げろ。
