【実務・中級編】【上級プロフェッショナル向け】ネットワーク切断時の「トランザクション保存」:ローカルへの一時書き出しと復旧同期システム – AutoCAD VBA解析バイブル

スポンサーリンク
スポンサーリンク

【極限の設計】ネットワーク切断を「想定内」に変える:AutoCAD VBAにおけるShadow-Save(一時退避)同期システムの実装

業務自動化の世界において、最も恐ろしいのは「コードが動かないこと」ではない。「コードが動いている最中に、データが死ぬこと」だ。

特に、大規模な設計プロジェクトにおいて、DWGファイルがネットワーク上のファイルサーバー(UNCパス)に置かれているケースは多い。ここで、開発者が最も陥りやすい罠がある。それは、`ThisDrawing.Save` や `SaveAs` を単に呼び出すだけの、あまりに無防備な実装だ。

ネットワークの一時的な瞬断、あるいはセッションのタイムアウトが発生した瞬間、書き込み中のDWGファイルは破損(Corrupt)し、設計者の数時間を、あるいは数日間の努力をゴミに変える。

今日は、そんな「運任せの保存」を卒業し、プロフェッショナルとして実装すべき「トランザクション型・ローカル退避&復旧同期システム」の設計思想とその実装を伝授する。

1. なぜ、標準のAutoSaveでは不十分なのか

AutoCADには標準の自動保存機能がある。しかし、エンジニアが構築する「業務効率化ツール」の文脈では、以下の3点が致命的な欠落となる。

1. コンテキストの欠如: 標準のAutoSaveは「図面データ」は守るが、VBA側で保持している「処理中の変数状態」や「外部DBとの連携ログ」までは同期できない。
2. 書き込み失敗への無策: ネットワーク切断時に `Save` コマンドが走ると、OSレベルでの書き込みエラーが発生し、最悪の場合、ファイルポインタが壊れた状態でファイルがロックされる。
3. 復旧プロセスの自動化不足: 復旧は常に「手動」であり、どのファイルが、どのタイミングで、どの程度最新だったのかを判別するロジックが欠落している。

我々が構築すべきは、「ネットワークへの書き込みを『不確実な試行』と定義し、失敗した瞬間に『確実なローカル保存』へフォールバックする」、極めて堅牢な設計である。

2. アーキテクチャ設計:Shadow-Saveパターン

設計の核となるのは、以下の3つのフェーズからなるサイクルだ。

A. Pre-flight Check(事前検証)

保存を実行する前に、ターゲットとなるネットワークパスへの書き込み権限と接続性を検証する。

B. Shadow-Write(二段構えの保存)

1. Primary Attempt: ネットワークパスへの `SaveAs` を試行。
2. Fallback (The Shadow): ネットワークエラー(Error 70: Permission Denied, Error 76: Path not found等)をキャッチした瞬間、即座にローカルの隠しディレクトリへ、タイムスタンプとシリアル番号を付与した名称で退避させる。

C. Synchronization Logic(復旧同期)

次回起動時、ローカルの退避フォルダをスキャンし、サーバー上のファイルよりも新しい(あるいはサーバー上に存在しない)「孤立したシャドウファイル」を発見した場合、ユーザーに同期(Overwrite or Keep both)を促す。

3. プロダクション・コード例

以下に、実務に耐えうる堅牢な実装例を示す。ここでは、ファイル操作の信頼性を高めるために `FileSystemObject` (FSO) を活用する。

※このコードを使用するには、VBAエディタの [ツール] -> [参照設定] から “Microsoft Scripting Runtime” にチェックを入れておくこと。

Option Explicit

' ==============================================================================
' Module: ShadowSaveManager
' Description: ネットワーク切断を想定した堅牢な保存・復旧システム
' Author: Chief Architect
' ==============================================================================

Private Const LOCAL_CACHE_DIR As String = "C:\ProgramData\YourCompany\AutoCAD_ShadowCache\"

''' <summary>
''' 高度な保存プロセスを実行する。
''' ネットワークへの保存に失敗した場合、ローカルへ緊急退避を行う。
''' </summary>
Public Sub ExecuteSafeSave()
    Dim targetPath As String
    Dim localPath As String
    Dim fileName As String
    Dim fso As New FileSystemObject
    Dim serialNo As String
    
    On Error GoTo ErrorHandler
    
    ' 1. 現在の図面情報を取得
    targetPath = ThisDrawing.FullName
    fileName = ThisDrawing.Name
    serialNo = Format(Now, "yyyymmdd_hhmmss")
    
    ' 2. ローカルキャッシュディレクトリの存在確認と作成
    If Not fso.FolderExists(LOCAL_CACHE_DIR) Then
        ' 権限エラーを避けるため、適切なディレクトリを作成(実際には管理者権限に注意)
        fso.CreateFolder LOCAL_CACHE_DIR
    End If

    ' 3. ネットワークへの保存試行 (Primary Attempt)
    Debug.Print "Attempting Primary Save to: " & targetPath
    
    ' ネットワークパスへの書き込みを試みる
    ' ※注意: SaveAsは図面のパス自体を書き換えるため、
    ' 既存パスがネットワークの場合、失敗時のハンドリングが極めて重要
    ThisDrawing.Save

    MsgBox "保存が成功しました。", vbInformation
    Exit Sub

