Transaction multiGet convert to list-based (#9522)

Summary:
Transaction multiGet convert to list-based.

RocksDB Java (non-transactional) has multiGetAsList() methods to expose multiGet(). These return a list of results. These methods replaced multiGet() methods returning an array of results, which were deprecated in Rocks 6 and are being removed in Rocks 7.

The transactional API still presents multiGet() methods returning arrays, so in Rocks 7 we replace these with multiGetAsList()methods and deprecate the multiGet() methods.

This does not require any changes to the supporting JNI/C++ code, only to the wrappers which present the Java API.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/9522

Reviewed By: mrambacher

Differential Revision: D34114373

Pulled By: jay-zhuang

fbshipit-source-id: cb22d6095934d951b6aee4aed3e07923d3c18007
This commit is contained in:
Alan Paxton 2022-02-14 08:30:00 -08:00 committed by Facebook GitHub Bot
parent 479eb1aad6
commit eed71dfa82
4 changed files with 429 additions and 74 deletions

View File

@ -5,6 +5,8 @@
package org.rocksdb; package org.rocksdb;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
/** /**
@ -310,7 +312,7 @@ public class Transaction extends RocksObject {
/** /**
* This function is similar to * This function is similar to
* {@link RocksDB#multiGet(ReadOptions, List, List)} except it will * {@link RocksDB#multiGetAsList} except it will
* also read pending changes in this transaction. * also read pending changes in this transaction.
* Currently, this function will return Status::MergeInProgress if the most * Currently, this function will return Status::MergeInProgress if the most
* recent write to the queried key in this batch is a Merge. * recent write to the queried key in this batch is a Merge.
@ -336,9 +338,10 @@ public class Transaction extends RocksObject {
* @throws IllegalArgumentException thrown if the size of passed keys is not * @throws IllegalArgumentException thrown if the size of passed keys is not
* equal to the amount of passed column family handles. * equal to the amount of passed column family handles.
*/ */
@Deprecated
public byte[][] multiGet(final ReadOptions readOptions, public byte[][] multiGet(final ReadOptions readOptions,
final List<ColumnFamilyHandle> columnFamilyHandles, final List<ColumnFamilyHandle> columnFamilyHandles, final byte[][] keys)
final byte[][] keys) throws RocksDBException { throws RocksDBException {
assert(isOwningHandle()); assert(isOwningHandle());
// Check if key size equals cfList size. If not a exception must be // Check if key size equals cfList size. If not a exception must be
// thrown. If not a Segmentation fault happens. // thrown. If not a Segmentation fault happens.
@ -360,7 +363,57 @@ public class Transaction extends RocksObject {
/** /**
* This function is similar to * This function is similar to
* {@link RocksDB#multiGet(ReadOptions, List)} except it will * {@link RocksDB#multiGetAsList(ReadOptions, List, List)} except it will
* also read pending changes in this transaction.
* Currently, this function will return Status::MergeInProgress if the most
* recent write to the queried key in this batch is a Merge.
*
* If {@link ReadOptions#snapshot()} is not set, the current version of the
* key will be read. Calling {@link #setSnapshot()} does not affect the
* version of the data returned.
*
* Note that setting {@link ReadOptions#setSnapshot(Snapshot)} will affect
* what is read from the DB but will NOT change which keys are read from this
* transaction (the keys in this transaction do not yet belong to any snapshot
* and will be fetched regardless).
*
* @param readOptions Read options.
* @param columnFamilyHandles {@link java.util.List} containing
* {@link org.rocksdb.ColumnFamilyHandle} instances.
* @param keys of keys for which values need to be retrieved.
*
* @return Array of values, one for each key
*
* @throws RocksDBException thrown if error happens in underlying
* native library.
* @throws IllegalArgumentException thrown if the size of passed keys is not
* equal to the amount of passed column family handles.
*/
public List<byte[]> multiGetAsList(final ReadOptions readOptions,
final List<ColumnFamilyHandle> columnFamilyHandles, final List<byte[]> keys)
throws RocksDBException {
assert (isOwningHandle());
// Check if key size equals cfList size. If not a exception must be
// thrown. If not a Segmentation fault happens.
if (keys.size() != columnFamilyHandles.size()) {
throw new IllegalArgumentException("For each key there must be a ColumnFamilyHandle.");
}
if (keys.size() == 0) {
return new ArrayList<>(0);
}
final byte[][] keysArray = keys.toArray(new byte[keys.size()][]);
final long[] cfHandles = new long[columnFamilyHandles.size()];
for (int i = 0; i < columnFamilyHandles.size(); i++) {
cfHandles[i] = columnFamilyHandles.get(i).nativeHandle_;
}
return Arrays.asList(multiGet(nativeHandle_, readOptions.nativeHandle_, keysArray, cfHandles));
}
/**
* This function is similar to
* {@link RocksDB#multiGetAsList} except it will
* also read pending changes in this transaction. * also read pending changes in this transaction.
* Currently, this function will return Status::MergeInProgress if the most * Currently, this function will return Status::MergeInProgress if the most
* recent write to the queried key in this batch is a Merge. * recent write to the queried key in this batch is a Merge.
@ -383,8 +436,9 @@ public class Transaction extends RocksObject {
* @throws RocksDBException thrown if error happens in underlying * @throws RocksDBException thrown if error happens in underlying
* native library. * native library.
*/ */
public byte[][] multiGet(final ReadOptions readOptions, @Deprecated
final byte[][] keys) throws RocksDBException { public byte[][] multiGet(final ReadOptions readOptions, final byte[][] keys)
throws RocksDBException {
assert(isOwningHandle()); assert(isOwningHandle());
if(keys.length == 0) { if(keys.length == 0) {
return new byte[0][0]; return new byte[0][0];
@ -394,6 +448,41 @@ public class Transaction extends RocksObject {
keys); keys);
} }
/**
* This function is similar to
* {@link RocksDB#multiGetAsList} except it will
* also read pending changes in this transaction.
* Currently, this function will return Status::MergeInProgress if the most
* recent write to the queried key in this batch is a Merge.
*
* If {@link ReadOptions#snapshot()} is not set, the current version of the
* key will be read. Calling {@link #setSnapshot()} does not affect the
* version of the data returned.
*
* Note that setting {@link ReadOptions#setSnapshot(Snapshot)} will affect
* what is read from the DB but will NOT change which keys are read from this
* transaction (the keys in this transaction do not yet belong to any snapshot
* and will be fetched regardless).
*
* @param readOptions Read options.=
* {@link org.rocksdb.ColumnFamilyHandle} instances.
* @param keys of keys for which values need to be retrieved.
*
* @return Array of values, one for each key
*
* @throws RocksDBException thrown if error happens in underlying
* native library.
*/
public List<byte[]> multiGetAsList(final ReadOptions readOptions, final List<byte[]> keys)
throws RocksDBException {
if (keys.size() == 0) {
return new ArrayList<>(0);
}
final byte[][] keysArray = keys.toArray(new byte[keys.size()][]);
return Arrays.asList(multiGet(nativeHandle_, readOptions.nativeHandle_, keysArray));
}
/** /**
* Read this key and ensure that this transaction will only * Read this key and ensure that this transaction will only
* be able to be committed if this key is not written outside this * be able to be committed if this key is not written outside this
@ -541,9 +630,10 @@ public class Transaction extends RocksObject {
* @throws RocksDBException thrown if error happens in underlying * @throws RocksDBException thrown if error happens in underlying
* native library. * native library.
*/ */
@Deprecated
public byte[][] multiGetForUpdate(final ReadOptions readOptions, public byte[][] multiGetForUpdate(final ReadOptions readOptions,
final List<ColumnFamilyHandle> columnFamilyHandles, final List<ColumnFamilyHandle> columnFamilyHandles, final byte[][] keys)
final byte[][] keys) throws RocksDBException { throws RocksDBException {
assert(isOwningHandle()); assert(isOwningHandle());
// Check if key size equals cfList size. If not a exception must be // Check if key size equals cfList size. If not a exception must be
// thrown. If not a Segmentation fault happens. // thrown. If not a Segmentation fault happens.
@ -562,6 +652,43 @@ public class Transaction extends RocksObject {
keys, cfHandles); keys, cfHandles);
} }
/**
* A multi-key version of
* {@link #getForUpdate(ReadOptions, ColumnFamilyHandle, byte[], boolean)}.
*
*
* @param readOptions Read options.
* @param columnFamilyHandles {@link org.rocksdb.ColumnFamilyHandle}
* instances
* @param keys the keys to retrieve the values for.
*
* @return Array of values, one for each key
*
* @throws RocksDBException thrown if error happens in underlying
* native library.
*/
public List<byte[]> multiGetForUpdateAsList(final ReadOptions readOptions,
final List<ColumnFamilyHandle> columnFamilyHandles, final List<byte[]> keys)
throws RocksDBException {
assert (isOwningHandle());
// Check if key size equals cfList size. If not a exception must be
// thrown. If not a Segmentation fault happens.
if (keys.size() != columnFamilyHandles.size()) {
throw new IllegalArgumentException("For each key there must be a ColumnFamilyHandle.");
}
if (keys.size() == 0) {
return new ArrayList<>();
}
final byte[][] keysArray = keys.toArray(new byte[keys.size()][]);
final long[] cfHandles = new long[columnFamilyHandles.size()];
for (int i = 0; i < columnFamilyHandles.size(); i++) {
cfHandles[i] = columnFamilyHandles.get(i).nativeHandle_;
}
return Arrays.asList(
multiGetForUpdate(nativeHandle_, readOptions.nativeHandle_, keysArray, cfHandles));
}
/** /**
* A multi-key version of {@link #getForUpdate(ReadOptions, byte[], boolean)}. * A multi-key version of {@link #getForUpdate(ReadOptions, byte[], boolean)}.
* *
@ -574,8 +701,9 @@ public class Transaction extends RocksObject {
* @throws RocksDBException thrown if error happens in underlying * @throws RocksDBException thrown if error happens in underlying
* native library. * native library.
*/ */
public byte[][] multiGetForUpdate(final ReadOptions readOptions, @Deprecated
final byte[][] keys) throws RocksDBException { public byte[][] multiGetForUpdate(final ReadOptions readOptions, final byte[][] keys)
throws RocksDBException {
assert(isOwningHandle()); assert(isOwningHandle());
if(keys.length == 0) { if(keys.length == 0) {
return new byte[0][0]; return new byte[0][0];
@ -585,6 +713,30 @@ public class Transaction extends RocksObject {
readOptions.nativeHandle_, keys); readOptions.nativeHandle_, keys);
} }
/**
* A multi-key version of {@link #getForUpdate(ReadOptions, byte[], boolean)}.
*
*
* @param readOptions Read options.
* @param keys the keys to retrieve the values for.
*
* @return List of values, one for each key
*
* @throws RocksDBException thrown if error happens in underlying
* native library.
*/
public List<byte[]> multiGetForUpdateAsList(
final ReadOptions readOptions, final List<byte[]> keys) throws RocksDBException {
assert (isOwningHandle());
if (keys.size() == 0) {
return new ArrayList<>(0);
}
final byte[][] keysArray = keys.toArray(new byte[keys.size()][]);
return Arrays.asList(multiGetForUpdate(nativeHandle_, readOptions.nativeHandle_, keysArray));
}
/** /**
* Returns an iterator that will iterate on all keys in the default * Returns an iterator that will iterate on all keys in the default
* column family including both keys in the DB and uncommitted keys in this * column family including both keys in the DB and uncommitted keys in this

View File

@ -206,12 +206,8 @@ public abstract class AbstractTransactionTest {
@Test @Test
public void multiGetPut_cf() throws RocksDBException { public void multiGetPut_cf() throws RocksDBException {
final byte keys[][] = new byte[][] { final byte[][] keys = new byte[][] {"key1".getBytes(UTF_8), "key2".getBytes(UTF_8)};
"key1".getBytes(UTF_8), final byte[][] values = new byte[][] {"value1".getBytes(UTF_8), "value2".getBytes(UTF_8)};
"key2".getBytes(UTF_8)};
final byte values[][] = new byte[][] {
"value1".getBytes(UTF_8),
"value2".getBytes(UTF_8)};
try(final DBContainer dbContainer = startDb(); try(final DBContainer dbContainer = startDb();
final ReadOptions readOptions = new ReadOptions(); final ReadOptions readOptions = new ReadOptions();
@ -227,14 +223,31 @@ public abstract class AbstractTransactionTest {
} }
} }
@Test
public void multiGetPutAsList_cf() throws RocksDBException {
final byte[][] keys = new byte[][] {"key1".getBytes(UTF_8), "key2".getBytes(UTF_8)};
final byte[][] values = new byte[][] {"value1".getBytes(UTF_8), "value2".getBytes(UTF_8)};
try (final DBContainer dbContainer = startDb();
final ReadOptions readOptions = new ReadOptions();
final Transaction txn = dbContainer.beginTransaction()) {
final ColumnFamilyHandle testCf = dbContainer.getTestColumnFamily();
final List<ColumnFamilyHandle> cfList = Arrays.asList(testCf, testCf);
assertThat(txn.multiGetAsList(readOptions, cfList, Arrays.asList(keys)))
.containsExactly(null, null);
txn.put(testCf, keys[0], values[0]);
txn.put(testCf, keys[1], values[1]);
assertThat(txn.multiGetAsList(readOptions, cfList, Arrays.asList(keys)))
.containsExactly(values);
}
}
@Test @Test
public void multiGetPut() throws RocksDBException { public void multiGetPut() throws RocksDBException {
final byte keys[][] = new byte[][] { final byte[][] keys = new byte[][] {"key1".getBytes(UTF_8), "key2".getBytes(UTF_8)};
"key1".getBytes(UTF_8), final byte[][] values = new byte[][] {"value1".getBytes(UTF_8), "value2".getBytes(UTF_8)};
"key2".getBytes(UTF_8)};
final byte values[][] = new byte[][] {
"value1".getBytes(UTF_8),
"value2".getBytes(UTF_8)};
try(final DBContainer dbContainer = startDb(); try(final DBContainer dbContainer = startDb();
final ReadOptions readOptions = new ReadOptions(); final ReadOptions readOptions = new ReadOptions();
@ -248,6 +261,22 @@ public abstract class AbstractTransactionTest {
} }
} }
@Test
public void multiGetPutAsList() throws RocksDBException {
final byte[][] keys = new byte[][] {"key1".getBytes(UTF_8), "key2".getBytes(UTF_8)};
final byte[][] values = new byte[][] {"value1".getBytes(UTF_8), "value2".getBytes(UTF_8)};
try (final DBContainer dbContainer = startDb();
final ReadOptions readOptions = new ReadOptions();
final Transaction txn = dbContainer.beginTransaction()) {
assertThat(txn.multiGetAsList(readOptions, Arrays.asList(keys))).containsExactly(null, null);
txn.put(keys[0], values[0]);
txn.put(keys[1], values[1]);
assertThat(txn.multiGetAsList(readOptions, Arrays.asList(keys))).containsExactly(values);
}
}
@Test @Test
public void getForUpdate_cf() throws RocksDBException { public void getForUpdate_cf() throws RocksDBException {
final byte k1[] = "key1".getBytes(UTF_8); final byte k1[] = "key1".getBytes(UTF_8);
@ -495,6 +524,7 @@ public abstract class AbstractTransactionTest {
} }
} }
@Deprecated
@Test @Test
public void multiGetPutUntracked_cf() throws RocksDBException { public void multiGetPutUntracked_cf() throws RocksDBException {
final byte keys[][] = new byte[][] { final byte keys[][] = new byte[][] {
@ -518,14 +548,32 @@ public abstract class AbstractTransactionTest {
} }
} }
@Test
public void multiGetPutUntrackedAsList_cf() throws RocksDBException {
final byte[][] keys = new byte[][] {"key1".getBytes(UTF_8), "key2".getBytes(UTF_8)};
final byte[][] values = new byte[][] {"value1".getBytes(UTF_8), "value2".getBytes(UTF_8)};
try (final DBContainer dbContainer = startDb();
final ReadOptions readOptions = new ReadOptions();
final Transaction txn = dbContainer.beginTransaction()) {
final ColumnFamilyHandle testCf = dbContainer.getTestColumnFamily();
final List<ColumnFamilyHandle> cfList = Arrays.asList(testCf, testCf);
assertThat(txn.multiGetAsList(readOptions, cfList, Arrays.asList(keys)))
.containsExactly(null, null);
txn.putUntracked(testCf, keys[0], values[0]);
txn.putUntracked(testCf, keys[1], values[1]);
assertThat(txn.multiGetAsList(readOptions, cfList, Arrays.asList(keys)))
.containsExactly(values);
}
}
@Deprecated
@Test @Test
public void multiGetPutUntracked() throws RocksDBException { public void multiGetPutUntracked() throws RocksDBException {
final byte keys[][] = new byte[][] { final byte[][] keys = new byte[][] {"key1".getBytes(UTF_8), "key2".getBytes(UTF_8)};
"key1".getBytes(UTF_8), final byte[][] values = new byte[][] {"value1".getBytes(UTF_8), "value2".getBytes(UTF_8)};
"key2".getBytes(UTF_8)};
final byte values[][] = new byte[][] {
"value1".getBytes(UTF_8),
"value2".getBytes(UTF_8)};
try(final DBContainer dbContainer = startDb(); try(final DBContainer dbContainer = startDb();
final ReadOptions readOptions = new ReadOptions(); final ReadOptions readOptions = new ReadOptions();
@ -538,6 +586,21 @@ public abstract class AbstractTransactionTest {
} }
} }
@Test
public void multiGetPutAsListUntracked() throws RocksDBException {
final byte[][] keys = new byte[][] {"key1".getBytes(UTF_8), "key2".getBytes(UTF_8)};
final byte[][] values = new byte[][] {"value1".getBytes(UTF_8), "value2".getBytes(UTF_8)};
try (final DBContainer dbContainer = startDb();
final ReadOptions readOptions = new ReadOptions();
final Transaction txn = dbContainer.beginTransaction()) {
assertThat(txn.multiGetAsList(readOptions, Arrays.asList(keys))).containsExactly(null, null);
txn.putUntracked(keys[0], values[0]);
txn.putUntracked(keys[1], values[1]);
assertThat(txn.multiGetAsList(readOptions, Arrays.asList(keys))).containsExactly(values);
}
}
@Test @Test
public void mergeUntracked_cf() throws RocksDBException { public void mergeUntracked_cf() throws RocksDBException {
final byte[] k1 = "key1".getBytes(UTF_8); final byte[] k1 = "key1".getBytes(UTF_8);

View File

@ -19,9 +19,9 @@ public class OptimisticTransactionTest extends AbstractTransactionTest {
@Test @Test
public void getForUpdate_cf_conflict() throws RocksDBException { public void getForUpdate_cf_conflict() throws RocksDBException {
final byte k1[] = "key1".getBytes(UTF_8); final byte[] k1 = "key1".getBytes(UTF_8);
final byte v1[] = "value1".getBytes(UTF_8); final byte[] v1 = "value1".getBytes(UTF_8);
final byte v12[] = "value12".getBytes(UTF_8); final byte[] v12 = "value12".getBytes(UTF_8);
try(final DBContainer dbContainer = startDb(); try(final DBContainer dbContainer = startDb();
final ReadOptions readOptions = new ReadOptions()) { final ReadOptions readOptions = new ReadOptions()) {
final ColumnFamilyHandle testCf = dbContainer.getTestColumnFamily(); final ColumnFamilyHandle testCf = dbContainer.getTestColumnFamily();
@ -57,9 +57,9 @@ public class OptimisticTransactionTest extends AbstractTransactionTest {
@Test @Test
public void getForUpdate_conflict() throws RocksDBException { public void getForUpdate_conflict() throws RocksDBException {
final byte k1[] = "key1".getBytes(UTF_8); final byte[] k1 = "key1".getBytes(UTF_8);
final byte v1[] = "value1".getBytes(UTF_8); final byte[] v1 = "value1".getBytes(UTF_8);
final byte v12[] = "value12".getBytes(UTF_8); final byte[] v12 = "value12".getBytes(UTF_8);
try(final DBContainer dbContainer = startDb(); try(final DBContainer dbContainer = startDb();
final ReadOptions readOptions = new ReadOptions()) { final ReadOptions readOptions = new ReadOptions()) {
@ -92,14 +92,11 @@ public class OptimisticTransactionTest extends AbstractTransactionTest {
} }
} }
@Deprecated
@Test @Test
public void multiGetForUpdate_cf_conflict() throws RocksDBException { public void multiGetForUpdate_cf_conflict() throws RocksDBException {
final byte keys[][] = new byte[][] { final byte[][] keys = new byte[][] {"key1".getBytes(UTF_8), "key2".getBytes(UTF_8)};
"key1".getBytes(UTF_8), final byte[][] values = new byte[][] {"value1".getBytes(UTF_8), "value2".getBytes(UTF_8)};
"key2".getBytes(UTF_8)};
final byte values[][] = new byte[][] {
"value1".getBytes(UTF_8),
"value2".getBytes(UTF_8)};
final byte[] otherValue = "otherValue".getBytes(UTF_8); final byte[] otherValue = "otherValue".getBytes(UTF_8);
try(final DBContainer dbContainer = startDb(); try(final DBContainer dbContainer = startDb();
@ -139,14 +136,54 @@ public class OptimisticTransactionTest extends AbstractTransactionTest {
} }
} }
@Test
public void multiGetAsListForUpdate_cf_conflict() throws RocksDBException {
final byte[][] keys = new byte[][] {"key1".getBytes(UTF_8), "key2".getBytes(UTF_8)};
final byte[][] values = new byte[][] {"value1".getBytes(UTF_8), "value2".getBytes(UTF_8)};
final byte[] otherValue = "otherValue".getBytes(UTF_8);
try (final DBContainer dbContainer = startDb();
final ReadOptions readOptions = new ReadOptions()) {
final ColumnFamilyHandle testCf = dbContainer.getTestColumnFamily();
final List<ColumnFamilyHandle> cfList = Arrays.asList(testCf, testCf);
try (final Transaction txn = dbContainer.beginTransaction()) {
txn.put(testCf, keys[0], values[0]);
txn.put(testCf, keys[1], values[1]);
assertThat(txn.multiGetAsList(readOptions, cfList, Arrays.asList(keys)))
.containsExactly(values);
txn.commit();
}
try (final Transaction txn2 = dbContainer.beginTransaction()) {
try (final Transaction txn3 = dbContainer.beginTransaction()) {
assertThat(txn3.multiGetForUpdateAsList(readOptions, cfList, Arrays.asList(keys)))
.containsExactly(values);
// NOTE: txn2 updates k1, during txn3
txn2.put(testCf, keys[0], otherValue);
assertThat(txn2.get(testCf, readOptions, keys[0])).isEqualTo(otherValue);
txn2.commit();
try {
txn3.commit(); // should cause an exception!
} catch (final RocksDBException e) {
assertThat(e.getStatus().getCode()).isSameAs(Status.Code.Busy);
return;
}
}
}
fail("Expected an exception for put after getForUpdate from conflicting"
+ "transactions");
}
}
@Deprecated
@Test @Test
public void multiGetForUpdate_conflict() throws RocksDBException { public void multiGetForUpdate_conflict() throws RocksDBException {
final byte keys[][] = new byte[][] { final byte[][] keys = new byte[][] {"key1".getBytes(UTF_8), "key2".getBytes(UTF_8)};
"key1".getBytes(UTF_8), final byte[][] values = new byte[][] {"value1".getBytes(UTF_8), "value2".getBytes(UTF_8)};
"key2".getBytes(UTF_8)};
final byte values[][] = new byte[][] {
"value1".getBytes(UTF_8),
"value2".getBytes(UTF_8)};
final byte[] otherValue = "otherValue".getBytes(UTF_8); final byte[] otherValue = "otherValue".getBytes(UTF_8);
try(final DBContainer dbContainer = startDb(); try(final DBContainer dbContainer = startDb();
@ -183,11 +220,50 @@ public class OptimisticTransactionTest extends AbstractTransactionTest {
} }
} }
@Test
public void multiGetasListForUpdate_conflict() throws RocksDBException {
final byte[][] keys = new byte[][] {"key1".getBytes(UTF_8), "key2".getBytes(UTF_8)};
final byte[][] values = new byte[][] {"value1".getBytes(UTF_8), "value2".getBytes(UTF_8)};
final byte[] otherValue = "otherValue".getBytes(UTF_8);
try (final DBContainer dbContainer = startDb();
final ReadOptions readOptions = new ReadOptions()) {
try (final Transaction txn = dbContainer.beginTransaction()) {
txn.put(keys[0], values[0]);
txn.put(keys[1], values[1]);
assertThat(txn.multiGetAsList(readOptions, Arrays.asList(keys))).containsExactly(values);
txn.commit();
}
try (final Transaction txn2 = dbContainer.beginTransaction()) {
try (final Transaction txn3 = dbContainer.beginTransaction()) {
assertThat(txn3.multiGetForUpdateAsList(readOptions, Arrays.asList(keys)))
.containsExactly(values);
// NOTE: txn2 updates k1, during txn3
txn2.put(keys[0], otherValue);
assertThat(txn2.get(readOptions, keys[0])).isEqualTo(otherValue);
txn2.commit();
try {
txn3.commit(); // should cause an exception!
} catch (final RocksDBException e) {
assertThat(e.getStatus().getCode()).isSameAs(Status.Code.Busy);
return;
}
}
}
fail("Expected an exception for put after getForUpdate from conflicting"
+ "transactions");
}
}
@Test @Test
public void undoGetForUpdate_cf_conflict() throws RocksDBException { public void undoGetForUpdate_cf_conflict() throws RocksDBException {
final byte k1[] = "key1".getBytes(UTF_8); final byte[] k1 = "key1".getBytes(UTF_8);
final byte v1[] = "value1".getBytes(UTF_8); final byte[] v1 = "value1".getBytes(UTF_8);
final byte v12[] = "value12".getBytes(UTF_8); final byte[] v12 = "value12".getBytes(UTF_8);
try(final DBContainer dbContainer = startDb(); try(final DBContainer dbContainer = startDb();
final ReadOptions readOptions = new ReadOptions()) { final ReadOptions readOptions = new ReadOptions()) {
final ColumnFamilyHandle testCf = dbContainer.getTestColumnFamily(); final ColumnFamilyHandle testCf = dbContainer.getTestColumnFamily();
@ -220,9 +296,9 @@ public class OptimisticTransactionTest extends AbstractTransactionTest {
@Test @Test
public void undoGetForUpdate_conflict() throws RocksDBException { public void undoGetForUpdate_conflict() throws RocksDBException {
final byte k1[] = "key1".getBytes(UTF_8); final byte[] k1 = "key1".getBytes(UTF_8);
final byte v1[] = "value1".getBytes(UTF_8); final byte[] v1 = "value1".getBytes(UTF_8);
final byte v12[] = "value12".getBytes(UTF_8); final byte[] v12 = "value12".getBytes(UTF_8);
try(final DBContainer dbContainer = startDb(); try(final DBContainer dbContainer = startDb();
final ReadOptions readOptions = new ReadOptions()) { final ReadOptions readOptions = new ReadOptions()) {
@ -261,12 +337,10 @@ public class OptimisticTransactionTest extends AbstractTransactionTest {
try { try {
txn.setName(name); txn.setName(name);
} catch(final RocksDBException e) {
assertThat(e.getStatus().getCode() == Status.Code.InvalidArgument);
return;
}
fail("Optimistic transactions cannot be named."); fail("Optimistic transactions cannot be named.");
} catch(final RocksDBException e) {
assertThat(e.getStatus().getCode()).isEqualTo(Status.Code.InvalidArgument);
}
} }
} }

View File

@ -19,9 +19,9 @@ public class TransactionTest extends AbstractTransactionTest {
@Test @Test
public void getForUpdate_cf_conflict() throws RocksDBException { public void getForUpdate_cf_conflict() throws RocksDBException {
final byte k1[] = "key1".getBytes(UTF_8); final byte[] k1 = "key1".getBytes(UTF_8);
final byte v1[] = "value1".getBytes(UTF_8); final byte[] v1 = "value1".getBytes(UTF_8);
final byte v12[] = "value12".getBytes(UTF_8); final byte[] v12 = "value12".getBytes(UTF_8);
try(final DBContainer dbContainer = startDb(); try(final DBContainer dbContainer = startDb();
final ReadOptions readOptions = new ReadOptions()) { final ReadOptions readOptions = new ReadOptions()) {
final ColumnFamilyHandle testCf = dbContainer.getTestColumnFamily(); final ColumnFamilyHandle testCf = dbContainer.getTestColumnFamily();
@ -53,9 +53,9 @@ public class TransactionTest extends AbstractTransactionTest {
@Test @Test
public void getForUpdate_conflict() throws RocksDBException { public void getForUpdate_conflict() throws RocksDBException {
final byte k1[] = "key1".getBytes(UTF_8); final byte[] k1 = "key1".getBytes(UTF_8);
final byte v1[] = "value1".getBytes(UTF_8); final byte[] v1 = "value1".getBytes(UTF_8);
final byte v12[] = "value12".getBytes(UTF_8); final byte[] v12 = "value12".getBytes(UTF_8);
try(final DBContainer dbContainer = startDb(); try(final DBContainer dbContainer = startDb();
final ReadOptions readOptions = new ReadOptions()) { final ReadOptions readOptions = new ReadOptions()) {
@ -86,12 +86,8 @@ public class TransactionTest extends AbstractTransactionTest {
@Test @Test
public void multiGetForUpdate_cf_conflict() throws RocksDBException { public void multiGetForUpdate_cf_conflict() throws RocksDBException {
final byte keys[][] = new byte[][] { final byte[][] keys = new byte[][] {"key1".getBytes(UTF_8), "key2".getBytes(UTF_8)};
"key1".getBytes(UTF_8), final byte[][] values = new byte[][] {"value1".getBytes(UTF_8), "value2".getBytes(UTF_8)};
"key2".getBytes(UTF_8)};
final byte values[][] = new byte[][] {
"value1".getBytes(UTF_8),
"value2".getBytes(UTF_8)};
final byte[] otherValue = "otherValue".getBytes(UTF_8); final byte[] otherValue = "otherValue".getBytes(UTF_8);
try(final DBContainer dbContainer = startDb(); try(final DBContainer dbContainer = startDb();
@ -126,14 +122,49 @@ public class TransactionTest extends AbstractTransactionTest {
} }
} }
@Test
public void multiGetAsListForUpdate_cf_conflict() throws RocksDBException {
final byte[][] keys = new byte[][] {"key1".getBytes(UTF_8), "key2".getBytes(UTF_8)};
final byte[][] values = new byte[][] {"value1".getBytes(UTF_8), "value2".getBytes(UTF_8)};
final byte[] otherValue = "otherValue".getBytes(UTF_8);
try (final DBContainer dbContainer = startDb();
final ReadOptions readOptions = new ReadOptions()) {
final ColumnFamilyHandle testCf = dbContainer.getTestColumnFamily();
final List<ColumnFamilyHandle> cfList = Arrays.asList(testCf, testCf);
try (final Transaction txn = dbContainer.beginTransaction()) {
txn.put(testCf, keys[0], values[0]);
txn.put(testCf, keys[1], values[1]);
assertThat(txn.multiGetAsList(readOptions, cfList, Arrays.asList(keys)))
.containsExactly(values);
txn.commit();
}
try (final Transaction txn2 = dbContainer.beginTransaction()) {
try (final Transaction txn3 = dbContainer.beginTransaction()) {
assertThat(txn3.multiGetForUpdateAsList(readOptions, cfList, Arrays.asList(keys)))
.containsExactly(values);
// NOTE: txn2 updates k1, during txn3
try {
txn2.put(testCf, keys[0], otherValue); // should cause an exception!
} catch (final RocksDBException e) {
assertThat(e.getStatus().getCode()).isSameAs(Status.Code.TimedOut);
return;
}
}
}
fail("Expected an exception for put after getForUpdate from conflicting"
+ "transactions");
}
}
@Test @Test
public void multiGetForUpdate_conflict() throws RocksDBException { public void multiGetForUpdate_conflict() throws RocksDBException {
final byte keys[][] = new byte[][] { final byte[][] keys = new byte[][] {"key1".getBytes(UTF_8), "key2".getBytes(UTF_8)};
"key1".getBytes(UTF_8), final byte[][] values = new byte[][] {"value1".getBytes(UTF_8), "value2".getBytes(UTF_8)};
"key2".getBytes(UTF_8)};
final byte values[][] = new byte[][] {
"value1".getBytes(UTF_8),
"value2".getBytes(UTF_8)};
final byte[] otherValue = "otherValue".getBytes(UTF_8); final byte[] otherValue = "otherValue".getBytes(UTF_8);
try(final DBContainer dbContainer = startDb(); try(final DBContainer dbContainer = startDb();
@ -150,6 +181,41 @@ public class TransactionTest extends AbstractTransactionTest {
assertThat(txn3.multiGetForUpdate(readOptions, keys)) assertThat(txn3.multiGetForUpdate(readOptions, keys))
.isEqualTo(values); .isEqualTo(values);
// NOTE: txn2 updates k1, during txn3
try {
txn2.put(keys[0], otherValue); // should cause an exception!
} catch (final RocksDBException e) {
assertThat(e.getStatus().getCode()).isSameAs(Status.Code.TimedOut);
return;
}
}
}
fail("Expected an exception for put after getForUpdate from conflicting"
+ "transactions");
}
}
@Test
public void multiGetAsListForUpdate_conflict() throws RocksDBException {
final byte[][] keys = new byte[][] {"key1".getBytes(UTF_8), "key2".getBytes(UTF_8)};
final byte[][] values = new byte[][] {"value1".getBytes(UTF_8), "value2".getBytes(UTF_8)};
final byte[] otherValue = "otherValue".getBytes(UTF_8);
try (final DBContainer dbContainer = startDb();
final ReadOptions readOptions = new ReadOptions()) {
try (final Transaction txn = dbContainer.beginTransaction()) {
txn.put(keys[0], values[0]);
txn.put(keys[1], values[1]);
assertThat(txn.multiGetAsList(readOptions, Arrays.asList(keys))).containsExactly(values);
txn.commit();
}
try (final Transaction txn2 = dbContainer.beginTransaction()) {
try (final Transaction txn3 = dbContainer.beginTransaction()) {
assertThat(txn3.multiGetForUpdateAsList(readOptions, Arrays.asList(keys)))
.containsExactly(values);
// NOTE: txn2 updates k1, during txn3 // NOTE: txn2 updates k1, during txn3
try { try {
txn2.put(keys[0], otherValue); // should cause an exception! txn2.put(keys[0], otherValue); // should cause an exception!