Try   HackMD
tags: database

Assignment 4 report 2

組員: 109062274, 109062314, 109062315

BufferPoolMgr

解答中實作了 lock striping, 分別對 Block 以及 File 上 lock。但我們實作中都沒考慮到。

Why Block Lock Stripping?

有可能 2 個以上的 transaction 想對同一個 block 做 pin ,但是 bufferpool 中沒有相對應的 block,所以 buffer replacement strategy 會各分派一個不同的 candidate buffer 給各個 transaction 做 swapping。但這樣的結果是 bufferpool 中有不同的 Buffer 內容包著相同的 BlockId。這樣有可能會有consistency issue。這樣也違反了 pinning Buffer 原本的用意,原本用意是每個 Buffer cache 不同的 Block, 並依據 pin count 來決定 buffer replacement strategy。而且如果相同的 Block 佔據不同的 Buffer,而且這個 Block 被頻繁使用,bufferpool 能 swap out 的 Buffer 會相對減少。

Why File Lock Stripping?

我覺得解答的寫法沒有必要將同一個 filename 上 lock,這是因為假如 2 個以上的 transactions 同時執行 BufferPoolMgr::pinNew,在 clock replacement 已經有 swapLock 保護同一個 Buffer 免於在做 swapping 還被選作 candidate buffer,所以 swapping 是一次做完沒有停頓 (即使 transactions pinNew 同一個 file)。而且 swapping 完成時,Buffer 的 BlockId 一定是不同的,因為 pinNew 代表說我們要 append file , append file 一定是 return 新的 BlockId 給 Buffer。所以也不會有不同的 Buffer 內容包著相同的 BlockId 的問題。如果是同個 file 要做 append 分配到不同 Buffer 做 swapping,其實最底層的 JavaNioFileChannel::append()也有上一個 readwrite lock,所以也沒有race condition 的問題。

故我們覺得 Block Lock Stripping 技術解決了不同 buffer 可能會有同樣的 Block 的問題。但 File Lock Stripping 有點沒必要。

BufferPoolMgr::pin

解答中的寫法跟我們在 phase 1 report 中講的方法基本上大相逕庭,但是我們的實作發現一次跑多個 testcase 會過單獨跑某個 testcase 不會過,因此把 pin 改回原來的版本(synchronized method)。這對於 throughput 是個很大的 bottleneck,因為 synchronized method 只會允許 1 個 Thread 執行 pin 這個程式片段,但是如果多個 transaction pin 的 Buffer 不同, Block 也不同,這其實是可以併行執行的,也不會相互影響,這也許就是我們在 Phase1 實驗中得不到更好的 throughput 的原因。

BufferMgr

這部分的優化,助教與我們的作法皆是減少 synchronized(bufferPool) 所包住的範圍,而差異在於助教包得更精準,且根據何時要呼叫 bufferPool.notifyAll; 有更好的判斷。而在實作 pinpinNew 實作FIFO的部分上,我們改為允許由 WAKE_RATIO 外部參數決定之區間大小皆可繼續執行的邏輯,期望有更好的 performance。

Buffer

解答實作做了 contentLock,對 Page 的操作上 Lock,但我們沒有實作這功能。而 pins 計數器以 AtomicInteger 來實現這點不謀而合。

ContentLock

從解答中發現 contentLock 主要是對以下方法上 ReadWriteLock

  • Buffer::getval
  • Buffer::setval
  • Buffer::lastLsn
  • Buffer::close
  • Buffer::flush
  • Buffer::isModified // 原本沒有的方法

但是其實有些 method 可以拿掉 ReadWrite Lock,像是 Buffer::close 底層的 JavaNioByteBuffer::close 不會做任何事,還有在 Buffer::getval 中呼叫的 Page::getval 以及 Buffer::setval 中呼叫 Page::setval

另外 Pagesetval 以及 getval 都還保留 synchronized method。由於有 contentLock 就能保證 Page::getval 以及 Page::setval 是 thread-safe,因此應該可以拿掉 synchronized。

FileMgr & Page

  • 助教: 透過保留 Page methods 的 synchronized,使讀寫檔案、IoBuffer 為 thread-safe,因此移除了 FileMgrreadwriteappend synchronized。而在 FileMgr 中,getFileChanneldelete 則根據 filename 使用 lock stripping 技術,畢竟不能同時有多個 thread 新增或刪除同一個檔案
  • 我們: 將 FileMgr 對檔案操作之 methods 加上 StampedLock,Read 用 pessimistic read、Size 用 very-optimistic read、Write & delete 則採用 write lock。而在 Page 中,由於對檔案讀寫已經是 thread-safe,因此將 readwriteappend 的 synchronized 移除,然後將對 IoBuffer 操作之 getValsetVal 加上 ReentrantLock
  • 比較: 我們沒有處理好 getFileChannel (相當於我們的 getLockableFile) 的 thread-safe,雖然有使用 try-catch 避免出錯,但邏輯上還是助教的寫法比較好 (根據 filename 加上 lock)。而其他部分,相比助教直接使用 synchronized 達成 thread-safe,我們會根據不同狀況使用 StampedLock、ReentrantLock,使得有較好的並行性。

BlockId

與助教類似,hashCodetoString 方法能直接取出恆定的計算結果。