@Test
public void testFence2() throws Exception {

  byte[] fenceId = "test_table".getBytes(Charsets.UTF_8);

  // Readers use fence to indicate that they are interested in changes to specific data
  // Reader 1
  TransactionAware readFence1 = VisibilityFence.create(fenceId);
  TransactionContext readTxContext1 = new TransactionContext(new InMemoryTxSystemClient(txManager), readFence1);
  readTxContext1.start();

  // Reader 2
  TransactionAware readFence2 = VisibilityFence.create(fenceId);
  TransactionContext readTxContext2 = new TransactionContext(new InMemoryTxSystemClient(txManager), readFence2);
  readTxContext2.start();

  // Reader 3
  TransactionAware readFence3 = VisibilityFence.create(fenceId);
  TransactionContext readTxContext3 = new TransactionContext(new InMemoryTxSystemClient(txManager), readFence3);
  readTxContext3.start();

  // Writer updates data here in a separate transaction (code not shown)
  // start tx
  // update
  // commit tx

  // Now writer needs to wait for readers 1, 2, and 3 to see the change, it uses write fence to do so
  TransactionAware writeFence = new WriteFence(fenceId);
  TransactionContext writeTxContext = new TransactionContext(new InMemoryTxSystemClient(txManager), writeFence);
  writeTxContext.start();

  // Reader 1 commits before writeFence is committed
  readTxContext1.finish();

  try {
    // writeFence will throw exception since Reader 1 committed without seeing changes
    writeTxContext.finish();
    Assert.fail("Expected transaction to fail");
  } catch (TransactionConflictException e) {
    // Expected
    writeTxContext.abort();
  }

  // Start over writeFence again
  writeTxContext.start();

  // Now, Reader 3 commits before writeFence
  // Note that Reader 3 does not conflict with Reader 1
  readTxContext3.finish();

  try {
    // writeFence will throw exception again since Reader 3 committed without seeing changes
    writeTxContext.finish();
    Assert.fail("Expected transaction to fail");
  } catch (TransactionConflictException e) {
    // Expected
    writeTxContext.abort();
  }

  // Start over writeFence again
  writeTxContext.start();
  // This time writeFence commits before the other readers
  writeTxContext.finish();

  // After this point all readers will see the change

  try {
    // Reader 2 commits after writeFence, hence this commit with throw exception
    readTxContext2.finish();
    Assert.fail("Expected transaction to fail");
  } catch (TransactionConflictException e) {
    // Expected
    readTxContext2.abort();
  }

  // Reader 2 has to abort and start over again. It will see the changes now.
  readTxContext2 = new TransactionContext(new InMemoryTxSystemClient(txManager), readFence2);
  readTxContext2.start();
  readTxContext2.finish();
}