public final class VisibilityFence extends Object
Typically a reader will never conflict with a writer, since a reader only sees committed changes when its transaction started. However to ensure that after a given point all readers are aware of a change, we have to introduce a conflict between a reader and a writer that act on the same data concurrently.
This is done by the reader indicating that it is interested in changes to a piece of data by using a fence in its transaction. If there are no changes to the data when reader tries to commit the transaction containing the fence, the commit succeeds.
On the other hand, a writer updates the same data in a transaction. After the write transaction is committed, the writer then waits on the fence to ensure that all in-progress readers are aware of this update. When the wait on the fence returns successfully, it means that any in-progress readers that have not seen the change will not be allowed to commit anymore. This will force the readers to start a new transaction, and this ensures that the changes made by writer are visible to the readers.
In case an in-progress reader commits when the writer is waiting on the fence, then the wait method will retry until the given timeout.
Hence a successful await on a fence ensures that any reader (using the same fence) that successfully commits after this point onwards would see the change.
Sample reader code:
TransactionAware fence = VisibilityFence.create(fenceId);
TransactionContext readTxContext = new TransactionContext(txClient, fence, table1, table2, ...);
readTxContext.start();
// do operations using table1, table2, etc.
// finally commit
try {
readTxContext.finish();
} catch (TransactionConflictException e) {
// handle conflict by aborting and starting over with a new transaction
}
Sample writer code:
// start transaction
// write change
// commit transaction
// Now wait on the fence (with the same fenceId as the readers) to ensure that all in-progress readers are
aware of this change
try {
FenceWait fenceWait = VisibilityFence.prepareWait(fenceId, txClient);
fenceWait.await(50000, 50, TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
// await timed out, the change may not be visible to all in-progress readers.
// Application has two options at this point:
// 1. Revert the write. Re-try the write and fence wait again.
// 2. Retry only the wait with the same fenceWait object (retry logic is not shown here).
}
fenceId in the above samples refers to any id that both the readers and writer know for a given
piece of data. Both readers and writer will have to use the same fenceId to synchronize on a given data.
Typically fenceId uniquely identifies the data in question.
For example, if the data is a table row, the fenceId can be composed of table name and row key.
If the data is a table cell, the fenceId can be composed of table name, row key, and column key.
Note that in this implementation, any reader that starts a transaction after the write is committed, and while this read transaction is in-progress, if a writer successfully starts and completes an await on the fence then this reader will get a conflict while committing the fence even though this reader has seen the latest changes. This is because today there is no way to determine the commit time of a transaction.
Modifier and Type | Method and Description |
---|---|
static TransactionAware |
create(byte[] fenceId)
Used by a reader to get a fence that can be added to its transaction context.
|
static FenceWait |
prepareWait(byte[] fenceId,
TransactionSystemClient txClient)
Used by a writer to wait on a fence so that changes are visible to all readers with in-progress transactions.
|
public static TransactionAware create(byte[] fenceId)
fenceId
- uniquely identifies the data that this fence is used to synchronize.
If the data is a table cell then this id can be composed of the table name, row key
and column key for the data.TransactionAware
to be added to reader's transaction context.public static FenceWait prepareWait(byte[] fenceId, TransactionSystemClient txClient) throws TransactionFailureException, InterruptedException, TimeoutException
fenceId
- uniquely identifies the data that this fence is used to synchronize.
If the data is a table cell then this id can be composed of the table name, row key
and column key for the data.FenceWait
objectTransactionFailureException
InterruptedException
TimeoutException
Copyright © 2016 The Apache Software Foundation. All rights reserved.