This is a continuation of the previous two labs. The first main goal is to integrate the lock manager into your existing code base so that appropriate locks are acquired whenever pages are accessed. The second goal is to support the notion of a Transaction.
See previous labs.
See previous labs.
In this part, you will write an implementation of the AccessManager
in the AccessManagerImpl
class. In addition, you will revise your HeapFile
code to call AccessManager
instead of BufferManager
.
Please note that the idea of an AccessManager
is not something you will see in the book and is purely something created for ColgateDB. The basic idea of the AccessManager
is that whenever you need a page, you talk to the AccessManager
. The AccessManager
, in turn, can talk with the BufferManager
. The classes have separate responsibilities:
BufferManager
: its sole concern is caching pages in memory. It is unaware of transactions or locks.LockManager
: its sole concern is granting locks to a pool of objects (in this case, pages). It is unaware of transactions or the buffer manager.AccessManager
: is a central point for accessing page data. The AccessManager
is “aware” of buffer management, lock management, and the notion of a transaction. But it delegates most of work to the approrpiate helper classes.Because the ColgateDB assignments build gradually, I needed to add the AccessManager
layer as the layer where all of the pieces you’ve built previously come together!
You are expected to modify these files:
AccessManagerImpl.java
HeapFile.java
You are expected to read but not modify the other files that were provided.
BufferManager
directly, it should instead use the AccessManager
via the Database.getAccessManager()
method. IntelliJ’s “Find Usages” tool may be handy.AccessManagerImpl
. This will be important later when it comes to handling aborted transactions. For now, ensure that your AccessManagerImpl
maintains a list of transactions that have pinned each page. Keep in mind (1) a transaction could pin a page multiple times (thus generating multiple entries in the list), and (2) multiple transactions will be executing this code concurrently so updates must be done atomically (use synchronized
blocks). With a careful implementation, you can avoid the need to worry about deadlock occurring within AccessManagerImpl
(the LockManagerImpl
may still experience deadlock, however).HeapFile
(see below). Second, all locks should be held until the transaction aborts or commits. Look at Transaction.commit()
and Transaction.abort()
and follow what the code does. Then it should be clear where you need to release the locks.true
; something we will change in a later lab.AccessManagerImpl
enforces a no steal policy. (Hint: this can be done with one line in the constructor of AccessManagerImpl
.)There are some subtleties around acquiring and releasing locks in the following situations:
READ_ONLY
lock to do this. If a transaction t finds no free slot on a page p, t may immediately release the lock on p. Although this technically contradicts the rules of two-phase locking, this deviation is permitted because t did not use any data from the page, such that a concurrent transaction t’ which updated p cannot possibly effect the answer or outcome of t. Important: the lock should only be released if it was just acquired; if the transaction already had the lock (e.g., from a previous operation), then the lock should not be released. You will need to check whether the lock was previously held.HeapFile
. When a transaction needs to allocate a new page to the HeapFile, care must be taken that the entire page allocation process is done atomically. Note: this is not as simple as making allocatePage
atomic (why not?).The milestone for this lab will be to complete the above tasks and pass TransactionTest
and AccessManagerTest
.
Upload your files to Gradescope.