org.akubraproject.txn.derby
Class TransactionalStore

java.lang.Object
  extended by org.akubraproject.impl.AbstractBlobStore
      extended by org.akubraproject.txn.AbstractTransactionalStore
          extended by org.akubraproject.txn.derby.TransactionalStore
All Implemented Interfaces:
org.akubraproject.BlobStore

public class TransactionalStore
extends AbstractTransactionalStore

A simple transactional store using Derby db for the transaction logging and id mappings. It provides snapshot isolation with fail-fast semantics, meaning it will immediately throw a ConcurrentBlobUpdateException if a transaction tries to modify (insert, delete, or overwrite) a blob which was modified by another transaction since the start of the first transaction (even if the change by the other transaction hasn't been committed yet). The assumption is that rollbacks are rare and that it is better to be notified of a conflict immediately rather than wasting time uploading large amounts of data that will just have to be deleted again.

In general a transaction must be considered failed and should be rolled back after any exception occurred.

This store must be configured with exactly one underlying blob-store. It supports arbitrary application-ids and maps them to the underlying blob-store's id's; it currently requires that the underlying blob-store be capable of generating ids.

Snapshot isolation is implemented using a MVCC design as follows. A name-map holds a list of versioned id mappings which maps application-ids to underlying store-ids; in addition, each mapping has two flags indicating whether the mapping has been deleted and whether it has been committed. When a transaction starts it is given a read version number (these increase monotonically); only committed map entries with a version less than this read version or uncommitted entries with a version the same as the read version will be read; if there are multiple such entries for a given app-id, then the one with the highest version is used. If the transaction makes a change (adding, removing, replacing, etc), a new entry in recorded in the map with the version set to the read-version and with the committed flag set to false. On commit the transaction is assigned a write version number (which is higher than any previously issued read version numbers) and which it then sets on all entries written as part of this transaction; it also sets the committed flag to true on these entries.

Old entries (and the underlying blobs) are cleaned out as they become unreferenced, i.e. when no active transaction could refer to them anymore. In order to speed up the discovery of such entries, a separate deleted-list is kept into which an entry is made each time an entry in the main map is marked as deleted and each time a blob is marked as deleted. This list is processed at the end of every transaction and upon startup (on startup the list is completely cleared as there are no active transactions).

A note on locking: Derby, even in read-uncommitted mode, likes to acquire exclusive locks on rows when doing inserts, deletes, and updates. This would be ok, except that it sometimes attempts to lock rows it won't change. This can lead to deadlocks. The way around this that I've found is to ensure Derby always uses an index when searching for the rows to update or delete. This is accomplished by giving the optimizer explicit instructions via the DERBY-PROPERTIES directive in the queries. Since this directive is only supported in select statements, all updates and deletes are done via updatable queries (result-sets). This actually performs about the same as a direct update or delete statement. See also the thread disabling locking (continued), or at gmane. Unfortunately, however, this does not seem to be sufficient: Derby may still lock other rows, as documented in Scope of locks in Derbys's developer guide. When this happens, the wait for the lock will eventually time out and an exception will be thrown. However, I have not enountered this issue so far. But a related issue is present in 10.4 and earlier, namely DERBY-2991; testing with 10.5 indicates this issue has been resolved. For these reasons a flag is provided to restrict the number of concurrent write-transactions to one, and the three-argument-constructor will set this single-writer flag to true for derby 10.4 and earlier.

Author:
Ronald Tschalär

Field Summary
static String DEL_TABLE
          The SQL table used by this store to hold the list of deleted blobs
static String NAME_TABLE
          The SQL table used by this store to hold the name mappings
 
Fields inherited from class org.akubraproject.txn.AbstractTransactionalStore
wrappedStore
 
Fields inherited from class org.akubraproject.impl.AbstractBlobStore
id
 
Constructor Summary
TransactionalStore(URI id, org.akubraproject.BlobStore wrappedStore, String dbDir)
          Create a new transactional store.
TransactionalStore(URI id, org.akubraproject.BlobStore wrappedStore, String dbDir, boolean singleWriter)
          Create a new transactional store.
 
Method Summary
 org.akubraproject.BlobStoreConnection openConnection(Transaction tx, Map<String,String> hints)
           
 
Methods inherited from class org.akubraproject.impl.AbstractBlobStore
getId
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Field Detail

NAME_TABLE

public static final String NAME_TABLE
The SQL table used by this store to hold the name mappings

See Also:
Constant Field Values

DEL_TABLE

public static final String DEL_TABLE
The SQL table used by this store to hold the list of deleted blobs

See Also:
Constant Field Values
Constructor Detail

TransactionalStore

public TransactionalStore(URI id,
                          org.akubraproject.BlobStore wrappedStore,
                          String dbDir)
                   throws IOException
Create a new transactional store. The single-writer flag will be determined automatically depending on the version of derby being used.

Parameters:
id - the id of this store
wrappedStore - the wrapped non-transactional store
dbDir - the directory to use to store the transaction information
Throws:
IOException - if there was an error initializing the db

TransactionalStore

public TransactionalStore(URI id,
                          org.akubraproject.BlobStore wrappedStore,
                          String dbDir,
                          boolean singleWriter)
                   throws IOException
Create a new transactional store.

Parameters:
id - the id of this store
wrappedStore - the wrapped non-transactional store
dbDir - the directory to use to store the transaction information
singleWriter - if true, serialize all writers to avoid all locking issues with Derby; if false, some transactions may fail sometimes due to locks timing out
Throws:
IOException - if there was an error initializing the db
Method Detail

openConnection

public org.akubraproject.BlobStoreConnection openConnection(Transaction tx,
                                                            Map<String,String> hints)
                                                     throws IllegalStateException,
                                                            IOException
Throws:
IllegalStateException - if no backing store has been set yet
IOException


Copyright © 2009-2012 DuraSpace. All Rights Reserved.