バッファ管理。タプルの削除

DELETEを実行するとだいたいこんな流れ。

  • 条件に合ったタプルを持ってくる。持ってくるときの処理はバッファ管理。バッファの読み書きと同じ。(リンク先も今回もシーケンススキャンされることを前提。)
  • HeapTupleSatisfiesUpdate()を呼んでタプルが更新可能かチェックする。
  • 他の実行中のトランザクションによって更新されているならば他のトランザクションが終わるまで待つ(他のトランザクションが終了すると起こされる)。セマフォでプロセス間通信。
  • タプルのヘッダに情報をセット(infomask, xmax, cmax)してバッファをダーティにする。WALにも書く。

実行中のトランザクションによって更新されているかどうかで結構処理がちがう。更新されている場合はLockTuple()/UnlockTuple()が呼ばれ、更新されていない場合は呼ばれない。名前からして排他ロックや共有ロックが必要なところで必ず呼ばれるのかと思ったがちがった。
また、タプルのロックでは必ずheap_lock_tuple()が呼ばれると思っていたがちがった。削除のときはこの関数を呼んでいない(FKによって参照されていれば制約チェックとしてheap_lock_tuple()を呼び出すけど)。


他の実行中のトランザクション(プロセス)の完了を待つにはセマフォが使われる。PGSemaphoreLock()の中でsemop()が呼ばれる。GDBで確認した呼び出しシーケンス(抜粋)は次のとおり。XactLockTableWait()が他のトランザクションを待つための関数。

(gdb) bt
#0  PGSemaphoreLock (sema=0xb78633f8, interruptOK=1 '\001') at pg_sema.c:371
#1  0x0824089f in ProcSleep (locallock=0x8466be8, lockMethodTable=0x83ce824) at proc.c:880
#2  0x0823da6c in WaitOnLock (locallock=0x8466be8, owner=0x84e6128) at lock.c:1146
#3  0x0823d440 in LockAcquire (locktag=0xbfc4d4d8, lockmode=5, sessionLock=0 '\0', dontWait=0 '\0') at lock.c:791
#4  0x0823c34d in XactLockTableWait (xid=632) at lmgr.c:476
#5  0x0809e06d in heap_delete (relation=0xb5632370, tid=0xbfc4d67a, ctid=0xbfc4d5e6, update_xmax=0xbfc4d5e0, cid=0, crosscheck=0x0, wait=1 '\001') at heapam.c:2076
#6  0x0818d8ba in ExecDelete (tupleid=0xbfc4d67a, planSlot=0x84ffcc0, dest=0x84ae670, estate=0x84ffba8) at execMain.c:1702

トランザクションは完了すると待ちに入っているトランザクションを起こす。PGSemaphoreUnlock()の中でsemop()が呼ばれる。GDBで確認した呼び出しシーケンス(抜粋)は次のとおり。ここではロールバックしている。

(gdb) bt
#0  PGSemaphoreUnlock (sema=0xb77db228) at pg_sema.c:451
#1  0x08240dee in ProcWakeup (proc=0xb77db220, waitStatus=0) at proc.c:1075
#2  0x08240eb6 in ProcLockWakeup (lockMethodTable=0x83ce824, lock=0xb7783b80) at proc.c:1119
#3  0x0823d87c in CleanUpLock (lock=0xb7783b80, proclock=0xb77cfa40, lockMethodTable=0x83ce824, hashcode=3959657664, wakeupNeeded=1 '\001') at lock.c:1041
#4  0x0823e3ed in LockReleaseAll (lockmethodid=1, allLocks=1 '\001') at lock.c:1571
#5  0x0824044d in ProcReleaseLocks (isCommit=0 '\0') at proc.c:566
#6  0x08337b5c in ResourceOwnerReleaseInternal (owner=0x84592e8, phase=RESOURCE_RELEASE_LOCKS, isCommit=0 '\0', isTopLevel=1 '\001') at resowner.c:250
#7  0x083379ef in ResourceOwnerRelease (owner=0x84592e8, phase=RESOURCE_RELEASE_LOCKS, isCommit=0 '\0', isTopLevel=1 '\001') at resowner.c:171

ロック関係の処理は他にもいろいろある。奥が深そう。