/** * Like {@link #runInTx(Runnable)}, but allows returning a value and throwing an exception. */ public <R> R callInTx(Callable<R> callable) throws Exception { Transaction tx = activeTx.get(); // Only if not already set, allowing to call it recursively with first (outer) TX if (tx == null) { tx = beginTx(); activeTx.set(tx); try { R result = callable.call(); tx.commit(); return result; } finally { activeTx.remove(); tx.close(); } } else { if (tx.isReadOnly()) { throw new IllegalStateException("Cannot start a transaction while a read only transaction is active"); } return callable.call(); } }
/** * Runs the given runnable inside a transaction. * <p> * Efficiency notes: it is advised to run multiple puts in a transaction because each commit requires an expensive * disk synchronization. */ public void runInTx(Runnable runnable) { Transaction tx = activeTx.get(); // Only if not already set, allowing to call it recursively with first (outer) TX if (tx == null) { tx = beginTx(); activeTx.set(tx); try { runnable.run(); tx.commit(); } finally { activeTx.remove(); tx.close(); } } else { if (tx.isReadOnly()) { throw new IllegalStateException("Cannot start a transaction while a read only transaction is active"); } runnable.run(); } }
@Override public void run() { latchBeforeBeginTx.countDown(); Transaction tx2 = store.beginTx(); latchAfterBeginTx.countDown(); tx2.close(); } }.start();
Cursor<T> getWriter() { Cursor<T> cursor = getActiveTxCursor(); if (cursor != null) { return cursor; } else { Transaction tx = store.beginTx(); try { return tx.createCursor(entityClass); } catch (RuntimeException e) { tx.close(); throw e; } } }
@Test public void testEmptyTransaction() { Transaction transaction = store.beginTx(); transaction.commit(); }
@Test(expected = IllegalStateException.class) public void testCommitAfterAbortException() { Transaction tx = store.beginTx(); tx.abort(); tx.commit(); }
@Test public void testWriteTxBlocksOtherWriteTx() throws InterruptedException { long time = System.currentTimeMillis(); Transaction tx = store.beginTx(); long duration = System.currentTimeMillis() - time; // Usually 0 on desktop final CountDownLatch latchBeforeBeginTx = new CountDownLatch(1); final CountDownLatch latchAfterBeginTx = new CountDownLatch(1); new Thread() { @Override public void run() { latchBeforeBeginTx.countDown(); Transaction tx2 = store.beginTx(); latchAfterBeginTx.countDown(); tx2.close(); } }.start(); assertTrue(latchBeforeBeginTx.await(1, TimeUnit.SECONDS)); long waitTime = 50 + duration * 10; assertFalse(latchAfterBeginTx.await(waitTime, TimeUnit.MILLISECONDS)); tx.close(); assertTrue(latchAfterBeginTx.await(waitTime, TimeUnit.MILLISECONDS)); }
private void insertTestEntities(String... texts) { Transaction transaction = store.beginTx(); Cursor<TestEntity> cursor = transaction.createCursor(TestEntity.class); for (String text : texts) { putEntity(cursor, text, 0); } cursor.close(); transaction.commitAndClose(); }
@Test public void testPutAndGet() { byte[] value = {23, 27, 42, 66}; Transaction transaction = store.beginTx(); KeyValueCursor cursor = transaction.createKeyValueCursor(); cursor.put(42, value); assertTrue(Arrays.equals(value, cursor.get(42))); cursor.close(); transaction.abort(); }
@Test public void testTransactionCommitAndAbort() { prepareOneEntryWith1230(); Transaction transaction = store.beginTx(); KeyValueCursor cursor = transaction.createKeyValueCursor(); cursor.put(123, new byte[]{3, 2, 1, 0}); assertArrayEquals(new byte[]{3, 2, 1, 0}, cursor.get(123)); cursor.close(); transaction.abort(); transaction = store.beginTx(); cursor = transaction.createKeyValueCursor(); assertArrayEquals(new byte[]{1, 2, 3, 0}, cursor.get(123)); cursor.close(); transaction.abort(); }
@Test(expected = IllegalArgumentException.class) public void testPutEntityWithInvalidId() { TestEntity entity = new TestEntity(); entity.setId(777); Transaction transaction = store.beginTx(); Cursor<TestEntity> cursor = transaction.createCursor(TestEntity.class); try { cursor.put(entity); } finally { cursor.close(); transaction.close(); } }
@Test public void testFirstLastNextPrev() { Transaction transaction = store.beginTx(); KeyValueCursor cursor = transaction.createKeyValueCursor(); cursor.put(1, new byte[]{1, 2, 3, 4}); cursor.put(2, new byte[]{2, 3, 4, 5}); cursor.put(4, new byte[]{4, 5, 6, 7}); cursor.put(8, new byte[]{8, 9, 10, 11, 12, 13, 14, 15}); assertTrue(Arrays.equals(new byte[]{1, 2, 3, 4}, cursor.getFirst())); assertTrue(Arrays.equals(new byte[]{2, 3, 4, 5}, cursor.getNext())); assertTrue(Arrays.equals(new byte[]{4, 5, 6, 7}, cursor.getNext())); assertTrue(Arrays.equals(new byte[]{2, 3, 4, 5}, cursor.getPrev())); // getLast is currently unsupported // assertTrue(Arrays.equals(new byte[]{8, 9, 10, 11, 12, 13}, cursor.getLast())); // assertTrue(Arrays.equals(new byte[]{4, 5, 6, 7, 8}, cursor.getPrev())); cursor.close(); transaction.abort(); }
private void prepareOneEntryWith1230() { // prepare the data Transaction transaction = store.beginTx(); KeyValueCursor cursor = transaction.createKeyValueCursor(); cursor.put(123, new byte[]{1, 2, 3, 0}); cursor.close(); assertEquals(true, transaction.isActive()); transaction.commit(); assertEquals(false, transaction.isActive()); }
@Test public void testRemove() { Transaction transaction = store.beginTx(); try { KeyValueCursor cursor = transaction.createKeyValueCursor(); cursor.put(1, new byte[]{1, 1, 0, 0}); cursor.put(2, new byte[]{2, 1, 0, 0}); cursor.put(4, new byte[]{4, 1, 0, 0}); assertTrue(cursor.removeAt(2)); // now 4 should be next to 1 assertTrue(cursor.seek(1)); byte[] next = cursor.getNext(); assertNotNull(next); assertTrue(Arrays.equals(new byte[]{4, 1, 0, 0}, next)); } finally { transaction.close(); } }
@Test public void testLookupKeyUsingIndex() throws IOException { insertTestEntities("find me", "not me"); Transaction transaction = store.beginTx(); Cursor<TestEntity> cursor = transaction.createCursor(TestEntity.class); assertEquals(2, cursor.lookupKeyUsingIndex(9, "not me")); assertEquals(1, cursor.lookupKeyUsingIndex(9, "find me")); assertEquals(0, cursor.lookupKeyUsingIndex(9, "peter pan")); cursor.close(); transaction.abort(); }
@Test public void testPutAndGetNext() { byte[] value = {23, 27, 42, 66}; byte[] value2 = {0xc, 0xa, 0xf, 0xe}; Transaction transaction = store.beginTx(); KeyValueCursor cursor = transaction.createKeyValueCursor(); cursor.put(23, value); assertEquals(23, cursor.getKey()); cursor.put(42, value2); assertEquals(42, cursor.getKey()); assertTrue(Arrays.equals(value, cursor.get(23))); assertEquals(23, cursor.getKey()); assertTrue(Arrays.equals(value2, cursor.getNext())); assertEquals(42, cursor.getKey()); cursor.close(); transaction.abort(); }
@Test public void testPutAndGetEntity() { TestEntity entity = new TestEntity(); entity.setSimpleInt(1977); Transaction transaction = store.beginTx(); Cursor<TestEntity> cursor = transaction.createCursor(TestEntity.class); long key = cursor.put(entity); TestEntity entityRead = cursor.get(key); assertNotNull(entityRead); assertEquals(1977, entityRead.getSimpleInt()); cursor.close(); transaction.abort(); }
@Test public void testLookupKeyUsingIndex_samePrefix() { insertTestEntities("aaa", "aa"); Transaction transaction = store.beginTx(); Cursor<TestEntity> cursor = transaction.createCursor(TestEntity.class); assertEquals(0, cursor.lookupKeyUsingIndex(9, "a")); assertEquals(2, cursor.lookupKeyUsingIndex(9, "aa")); assertEquals(1, cursor.lookupKeyUsingIndex(9, "aaa")); assertEquals(0, cursor.lookupKeyUsingIndex(9, "aaaa")); cursor.close(); transaction.abort(); }
@Test public void testPutSameIndexValue() { TestEntity entity = new TestEntity(); String value = "lulu321"; entity.setSimpleString(value); Transaction transaction = store.beginTx(); TestEntity read; try { Cursor<TestEntity> cursor = transaction.createCursor(TestEntity.class); long key = cursor.put(entity); // And again entity.setSimpleInt(1977); cursor.put(entity); assertEquals(key, cursor.lookupKeyUsingIndex(9, value)); read = cursor.get(key); cursor.close(); } finally { transaction.close(); } assertEquals(1977, read.getSimpleInt()); assertEquals(value, read.getSimpleString()); }
Transaction transaction = store.beginTx(); KeyValueCursor cursor = transaction.createKeyValueCursor(); cursor.put(123, new byte[]{3, 2, 1, 0});