ErrorHandler:
    ' 4. エラーハンドリング (Fallback Strategy)
    ' ネットワーク切断、書き込み禁止、パスが見つからない等のエラーを捕捉
    Debug.Print "Primary Save Failed. Error: " & Err.Number & " - " & Err.Description
    
    ' ローカルへの退避パスを生成 (例: FileName_20231027_123045.dwg)
    localPath = LOCAL_CACHE_DIR & Replace(fileName, ".dwg", "") & "_" & serialNo & ".dwg"
    
    Debug.Print "Initiating Shadow-Save to: " & localPath
    
    ' 重要: ネットワークへの書き込みが中途半端に失敗している可能性があるため、
    ' 完全に独立した「ローカル保存」を強制する
    On Error Resume Next
    ThisDrawing.SaveAs localPath
    
    If Err.Number = 0 Then
        MsgBox "【警告】ネットワーク接続エラーを検知しました。" & vbCrLf & _
               "図面はローカルキャッシュへ安全に退避されました。" & vbCrLf & _
               "次回起動時に同期が必要です。", vbExclamation, "Shadow-Save Executed"
    Else
        MsgBox "【致命的エラー】ローカルへの退避にも失敗しました!" & vbCrLf & _
               "作業内容をコピーしてメモ帳等に控えてください。", vbCritical, "System Failure"
    End If
    On Error GoTo 0
End Sub

''' <summary>
''' 起動時に実行される復旧同期チェック
''' </summary>
Public Sub CheckForShadowFiles()
    Dim fso As New FileSystemObject
    Dim folder As folder
    Dim file As file
    Dim localPath As String
    Dim serverPath As String
    
    ' 本来は現在の図面の本来の場所を特定するロジックが必要
    ' ここでは概念的な実装を示す
    Set folder = fso.GetFolder(LOCAL_CACHE_DIR)
    
    For Each file In folder.Files
        If fso.GetExtensionName(file.Path) = "dwg" Then
            ' ここでサーバー側のファイルと、ローカルのシャドウファイルを比較する
            ' タイムスタンプ、あるいは独自のメタデータ(XML等)を用いた比較を推奨
            Debug.Print "Found Shadow File: " & file.Path
            
            ' ユーザーに同期を促すダイアログを表示
            If MsgBox("未同期のローカルキャッシュが見つかりました:" & vbCrLf & _
                      file.Name & vbCrLf & _
                      "サーバー側へ同期(上書き)しますか?", vbYesNo + vbQuestion) = vbYes Then
                ' 同期ロジック(ファイルコピー)をここに実装
                ' SyncToServer file.Path, serverPath
            End If
        End If
    Next file
End Sub

4. プロフェッショナルが留意すべき「極限の注意点」

このシステムをプロダクション環境に投入する際、以下の3点を決して忘れてはならない。

① ファイルロックのデッドロック

ネットワークエラーが発生した際、OS側で「ファイルがまだ書き込み中である」と誤認され、ロックが残り続けることがある。`FileSystemObject` を使用して、保存前に `file.IsReady` や `file.Attributes` を確認するロジックを組み込むことで、不適切な上書きを回避できる。

② メタデータの同期

単にDWGファイルをコピーするだけでは不十分だ。もしツールが「どの図面の、どのステップまで処理が終わったか」を外部DB(SQL ServerやAccess)に記録しているなら、「DWGの保存成功」と「DBの更新」は一つのトランザクションとして扱うべきだ。
DWGをローカルに退避させたなら、その「退避した事実」と「対象のID」をログファイル(JSONやXML)としてローカルに書き出しておく。これが復旧時の「地図」になる。

③ ローカルディレクトリの管理(ガベージコレクション)

ローカルに退避ファイルを溜め込み続けるのは、設計として美しくない。
「同期が完了したファイル」および「作成から30日以上経過した古いキャッシュ」を自動的に削除するクリーンアップ・ロジックを必ず実装せよ。これを怠ると、クライアントのPCのディスク容量を圧迫し、別のクレームを招くことになる。

結びに代えて

自動化エンジニアの真価は、順調な時にどれだけ速いコードを書けるかではなく、最悪の事態が起きた時に、どれだけ優雅に、かつ確実にデータを守り抜けるかで決まる。

「ネットワークは切れるもの」という前提に立ち、設計の最初の一行から「失敗した時のフォールバック」を組み込む。この思考こそが、アマチュアとプロフェッショナルを分かつ境界線である。

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