朝、自動実行の記録を見たら、1本だけ「異常終了 / 終了コード=2」と書いてあった。
毎日ログオンの10分後に走る点検ジョブ(=要するに、自分のPCで毎日勝手に動く小さな作業)で、仕事は「バックアップとして残した古いファイルが、所定の日数を超えてその場に居座っていないか数える」だけ。前日は「0件」で終わっていた。止まったまま気づかないと、古いファイルが溜まり続けて、次に掃除するときに何を消してよいか分からなくなる。自分のPCで何かを自動で走らせている人なら、同じ形の「失敗」に出会うと思う。
終了コード(=要するに、作業の結果を表す番号。0なら成功)の「2」は、私の設計では「計測できなかった」という意味にしてある。数えられなかったのに「0件」と書くと、それは嘘になるからだ。だから異常終了のほうが正しい挙動ではある。ただ、何が計測できなかったのかは、記録を開いても分からなかった。
記録は2行で途切れていた
点検ジョブは自分の記録に、走査した件数、見つかった件数、結果、終了の印、と順に書いていく。その朝の記録は「走査: 2,738件(陽性対照を確認)」「該当14件、超過0件」の2行で途切れ、結果の行も終了の印も無かった。陽性対照(=要するに、必ず見つかるはずのものが本当に見つかるかを毎回確かめる仕掛け)も通っている。
ここで気づく。計測は終わっている。2,738件を数え、超過0件まで出している。失われたのは、そのあとに書くはずだった3行目以降だけ。
原因の切り分けはAIエージェント(=要するに、指示を渡すと自分で調べて報告してくれるAI)に任せた。渡した条件は「診断だけ。直さない」。返ってきた結論はこうだった。
- 記録の書き込みは「失敗したら0.4秒待って3回まで」の作りで、3回とも失敗すると、計測が成功していても「書けなかった行がある」という理由で終了コード2になる。
- 同じ条件で手動で動かし直すと、完走して終了コード0。CPUに負荷をかけて4回動かしても再現しない。
- 記録のファイルを別のプログラムが3.5秒ほど開きっぱなしにすると、朝と同じ終了コード2になり、記録の行が途中で欠けることを再現できた。
「計測の失敗」ではなく「記録のファイルの取り合いに負けた」だった。
握っていたのは誰か、は分からなかった
犯人は特定できていない。候補はあった。このフォルダはノートアプリ(Obsidian)で普段開いている場所で、Windowsのウイルス対策や検索の索引付けも常駐している。どれも「変更があったら開いて読む」種類のものだ。
ここで、直す前の確認(監査)を担当したAIが足した事実が効いた。Windows PowerShell 5.1の Add-Content(=要するに、ファイルの末尾に1行足す命令)は、相手が排他ロック(=要するに、他の誰にも触らせない形で開くこと)をかけていなくても、読み取りで開いているだけで失敗する。実測の結果は次のとおりだった。
- 相手が「読み取りのみ共有」で開いている間: Add-Content も、.NETの File.AppendAllText も、共有モードを指定した FileStream も、3つとも失敗。
- 相手が「読み書きとも共有してよい」と宣言して開いている間: Add-Content は失敗。File.AppendAllText と FileStream(書き込みを共有する指定)は成功。
- 相手がいない対照: 3つとも成功。
要するに、同じ「末尾に足す」でも命令の種類で結果が変わり、Add-Content は相手が気前よく共有していても負ける。この表を作ったのはAIで、私は結果と再現手順を読んで納得しただけ。自分の手では回していない。
だから犯人は「ロックをかけた誰か」である必要がなく、「読み取りで開いていた誰か」でよかった。読むだけのプログラムは、私のPCの中にも何本か動いていると思う(どれが握ったかは分からないままだ)。
直した3か所(と、直さなかった1か所)
修正案は4つ出た。採ったのは3つ。
- やり直しの回数と間隔を広げた。3回×0.4秒を、8回・間隔を0.4秒から2.8秒へ段階的に延ばす形に変えた。合計で約11秒まで待てる。3.5秒握られる試験では完走し、15秒握られる試験では諦める、ところまでAIに動かして確かめてもらった。
- 書けなかった行を別のファイルに退避する。諦めた行を、同じフォルダの退避ファイルに「元の行」と「失敗理由」つきで1行残す。朝の記録が途切れていても、何が書けなかったかが分かる。
- 終了コードを分けた。「計測できなかった」の2と、「計測は成功したが記録に書けなかった行がある」の4を別の番号にした。ジョブ一覧には今までどおり異常終了として出るが、番号を見れば意味が違うと分かる。
直さなかったのは、ジョブを起動する側で標準エラー(=要するに、プログラムが「困った」と書き出す別の出力)を保存する案。ジョブを起動する仕組みそのもののファイルを変える案なので、今回は見送って別の機会にした。
ひとつ、文案の段階で止められた表現がある。規則に「書けなかった行は退避ファイルに残る」と書いていた。しかし、記録と退避ファイルの両方を同時に握られると、どちらにも残らず標準エラーにだけ出て、ジョブ起動の仕組みの中ではそれも消える、という反例が試験で出た。だから文は「原則残る。退避にも書けなかった行は、再実行で確かめる」に直した。「残る」と断定して書くと、残らなかった朝に気づけなくなる。
「失敗」の印が付いたら、最初に見分けること
自動化のジョブに「失敗」の印が付いたとき、最初に見分けるのは「仕事ができなかった」のか「仕事はできたが報告が書けなかった」のか、だ。私のジョブはその2つを同じ番号で返していた。仕事は済んだのに日報だけ書けなかった人を、欠勤扱いにしていたようなものだった。
自分のジョブで確かめるなら、次の3つ。
- 記録を書く命令は、相手が読み取りで開いているだけで失敗する種類か(PowerShell 5.1の Add-Content はそうだった)。
- 書けなかったとき、何回・何秒待ってから諦めるか。その数字を自分で決めたか。
- 「計測できなかった」と「計測は成功したが記録に書けなかった」を、別の番号で返しているか。
自動化を持っていない人向けには、一言だけ。「失敗」の表示が出たら、慌てる前に、仕事そのものの結果が残っていないかを先に見る。私の場合、答えは2行目にあった。
直してから日が浅いので、効いたかどうかはまだ分からない。次に同じ番号が出たら、退避ファイルを開くところから始める。
本記事にアフィリエイトリンク・PR案件は含まれていません。

Comments