Lab 10: Access Manager & Transactions

  • Course: COSC 460 Databases Fall 2018
  • Instructor: Michael Hay
  • Assignment: Lab 10: Access Manager & Transactions
  • Due: Mon, Dec 3 at 11:59 PM
  • Starter code: lab10.zip

Introduction

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.

Environment setup

See previous labs.

Saving and accessing your files

See previous labs.

Assignment overview

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!

Starter code

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.

Tasks

  • Task 1 Revise all existing code that pins pages. Rather than calling the BufferManager directly, it should instead use the AccessManager via the Database.getAccessManager() method. IntelliJ’s “Find Usages” tool may be handy.
  • Task 2 Keep track of pins and unpins inside 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).
  • Task 3 Ensure that your code implements strict 2PL. At a high level, this involves two things. First, before pinning a page, appropriate locks should be acquired. There are some subtle detail inside 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.
  • Task 4 When a transaction ends (either in an abort or commit), certain actions must be taken in addition to releasing locks.
    • The precise actions depends on the force policy which, by default, is set to true; something we will change in a later lab.
    • If a transaction is committing and a force policy is in place, then pages dirtied by this transaction should be flushed to disk. Be sure that the page was dirtied by this transaction and not some other transaction!
    • If a transaction is aborting, then any pages that are still pinned should be unpinned and any dirtied pages should be discarded from the buffer pool.
  • Task 5 Enforce a “no steal” policy. At this point ColgateDB does not have a logging system nor the capability to undo changes made by a transaction that aborts. Thus, we should ensure a “no steal” policy: pages that have been dirtied by a transaction should not be allowed to be flushed to disk until the transaction has committed. Make sure that AccessManagerImpl enforces a no steal policy. (Hint: this can be done with one line in the constructor of AccessManagerImpl.)

Implementation details: acquiring locks in HeapFile

There are some subtleties around acquiring and releasing locks in the following situations:

  • Looking for an empty slot into which you can insert tuples. Most implementations scan pages looking for an empty slot, and will need a 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.
  • Adding a new page to a 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?).

Milestone

The milestone for this lab will be to complete the above tasks and pass TransactionTest and AccessManagerTest.

Submission instructions

Upload your files to Gradescope.