|
||||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | |||||||||
SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |
java.lang.Objectorg.akubraproject.impl.AbstractBlobStore
org.akubraproject.txn.AbstractTransactionalStore
org.akubraproject.txn.derby.TransactionalStore
public class TransactionalStore
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.
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 |
---|
public static final String NAME_TABLE
public static final String DEL_TABLE
Constructor Detail |
---|
public TransactionalStore(URI id, org.akubraproject.BlobStore wrappedStore, String dbDir) throws IOException
id
- the id of this storewrappedStore
- the wrapped non-transactional storedbDir
- the directory to use to store the transaction information
IOException
- if there was an error initializing the dbpublic TransactionalStore(URI id, org.akubraproject.BlobStore wrappedStore, String dbDir, boolean singleWriter) throws IOException
id
- the id of this storewrappedStore
- the wrapped non-transactional storedbDir
- the directory to use to store the transaction informationsingleWriter
- if true, serialize all writers to avoid all locking issues with
Derby; if false, some transactions may fail sometimes due to
locks timing out
IOException
- if there was an error initializing the dbMethod Detail |
---|
public org.akubraproject.BlobStoreConnection openConnection(Transaction tx, Map<String,String> hints) throws IllegalStateException, IOException
IllegalStateException
- if no backing store has been set yet
IOException
|
||||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | |||||||||
SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |