READ_COMMITTED isolation can be sufficient and satisfactory for most applications, but those that do require stricter isolation should be permitted to perform large-scale compound cache operations atomically when necessary. The difference is that it should be a caching option rather than a characteristic entrenched in the implementation.

ISOLATION STRATEGIES

We just discussed transaction isolation requirements from the user perspective, but only waved our hands about the fact that such isolation requirements may be implemented differently. In this section we describe some of the common strategies for handling isolation between applications.

 

IMPLICIT COPY-ON-READ

One of the tried-and-true approaches to guaranteeing data consistency and complete isolation is implicit copy-on-read, which creates a local transactional cache copy of the object as soon as it is read in the unit of work. This certainly guarantees that whatever happens, no other application will see the changes to that object until they are written to the persistent store or merged into the shared cache. The following is a sample sequence of events in a traditional implicit copy-on-read scenario: 1. Begin tx.

2. Read object (get object from shared cache or from database).

3. Copy object and insert in tx cache.

4. Return copy to user.

5. User modifies copy.

6. Tx commit begins.

7. Modified copy contents get sent to persistent storage.

8. Tx commit completes.

9. Changes made to object are merged into shared cache.

A window exists between the time the transaction completes its commit phase and the time the changes are merged into the shared cache. This means that there is some nonzero amount of time in which another application could get a stale copy of the object from the shared cache, even though it has been updated in the database.

This window is of no real consequence, however, since if the second application is only reading the object, then it might just as well have read it before the first application committed its transaction and gotten the same stale data. The fact that it happened to have read it within that window of time has no relevance. If it were to have a stale copy of the object and perform a write on it, however, it would be bad if the changes of the first application ended

up getting overwritten or lost as a result of the staleness of the initial state of the object being written to by the second application. The solution to this problem lies in optimistic locking of the entity to ensure that no changes get overwritten or lost. 3

Because the implementation automatically performs the copying without the user needing to do anything, one of the advantages of an implicit copy-on-read is that the user need not take any special action when deciding to update the object. The user can rely on the copy that it has been using to read from and perform the writes on that copy. Any and all updates will be sent to the database at the appropriate time.

A marked problem with eagerly copying-on-read is the accumulation of objects in the transactional cache. The cache does not necessarily distinguish between objects that were read and those that were updated or made transactional because an update was forthcoming. Applications that start a transaction and do a great deal of reading but only a little writing will see their transactional caches grow to include all of the objects, not just those that contain changes and need to be written out.

IMPLICIT COPY-ON-WRITE

A more efficient approach to managing the transactional cache space is to do no copying of objects as they are read into the transaction, but only as they are modified by the user. This implicit copy-on-write approach limits the trans- action to containing only those objects that have been changed, and it does not leave the transaction vulnerable to bulging instance counts and management costs. A typical implicit copy-on-write sequence is:

1. Begin tx.

2. Read object (get object from shared cache or from database).

3. Return object to user.

4. User modifies object, causing copy to be created with changes stored inside it.

5. Insert copy in tx cache.

6. Tx commit begins.

7. Modified copy contents get sent to persistent storage.

8. Tx commit completes.

9. Changes made to object are merged into shared cache.
This appears to be an elegant approach to managing
transactional data, since only dirty objects end up being
copied, and this would again be performed automatically
by the implementation. Objects that became transac-
tional solely for reading turn out not really to be transac-
tional at all and don’t take up transactional space.
The catch to this strategy shows up when the copy

References:

http://www.acmqueue.com

Archives