【テクニカル・上級編】DAO.RecordsetのLockEditsプロパティによる悲観的排他制御の最適化 – Access VBA解析バイブル

スポンサーリンク

DAO.RecordsetとLockEdits:Accessにおける悲観的排他制御の「深淵」を掌握する

Accessにおける複数ユーザー環境の構築は、多くの開発者が「なんとなく動く」状態で放置している魔境だ。特に、`DAO.Recordset`の`LockEdits`プロパティを理解せずにシステムを組むことは、時限爆弾を抱えたまま運用するに等しい。

今日は、Accessの排他制御の核心、すなわち「悲観的ロック(Pessimistic Locking)」の最適化手法について、泥臭い現場の知見を交えて解説する。

1. LockEditsの本質と「悲観的」の意味

`Recordset.LockEdits`には2つのモードがある。

  • True(悲観的排他制御): `.Edit`メソッドを実行した瞬間にレコードをロックする。
  • False(楽観的排他制御): `.Update`を実行する直前までロックしない。

多くのエンジニアは「Falseの方が軽快だから良い」と短絡的に考えがちだが、大規模データや頻繁に更新が競合するマスターテーブルにおいて、楽観的ロックは「更新時エラー(Error 3197)」の嵐を招く。悲観的ロックは、整合性が絶対条件である業務システムにおける最後の砦である。

2. 実践:最適化された悲観的ロックの定石

単に`LockEdits = True`にするだけでは不十分だ。重要なのは「ロックを保持する期間を最小化すること」である。UI操作を挟んだまま編集状態を維持するのは、デッドロックへの招待状だ。

以下のコードは、トランザクションの重みを知り尽くしたアーキテクトが記述するべき基本形である。

‘ 悲観的ロックを制御する理想的な更新パターン
Public Sub UpdateRecordOptimized(ByVal targetID As Long)
Dim db As DAO.Database
Dim rs As DAO.Recordset

Set db = CurrentDb
‘ ロックの競合を考慮し、レコードセットは必要なカラムのみに絞る
Set rs = db.OpenRecordset(“SELECT FROM T_Orders WHERE OrderID = ” & targetID, dbOpenDynaset)

‘ 明示的な悲観的ロックの指定
rs.LockEdits = True

On Error GoTo HandleError

If Not rs.EOF Then
rs.Edit
rs!Status = “Processed”
‘ ここで重い処理を行わないこと。ロック時間は最小にする。
rs.Update
End If

Cleanup:
‘ オブジェクトの明示的解放はVBAの鉄則。ガベージコレクションを待つな。
If Not rs Is Nothing Then rs.Close: Set rs = Nothing
Set db = Nothing
Exit Sub

HandleError:
If Err.Number = 3260 Then
MsgBox “他ユーザーが編集中です。後ほど再試行してください。”, vbCritical
Else
MsgBox “予期せぬエラー: ” & Err.Description
End If
Resume Cleanup
End Sub

3. レガシー環境における「デッドロック回避」の極意

Accessの排他制御は、バックエンドであるJet/ACEエンジンのロックファイル(.ldb/.laccdb)に依存している。ここでWindows APIの力を借りる必要があるケースがある。

もし複雑なシステム連携や、外部プロセスからの書き込みが混在する場合、標準のロック機能だけでは競合を解消できない。その際は、APIによるグローバルミューテックス(Mutex)を実装し、アプリケーション層で排他をかけるのが、伝説級の現場で生き残るための「裏技」だ。

‘ 簡略化したミューテックスの宣言例
Private Declare PtrSafe Function CreateMutex Lib “kernel32” Alias “CreateMutexA” _
(ByVal lpMutexAttributes As LongPtr, ByVal bInitialOwner As Long, ByVal lpName As String) As LongPtr

これを活用し、特定の共有リソースへのアクセスをOSレベルでシリアライズすることで、Accessの`LockEdits`と組み合わせた強固な「二段構えの防壁」を構築できる。

4. 伝説のエンジニアからの提言:なぜ「オブジェクトの明示的解放」が必須か

Access VBAは、オブジェクトの参照カウンタがゼロにならない限り、メモリ上にインスタンスが滞留する。特に`Recordset`を`Set = Nothing`せずにループ内で放置することは、共有メモリの断片化を引き起こし、最終的に「原因不明のシステムリソース不足」を誘発する。

  • DAO/ADOオブジェクトは、スコープを抜ける前に必ず`Close`し、`Nothing`を代入せよ。
  • CurrentDbを多用するな。 一度変数に格納し、使い回すことで、オーバーヘッドを劇的に下げられる。

結びに:技術に「銀の弾丸」なし

`LockEdits`の最適化は、システムが扱うデータの「粒度」と「頻度」との対話である。すべてを悲観的にすればシステムは重くなり、すべてを楽観的にすればデータは壊れる。

シニアエンジニアの役割は、このバランスを「コード」という形で設計することだ。仕様書に書かれていない挙動まで掌握し、バックエンドの静かな悲鳴を聞き取れるようになった時、君もまたAccessの真の支配者になれるはずだ。

妥協のない設計を追求せよ。それが、レガシーシステムの寿命を十年延ばす唯一の道だ。

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