[PAN-2871] Columnar rocksdb (#1599)

* Columnated storage to allow for iteration over world state
* change MetricsCategory to PantheonMetricsCategory
* consistency renaming of kvstores

Signed-off-by: Adrian Sutton <adrian.sutton@consensys.net>
pull/2/head
Ratan Rai Sur 5 years ago committed by GitHub
parent ee20ed426d
commit d4a016798e
  1. 4
      ethereum/blockcreation/src/test/java/tech/pegasys/pantheon/ethereum/blockcreation/BlockTransactionSelectorTest.java
  2. 5
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/chain/GenesisState.java
  3. 4
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/privacy/PrivateTransactionKeyValueStorage.java
  4. 41
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/storage/keyvalue/KeyValueStorageProvider.java
  5. 61
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/storage/keyvalue/RocksDbStorageProvider.java
  6. 38
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/storage/keyvalue/WorldStateKeyValueStorage.java
  7. 4
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/worldstate/DebuggableMutableWorldState.java
  8. 12
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/worldstate/WorldStateStorage.java
  9. 5
      ethereum/core/src/test-support/java/tech/pegasys/pantheon/ethereum/core/ExecutionContextTestFixture.java
  10. 11
      ethereum/core/src/test-support/java/tech/pegasys/pantheon/ethereum/core/InMemoryStorageProvider.java
  11. 8
      ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/chain/GenesisStateTest.java
  12. 34
      ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/storage/keyvalue/KeyValueStorageWorldStateStorageTest.java
  13. 4
      ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/vm/operations/ExtCodeHashOperationTest.java
  14. 12
      ethereum/core/src/test/java/tech/pegasys/pantheon/ethereum/worldstate/DefaultMutableWorldStateTest.java
  15. 4
      ethereum/eth/src/jmh/java/tech/pegasys/pantheon/ethereum/eth/sync/worldstate/WorldStateDownloaderBenchmark.java
  16. 4
      ethereum/eth/src/test/java/tech/pegasys/pantheon/ethereum/eth/sync/worldstate/WorldDownloadStateTest.java
  17. 39
      ethereum/eth/src/test/java/tech/pegasys/pantheon/ethereum/eth/sync/worldstate/WorldStateDownloaderTest.java
  18. 326
      services/kvstore/src/main/java/tech/pegasys/pantheon/services/kvstore/ColumnarRocksDbKeyValueStorage.java
  19. 34
      services/kvstore/src/main/java/tech/pegasys/pantheon/services/kvstore/InMemoryKeyValueStorage.java
  20. 9
      services/kvstore/src/main/java/tech/pegasys/pantheon/services/kvstore/KeyValueStorage.java
  21. 29
      services/kvstore/src/main/java/tech/pegasys/pantheon/services/kvstore/RocksDbConfiguration.java
  22. 39
      services/kvstore/src/main/java/tech/pegasys/pantheon/services/kvstore/RocksDbKeyValueStorage.java
  23. 143
      services/kvstore/src/main/java/tech/pegasys/pantheon/services/kvstore/SegmentedKeyValueStorage.java
  24. 82
      services/kvstore/src/main/java/tech/pegasys/pantheon/services/kvstore/SegmentedKeyValueStorageAdapter.java
  25. 112
      services/kvstore/src/test/java/tech/pegasys/pantheon/services/kvstore/ColumnarRocksDbKeyValueStorageTest.java
  26. 5
      services/kvstore/src/test/java/tech/pegasys/pantheon/services/kvstore/RocksDbKeyValueStorageTest.java

@ -43,7 +43,7 @@ import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule;
import tech.pegasys.pantheon.ethereum.mainnet.TransactionProcessor;
import tech.pegasys.pantheon.ethereum.mainnet.TransactionValidator.TransactionInvalidReason;
import tech.pegasys.pantheon.ethereum.mainnet.ValidationResult;
import tech.pegasys.pantheon.ethereum.storage.keyvalue.KeyValueStorageWorldStateStorage;
import tech.pegasys.pantheon.ethereum.storage.keyvalue.WorldStateKeyValueStorage;
import tech.pegasys.pantheon.ethereum.vm.TestBlockchain;
import tech.pegasys.pantheon.ethereum.worldstate.DefaultMutableWorldState;
import tech.pegasys.pantheon.metrics.MetricsSystem;
@ -528,6 +528,6 @@ public class BlockTransactionSelectorTest {
private DefaultMutableWorldState inMemoryWorldState() {
return new DefaultMutableWorldState(
new KeyValueStorageWorldStateStorage(new InMemoryKeyValueStorage()));
new WorldStateKeyValueStorage(new InMemoryKeyValueStorage()));
}
}

@ -27,7 +27,7 @@ import tech.pegasys.pantheon.ethereum.core.Wei;
import tech.pegasys.pantheon.ethereum.core.WorldUpdater;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule;
import tech.pegasys.pantheon.ethereum.mainnet.ScheduleBasedBlockHeaderFunctions;
import tech.pegasys.pantheon.ethereum.storage.keyvalue.KeyValueStorageWorldStateStorage;
import tech.pegasys.pantheon.ethereum.storage.keyvalue.WorldStateKeyValueStorage;
import tech.pegasys.pantheon.ethereum.worldstate.DefaultMutableWorldState;
import tech.pegasys.pantheon.services.kvstore.InMemoryKeyValueStorage;
import tech.pegasys.pantheon.util.bytes.BytesValue;
@ -119,8 +119,7 @@ public final class GenesisState {
private static Hash calculateGenesisStateHash(final List<GenesisAccount> genesisAccounts) {
final MutableWorldState worldState =
new DefaultMutableWorldState(
new KeyValueStorageWorldStateStorage(new InMemoryKeyValueStorage()));
new DefaultMutableWorldState(new WorldStateKeyValueStorage(new InMemoryKeyValueStorage()));
writeAccountsTo(worldState, genesisAccounts);
return worldState.rootHash();
}

@ -25,14 +25,14 @@ import tech.pegasys.pantheon.util.bytes.BytesValues;
import java.util.List;
import java.util.Optional;
public class PrivateKeyValueStorage implements PrivateTransactionStorage {
public class PrivateTransactionKeyValueStorage implements PrivateTransactionStorage {
private final KeyValueStorage keyValueStorage;
private static final BytesValue EVENTS_KEY_SUFFIX = BytesValue.of("EVENTS".getBytes(UTF_8));
private static final BytesValue OUTPUT_KEY_SUFFIX = BytesValue.of("OUTPUT".getBytes(UTF_8));
public PrivateKeyValueStorage(final KeyValueStorage keyValueStorage) {
public PrivateTransactionKeyValueStorage(final KeyValueStorage keyValueStorage) {
this.keyValueStorage = keyValueStorage;
}

@ -15,9 +15,9 @@ package tech.pegasys.pantheon.ethereum.storage.keyvalue;
import tech.pegasys.pantheon.ethereum.chain.BlockchainStorage;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule;
import tech.pegasys.pantheon.ethereum.mainnet.ScheduleBasedBlockHeaderFunctions;
import tech.pegasys.pantheon.ethereum.privacy.PrivateKeyValueStorage;
import tech.pegasys.pantheon.ethereum.privacy.PrivateStateKeyValueStorage;
import tech.pegasys.pantheon.ethereum.privacy.PrivateStateStorage;
import tech.pegasys.pantheon.ethereum.privacy.PrivateTransactionKeyValueStorage;
import tech.pegasys.pantheon.ethereum.privacy.PrivateTransactionStorage;
import tech.pegasys.pantheon.ethereum.storage.StorageProvider;
import tech.pegasys.pantheon.ethereum.worldstate.WorldStateStorage;
@ -27,35 +27,60 @@ import java.io.IOException;
public class KeyValueStorageProvider implements StorageProvider {
private final KeyValueStorage keyValueStorage;
private final KeyValueStorage blockchainStorage;
private final KeyValueStorage worldStateStorage;
private final KeyValueStorage privateTransactionStorage;
private final KeyValueStorage privateStateStorage;
private final KeyValueStorage pruningStorage;
public KeyValueStorageProvider(final KeyValueStorage keyValueStorage) {
this.keyValueStorage = keyValueStorage;
this(keyValueStorage, keyValueStorage, keyValueStorage, keyValueStorage, keyValueStorage);
}
public KeyValueStorageProvider(
final KeyValueStorage blockchainStorage,
final KeyValueStorage worldStateStorage,
final KeyValueStorage privateTransactionStorage,
final KeyValueStorage privateStateStorage,
final KeyValueStorage pruningStorage) {
this.blockchainStorage = blockchainStorage;
this.worldStateStorage = worldStateStorage;
this.privateTransactionStorage = privateTransactionStorage;
this.privateStateStorage = privateStateStorage;
this.pruningStorage = pruningStorage;
}
@Override
public BlockchainStorage createBlockchainStorage(final ProtocolSchedule<?> protocolSchedule) {
return new KeyValueStoragePrefixedKeyBlockchainStorage(
keyValueStorage, ScheduleBasedBlockHeaderFunctions.create(protocolSchedule));
blockchainStorage, ScheduleBasedBlockHeaderFunctions.create(protocolSchedule));
}
@Override
public WorldStateStorage createWorldStateStorage() {
return new KeyValueStorageWorldStateStorage(keyValueStorage);
return new WorldStateKeyValueStorage(worldStateStorage);
}
@Override
public PrivateTransactionStorage createPrivateTransactionStorage() {
return new PrivateKeyValueStorage(keyValueStorage);
return new PrivateTransactionKeyValueStorage(privateTransactionStorage);
}
@Override
public PrivateStateStorage createPrivateStateStorage() {
return new PrivateStateKeyValueStorage(keyValueStorage);
return new PrivateStateKeyValueStorage(privateStateStorage);
}
public KeyValueStorage createPruningStorage() {
return pruningStorage;
}
@Override
public void close() throws IOException {
keyValueStorage.close();
blockchainStorage.close();
worldStateStorage.close();
privateTransactionStorage.close();
privateStateStorage.close();
pruningStorage.close();
}
}

@ -12,22 +12,83 @@
*/
package tech.pegasys.pantheon.ethereum.storage.keyvalue;
import static java.util.Arrays.asList;
import tech.pegasys.pantheon.ethereum.storage.StorageProvider;
import tech.pegasys.pantheon.metrics.MetricsSystem;
import tech.pegasys.pantheon.services.kvstore.ColumnarRocksDbKeyValueStorage;
import tech.pegasys.pantheon.services.kvstore.KeyValueStorage;
import tech.pegasys.pantheon.services.kvstore.RocksDbConfiguration;
import tech.pegasys.pantheon.services.kvstore.RocksDbKeyValueStorage;
import tech.pegasys.pantheon.services.kvstore.SegmentedKeyValueStorage;
import tech.pegasys.pantheon.services.kvstore.SegmentedKeyValueStorage.Segment;
import tech.pegasys.pantheon.services.kvstore.SegmentedKeyValueStorageAdapter;
import java.io.IOException;
import java.nio.file.Files;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class RocksDbStorageProvider {
private static final Logger LOG = LogManager.getLogger();
public static StorageProvider create(
final RocksDbConfiguration rocksDbConfiguration, final MetricsSystem metricsSystem)
throws IOException {
if (rocksDbConfiguration.useColumns()) {
return createSegmentedProvider(rocksDbConfiguration, metricsSystem);
} else {
return createUnsegmentedProvider(rocksDbConfiguration, metricsSystem);
}
}
private static StorageProvider createUnsegmentedProvider(
final RocksDbConfiguration rocksDbConfiguration, final MetricsSystem metricsSystem)
throws IOException {
Files.createDirectories(rocksDbConfiguration.getDatabaseDir());
final KeyValueStorage kv = RocksDbKeyValueStorage.create(rocksDbConfiguration, metricsSystem);
return new KeyValueStorageProvider(kv);
}
private static StorageProvider createSegmentedProvider(
final RocksDbConfiguration rocksDbConfiguration, final MetricsSystem metricsSystem)
throws IOException {
LOG.info("Using RocksDB columns");
Files.createDirectories(rocksDbConfiguration.getDatabaseDir());
final SegmentedKeyValueStorage<?> columnarStorage =
ColumnarRocksDbKeyValueStorage.create(
rocksDbConfiguration, asList(RocksDbSegment.values()), metricsSystem);
return new KeyValueStorageProvider(
new SegmentedKeyValueStorageAdapter<>(RocksDbSegment.BLOCKCHAIN, columnarStorage),
new SegmentedKeyValueStorageAdapter<>(RocksDbSegment.WORLD_STATE, columnarStorage),
new SegmentedKeyValueStorageAdapter<>(RocksDbSegment.PRIVATE_TRANSACTIONS, columnarStorage),
new SegmentedKeyValueStorageAdapter<>(RocksDbSegment.PRIVATE_STATE, columnarStorage),
new SegmentedKeyValueStorageAdapter<>(RocksDbSegment.PRUNING_STATE, columnarStorage));
}
private enum RocksDbSegment implements Segment {
BLOCKCHAIN((byte) 1),
WORLD_STATE((byte) 2),
PRIVATE_TRANSACTIONS((byte) 3),
PRIVATE_STATE((byte) 4),
PRUNING_STATE((byte) 5);
private final byte[] id;
RocksDbSegment(final byte... id) {
this.id = id;
}
@Override
public String getName() {
return name();
}
@Override
public byte[] getId() {
return id;
}
}
}

@ -16,16 +16,21 @@ import tech.pegasys.pantheon.ethereum.core.Hash;
import tech.pegasys.pantheon.ethereum.trie.MerklePatriciaTrie;
import tech.pegasys.pantheon.ethereum.worldstate.WorldStateStorage;
import tech.pegasys.pantheon.services.kvstore.KeyValueStorage;
import tech.pegasys.pantheon.util.Subscribers;
import tech.pegasys.pantheon.util.bytes.Bytes32;
import tech.pegasys.pantheon.util.bytes.BytesValue;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
public class KeyValueStorageWorldStateStorage implements WorldStateStorage {
public class WorldStateKeyValueStorage implements WorldStateStorage {
private final Subscribers<NodesAddedListener> nodeAddedListeners = Subscribers.create();
private final KeyValueStorage keyValueStorage;
public KeyValueStorageWorldStateStorage(final KeyValueStorage keyValueStorage) {
public WorldStateKeyValueStorage(final KeyValueStorage keyValueStorage) {
this.keyValueStorage = keyValueStorage;
}
@ -74,15 +79,35 @@ public class KeyValueStorageWorldStateStorage implements WorldStateStorage {
@Override
public Updater updater() {
return new Updater(keyValueStorage.startTransaction());
return new Updater(keyValueStorage.startTransaction(), nodeAddedListeners);
}
@Override
public long prune(final Predicate<BytesValue> inUseCheck) {
return keyValueStorage.removeUnless(inUseCheck);
}
@Override
public long addNodeAddedListener(final NodesAddedListener listener) {
return nodeAddedListeners.subscribe(listener);
}
@Override
public void removeNodeAddedListener(final long id) {
nodeAddedListeners.unsubscribe(id);
}
public static class Updater implements WorldStateStorage.Updater {
private final KeyValueStorage.Transaction transaction;
private final Subscribers<NodesAddedListener> nodeAddedListeners;
private final List<Bytes32> addedNodes = new ArrayList<>();
public Updater(final KeyValueStorage.Transaction transaction) {
public Updater(
final KeyValueStorage.Transaction transaction,
final Subscribers<NodesAddedListener> nodeAddedListeners) {
this.transaction = transaction;
this.nodeAddedListeners = nodeAddedListeners;
}
@Override
@ -91,6 +116,8 @@ public class KeyValueStorageWorldStateStorage implements WorldStateStorage {
// Don't save empty values
return this;
}
addedNodes.add(codeHash);
transaction.put(codeHash, code);
return this;
}
@ -101,6 +128,7 @@ public class KeyValueStorageWorldStateStorage implements WorldStateStorage {
// Don't save empty nodes
return this;
}
addedNodes.add(nodeHash);
transaction.put(nodeHash, node);
return this;
}
@ -111,12 +139,14 @@ public class KeyValueStorageWorldStateStorage implements WorldStateStorage {
// Don't save empty nodes
return this;
}
addedNodes.add(nodeHash);
transaction.put(nodeHash, node);
return this;
}
@Override
public void commit() {
nodeAddedListeners.forEach(listener -> listener.onNodesAdded(addedNodes));
transaction.commit();
}

@ -18,7 +18,7 @@ import tech.pegasys.pantheon.ethereum.core.MutableAccount;
import tech.pegasys.pantheon.ethereum.core.Wei;
import tech.pegasys.pantheon.ethereum.core.WorldState;
import tech.pegasys.pantheon.ethereum.core.WorldUpdater;
import tech.pegasys.pantheon.ethereum.storage.keyvalue.KeyValueStorageWorldStateStorage;
import tech.pegasys.pantheon.ethereum.storage.keyvalue.WorldStateKeyValueStorage;
import tech.pegasys.pantheon.services.kvstore.InMemoryKeyValueStorage;
import java.util.Collection;
@ -50,7 +50,7 @@ public class DebuggableMutableWorldState extends DefaultMutableWorldState {
private final DebugInfo info = new DebugInfo();
public DebuggableMutableWorldState() {
super(new KeyValueStorageWorldStateStorage(new InMemoryKeyValueStorage()));
super(new WorldStateKeyValueStorage(new InMemoryKeyValueStorage()));
}
public DebuggableMutableWorldState(final WorldState worldState) {

@ -16,7 +16,9 @@ import tech.pegasys.pantheon.ethereum.core.Hash;
import tech.pegasys.pantheon.util.bytes.Bytes32;
import tech.pegasys.pantheon.util.bytes.BytesValue;
import java.util.Collection;
import java.util.Optional;
import java.util.function.Predicate;
public interface WorldStateStorage {
@ -36,6 +38,12 @@ public interface WorldStateStorage {
Updater updater();
long prune(Predicate<BytesValue> inUseCheck);
long addNodeAddedListener(NodesAddedListener listener);
void removeNodeAddedListener(long id);
interface Updater {
Updater putCode(Bytes32 nodeHash, BytesValue code);
@ -54,4 +62,8 @@ public interface WorldStateStorage {
void rollback();
}
interface NodesAddedListener {
void onNodesAdded(Collection<Bytes32> nodeHash);
}
}

@ -22,7 +22,7 @@ import tech.pegasys.pantheon.ethereum.mainnet.MainnetBlockHeaderFunctions;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolScheduleBuilder;
import tech.pegasys.pantheon.ethereum.storage.keyvalue.KeyValueStoragePrefixedKeyBlockchainStorage;
import tech.pegasys.pantheon.ethereum.storage.keyvalue.KeyValueStorageWorldStateStorage;
import tech.pegasys.pantheon.ethereum.storage.keyvalue.WorldStateKeyValueStorage;
import tech.pegasys.pantheon.ethereum.worldstate.WorldStateArchive;
import tech.pegasys.pantheon.metrics.noop.NoOpMetricsSystem;
import tech.pegasys.pantheon.services.kvstore.InMemoryKeyValueStorage;
@ -53,8 +53,7 @@ public class ExecutionContextTestFixture {
new KeyValueStoragePrefixedKeyBlockchainStorage(
keyValueStorage, new MainnetBlockHeaderFunctions()),
new NoOpMetricsSystem());
this.stateArchive =
new WorldStateArchive(new KeyValueStorageWorldStateStorage(keyValueStorage));
this.stateArchive = new WorldStateArchive(new WorldStateKeyValueStorage(keyValueStorage));
this.protocolSchedule = protocolSchedule;
this.protocolContext = new ProtocolContext<>(blockchain, stateArchive, null);
genesisState.writeStateTo(stateArchive.getMutable());

@ -18,13 +18,13 @@ import tech.pegasys.pantheon.ethereum.chain.MutableBlockchain;
import tech.pegasys.pantheon.ethereum.mainnet.MainnetBlockHeaderFunctions;
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule;
import tech.pegasys.pantheon.ethereum.mainnet.ScheduleBasedBlockHeaderFunctions;
import tech.pegasys.pantheon.ethereum.privacy.PrivateKeyValueStorage;
import tech.pegasys.pantheon.ethereum.privacy.PrivateStateKeyValueStorage;
import tech.pegasys.pantheon.ethereum.privacy.PrivateStateStorage;
import tech.pegasys.pantheon.ethereum.privacy.PrivateTransactionKeyValueStorage;
import tech.pegasys.pantheon.ethereum.privacy.PrivateTransactionStorage;
import tech.pegasys.pantheon.ethereum.storage.StorageProvider;
import tech.pegasys.pantheon.ethereum.storage.keyvalue.KeyValueStoragePrefixedKeyBlockchainStorage;
import tech.pegasys.pantheon.ethereum.storage.keyvalue.KeyValueStorageWorldStateStorage;
import tech.pegasys.pantheon.ethereum.storage.keyvalue.WorldStateKeyValueStorage;
import tech.pegasys.pantheon.ethereum.worldstate.WorldStateArchive;
import tech.pegasys.pantheon.ethereum.worldstate.WorldStateStorage;
import tech.pegasys.pantheon.metrics.noop.NoOpMetricsSystem;
@ -46,8 +46,7 @@ public class InMemoryStorageProvider implements StorageProvider {
}
public static WorldStateArchive createInMemoryWorldStateArchive() {
return new WorldStateArchive(
new KeyValueStorageWorldStateStorage(new InMemoryKeyValueStorage()));
return new WorldStateArchive(new WorldStateKeyValueStorage(new InMemoryKeyValueStorage()));
}
@Override
@ -58,12 +57,12 @@ public class InMemoryStorageProvider implements StorageProvider {
@Override
public WorldStateStorage createWorldStateStorage() {
return new KeyValueStorageWorldStateStorage(new InMemoryKeyValueStorage());
return new WorldStateKeyValueStorage(new InMemoryKeyValueStorage());
}
@Override
public PrivateTransactionStorage createPrivateTransactionStorage() {
return new PrivateKeyValueStorage(new InMemoryKeyValueStorage());
return new PrivateTransactionKeyValueStorage(new InMemoryKeyValueStorage());
}
@Override

@ -20,7 +20,7 @@ import tech.pegasys.pantheon.ethereum.core.BlockHeader;
import tech.pegasys.pantheon.ethereum.core.Hash;
import tech.pegasys.pantheon.ethereum.mainnet.MainnetProtocolSchedule;
import tech.pegasys.pantheon.ethereum.rlp.BytesValueRLPOutput;
import tech.pegasys.pantheon.ethereum.storage.keyvalue.KeyValueStorageWorldStateStorage;
import tech.pegasys.pantheon.ethereum.storage.keyvalue.WorldStateKeyValueStorage;
import tech.pegasys.pantheon.ethereum.worldstate.DefaultMutableWorldState;
import tech.pegasys.pantheon.services.kvstore.InMemoryKeyValueStorage;
import tech.pegasys.pantheon.util.bytes.BytesValue;
@ -61,8 +61,7 @@ public final class GenesisStateTest {
assertThat(header.getExtraData()).isEqualTo(BytesValue.EMPTY);
assertThat(header.getParentHash()).isEqualTo(Hash.ZERO);
final DefaultMutableWorldState worldState =
new DefaultMutableWorldState(
new KeyValueStorageWorldStateStorage(new InMemoryKeyValueStorage()));
new DefaultMutableWorldState(new WorldStateKeyValueStorage(new InMemoryKeyValueStorage()));
genesisState.writeStateTo(worldState);
final Account first =
worldState.get(Address.fromHexString("0x0000000000000000000000000000000000000001"));
@ -102,8 +101,7 @@ public final class GenesisStateTest {
"0xe7fd8db206dcaf066b7c97b8a42a0abc18653613560748557ab44868652a78b6"));
final DefaultMutableWorldState worldState =
new DefaultMutableWorldState(
new KeyValueStorageWorldStateStorage(new InMemoryKeyValueStorage()));
new DefaultMutableWorldState(new WorldStateKeyValueStorage(new InMemoryKeyValueStorage()));
genesisState.writeStateTo(worldState);
final Account contract =
worldState.get(Address.fromHexString("0x3850000000000000000000000000000000000000"));

@ -15,7 +15,7 @@ package tech.pegasys.pantheon.ethereum.storage.keyvalue;
import static org.assertj.core.api.Assertions.assertThat;
import tech.pegasys.pantheon.ethereum.core.Hash;
import tech.pegasys.pantheon.ethereum.storage.keyvalue.KeyValueStorageWorldStateStorage.Updater;
import tech.pegasys.pantheon.ethereum.storage.keyvalue.WorldStateKeyValueStorage.Updater;
import tech.pegasys.pantheon.ethereum.trie.MerklePatriciaTrie;
import tech.pegasys.pantheon.services.kvstore.InMemoryKeyValueStorage;
import tech.pegasys.pantheon.util.bytes.Bytes32;
@ -27,40 +27,40 @@ public class KeyValueStorageWorldStateStorageTest {
@Test
public void getCode_returnsEmpty() {
final KeyValueStorageWorldStateStorage storage = emptyStorage();
final WorldStateKeyValueStorage storage = emptyStorage();
assertThat(storage.getCode(Hash.EMPTY)).contains(BytesValue.EMPTY);
}
@Test
public void getAccountStateTrieNode_returnsEmptyNode() {
final KeyValueStorageWorldStateStorage storage = emptyStorage();
final WorldStateKeyValueStorage storage = emptyStorage();
assertThat(storage.getAccountStateTrieNode(MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH))
.contains(MerklePatriciaTrie.EMPTY_TRIE_NODE);
}
@Test
public void getAccountStorageTrieNode_returnsEmptyNode() {
final KeyValueStorageWorldStateStorage storage = emptyStorage();
final WorldStateKeyValueStorage storage = emptyStorage();
assertThat(storage.getAccountStorageTrieNode(MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH))
.contains(MerklePatriciaTrie.EMPTY_TRIE_NODE);
}
@Test
public void getNodeData_returnsEmptyValue() {
final KeyValueStorageWorldStateStorage storage = emptyStorage();
final WorldStateKeyValueStorage storage = emptyStorage();
assertThat(storage.getNodeData(Hash.EMPTY)).contains(BytesValue.EMPTY);
}
@Test
public void getNodeData_returnsEmptyNode() {
final KeyValueStorageWorldStateStorage storage = emptyStorage();
final WorldStateKeyValueStorage storage = emptyStorage();
assertThat(storage.getNodeData(MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH))
.contains(MerklePatriciaTrie.EMPTY_TRIE_NODE);
}
@Test
public void getCode_saveAndGetSpecialValues() {
final KeyValueStorageWorldStateStorage storage = emptyStorage();
final WorldStateKeyValueStorage storage = emptyStorage();
storage
.updater()
.putCode(MerklePatriciaTrie.EMPTY_TRIE_NODE)
@ -75,7 +75,7 @@ public class KeyValueStorageWorldStateStorageTest {
@Test
public void getCode_saveAndGetRegularValue() {
final BytesValue bytes = BytesValue.fromHexString("0x123456");
final KeyValueStorageWorldStateStorage storage = emptyStorage();
final WorldStateKeyValueStorage storage = emptyStorage();
storage.updater().putCode(bytes).commit();
assertThat(storage.getCode(Hash.hash(bytes))).contains(bytes);
@ -83,7 +83,7 @@ public class KeyValueStorageWorldStateStorageTest {
@Test
public void getAccountStateTrieNode_saveAndGetSpecialValues() {
final KeyValueStorageWorldStateStorage storage = emptyStorage();
final WorldStateKeyValueStorage storage = emptyStorage();
storage
.updater()
.putAccountStateTrieNode(
@ -99,7 +99,7 @@ public class KeyValueStorageWorldStateStorageTest {
@Test
public void getAccountStateTrieNode_saveAndGetRegularValue() {
final BytesValue bytes = BytesValue.fromHexString("0x123456");
final KeyValueStorageWorldStateStorage storage = emptyStorage();
final WorldStateKeyValueStorage storage = emptyStorage();
storage.updater().putAccountStateTrieNode(Hash.hash(bytes), bytes).commit();
assertThat(storage.getAccountStateTrieNode(Hash.hash(bytes))).contains(bytes);
@ -107,7 +107,7 @@ public class KeyValueStorageWorldStateStorageTest {
@Test
public void getAccountStorageTrieNode_saveAndGetSpecialValues() {
final KeyValueStorageWorldStateStorage storage = emptyStorage();
final WorldStateKeyValueStorage storage = emptyStorage();
storage
.updater()
.putAccountStorageTrieNode(
@ -123,7 +123,7 @@ public class KeyValueStorageWorldStateStorageTest {
@Test
public void getAccountStorageTrieNode_saveAndGetRegularValue() {
final BytesValue bytes = BytesValue.fromHexString("0x123456");
final KeyValueStorageWorldStateStorage storage = emptyStorage();
final WorldStateKeyValueStorage storage = emptyStorage();
storage.updater().putAccountStorageTrieNode(Hash.hash(bytes), bytes).commit();
assertThat(storage.getAccountStateTrieNode(Hash.hash(bytes))).contains(bytes);
@ -131,7 +131,7 @@ public class KeyValueStorageWorldStateStorageTest {
@Test
public void getNodeData_saveAndGetSpecialValues() {
final KeyValueStorageWorldStateStorage storage = emptyStorage();
final WorldStateKeyValueStorage storage = emptyStorage();
storage
.updater()
.putAccountStorageTrieNode(
@ -147,7 +147,7 @@ public class KeyValueStorageWorldStateStorageTest {
@Test
public void getNodeData_saveAndGetRegularValue() {
final BytesValue bytes = BytesValue.fromHexString("0x123456");
final KeyValueStorageWorldStateStorage storage = emptyStorage();
final WorldStateKeyValueStorage storage = emptyStorage();
storage.updater().putAccountStorageTrieNode(Hash.hash(bytes), bytes).commit();
assertThat(storage.getNodeData(Hash.hash(bytes))).contains(bytes);
@ -159,7 +159,7 @@ public class KeyValueStorageWorldStateStorageTest {
final BytesValue bytesB = BytesValue.fromHexString("0x1234");
final BytesValue bytesC = BytesValue.fromHexString("0x123456");
final KeyValueStorageWorldStateStorage storage = emptyStorage();
final WorldStateKeyValueStorage storage = emptyStorage();
final Updater updaterA = storage.updater();
final Updater updaterB = storage.updater();
@ -186,7 +186,7 @@ public class KeyValueStorageWorldStateStorageTest {
assertThat(emptyStorage().isWorldStateAvailable(Hash.EMPTY_TRIE_HASH)).isTrue();
}
private KeyValueStorageWorldStateStorage emptyStorage() {
return new KeyValueStorageWorldStateStorage(new InMemoryKeyValueStorage());
private WorldStateKeyValueStorage emptyStorage() {
return new WorldStateKeyValueStorage(new InMemoryKeyValueStorage());
}
}

@ -26,7 +26,7 @@ import tech.pegasys.pantheon.ethereum.core.MessageFrameTestFixture;
import tech.pegasys.pantheon.ethereum.core.Wei;
import tech.pegasys.pantheon.ethereum.core.WorldUpdater;
import tech.pegasys.pantheon.ethereum.mainnet.ConstantinopleGasCalculator;
import tech.pegasys.pantheon.ethereum.storage.keyvalue.KeyValueStorageWorldStateStorage;
import tech.pegasys.pantheon.ethereum.storage.keyvalue.WorldStateKeyValueStorage;
import tech.pegasys.pantheon.ethereum.vm.MessageFrame;
import tech.pegasys.pantheon.ethereum.vm.Words;
import tech.pegasys.pantheon.ethereum.worldstate.WorldStateArchive;
@ -44,7 +44,7 @@ public class ExtCodeHashOperationTest {
private final Blockchain blockchain = mock(Blockchain.class);
private final WorldStateArchive worldStateArchive =
new WorldStateArchive(new KeyValueStorageWorldStateStorage(new InMemoryKeyValueStorage()));
new WorldStateArchive(new WorldStateKeyValueStorage(new InMemoryKeyValueStorage()));
private final WorldUpdater worldStateUpdater = worldStateArchive.getMutable().updater();
private final ExtCodeHashOperation operation =

@ -25,7 +25,7 @@ import tech.pegasys.pantheon.ethereum.core.MutableWorldState;
import tech.pegasys.pantheon.ethereum.core.Wei;
import tech.pegasys.pantheon.ethereum.core.WorldState;
import tech.pegasys.pantheon.ethereum.core.WorldUpdater;
import tech.pegasys.pantheon.ethereum.storage.keyvalue.KeyValueStorageWorldStateStorage;
import tech.pegasys.pantheon.ethereum.storage.keyvalue.WorldStateKeyValueStorage;
import tech.pegasys.pantheon.ethereum.trie.MerklePatriciaTrie;
import tech.pegasys.pantheon.services.kvstore.InMemoryKeyValueStorage;
import tech.pegasys.pantheon.services.kvstore.KeyValueStorage;
@ -47,13 +47,13 @@ public class DefaultMutableWorldStateTest {
private static final Address ADDRESS =
Address.fromHexString("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b");
private static MutableWorldState createEmpty(final KeyValueStorageWorldStateStorage storage) {
private static MutableWorldState createEmpty(final WorldStateKeyValueStorage storage) {
return new DefaultMutableWorldState(storage);
}
private static MutableWorldState createEmpty() {
final InMemoryKeyValueStorage inMemoryKeyValueStorage = new InMemoryKeyValueStorage();
return createEmpty(new KeyValueStorageWorldStateStorage(inMemoryKeyValueStorage));
return createEmpty(new WorldStateKeyValueStorage(inMemoryKeyValueStorage));
}
@Test
@ -158,8 +158,7 @@ public class DefaultMutableWorldStateTest {
@Test
public void commitAndPersist() {
final KeyValueStorage storage = new InMemoryKeyValueStorage();
final KeyValueStorageWorldStateStorage kvWorldStateStorage =
new KeyValueStorageWorldStateStorage(storage);
final WorldStateKeyValueStorage kvWorldStateStorage = new WorldStateKeyValueStorage(storage);
final MutableWorldState worldState = createEmpty(kvWorldStateStorage);
final WorldUpdater updater = worldState.updater();
final Wei newBalance = Wei.of(100000);
@ -190,8 +189,7 @@ public class DefaultMutableWorldStateTest {
// Create new world state and check that it can access modified address
final MutableWorldState newWorldState =
new DefaultMutableWorldState(
expectedRootHash, new KeyValueStorageWorldStateStorage(storage));
new DefaultMutableWorldState(expectedRootHash, new WorldStateKeyValueStorage(storage));
assertEquals(expectedRootHash, newWorldState.rootHash());
assertNotNull(newWorldState.get(ADDRESS));
assertEquals(newBalance, newWorldState.get(ADDRESS).getBalance());

@ -25,8 +25,8 @@ import tech.pegasys.pantheon.ethereum.eth.manager.RespondingEthPeer;
import tech.pegasys.pantheon.ethereum.eth.manager.RespondingEthPeer.Responder;
import tech.pegasys.pantheon.ethereum.eth.sync.SynchronizerConfiguration;
import tech.pegasys.pantheon.ethereum.storage.StorageProvider;
import tech.pegasys.pantheon.ethereum.storage.keyvalue.KeyValueStorageWorldStateStorage;
import tech.pegasys.pantheon.ethereum.storage.keyvalue.RocksDbStorageProvider;
import tech.pegasys.pantheon.ethereum.storage.keyvalue.WorldStateKeyValueStorage;
import tech.pegasys.pantheon.ethereum.worldstate.WorldStateArchive;
import tech.pegasys.pantheon.ethereum.worldstate.WorldStateStorage;
import tech.pegasys.pantheon.metrics.MetricsSystem;
@ -118,7 +118,7 @@ public class WorldStateDownloaderBenchmark {
private Hash createExistingWorldState() {
// Setup existing state
remoteKeyValueStorage = new InMemoryKeyValueStorage();
final WorldStateStorage storage = new KeyValueStorageWorldStateStorage(remoteKeyValueStorage);
final WorldStateStorage storage = new WorldStateKeyValueStorage(remoteKeyValueStorage);
final WorldStateArchive worldStateArchive = new WorldStateArchive(storage);
final MutableWorldState worldState = worldStateArchive.getMutable();

@ -22,7 +22,7 @@ import tech.pegasys.pantheon.ethereum.core.BlockHeader;
import tech.pegasys.pantheon.ethereum.core.BlockHeaderTestFixture;
import tech.pegasys.pantheon.ethereum.core.Hash;
import tech.pegasys.pantheon.ethereum.eth.manager.task.EthTask;
import tech.pegasys.pantheon.ethereum.storage.keyvalue.KeyValueStorageWorldStateStorage;
import tech.pegasys.pantheon.ethereum.storage.keyvalue.WorldStateKeyValueStorage;
import tech.pegasys.pantheon.ethereum.worldstate.WorldStateStorage;
import tech.pegasys.pantheon.services.kvstore.InMemoryKeyValueStorage;
import tech.pegasys.pantheon.services.tasks.CachingTaskCollection;
@ -45,7 +45,7 @@ public class WorldDownloadStateTest {
private static final long MIN_MILLIS_BEFORE_STALLING = 50_000;
private final WorldStateStorage worldStateStorage =
new KeyValueStorageWorldStateStorage(new InMemoryKeyValueStorage());
new WorldStateKeyValueStorage(new InMemoryKeyValueStorage());
private final BlockHeader header =
new BlockHeaderTestFixture().stateRoot(ROOT_NODE_HASH).buildHeader();

@ -47,7 +47,7 @@ import tech.pegasys.pantheon.ethereum.eth.sync.SynchronizerConfiguration;
import tech.pegasys.pantheon.ethereum.mainnet.MainnetProtocolSchedule;
import tech.pegasys.pantheon.ethereum.p2p.rlpx.wire.MessageData;
import tech.pegasys.pantheon.ethereum.rlp.RLP;
import tech.pegasys.pantheon.ethereum.storage.keyvalue.KeyValueStorageWorldStateStorage;
import tech.pegasys.pantheon.ethereum.storage.keyvalue.WorldStateKeyValueStorage;
import tech.pegasys.pantheon.ethereum.trie.MerklePatriciaTrie;
import tech.pegasys.pantheon.ethereum.trie.Node;
import tech.pegasys.pantheon.ethereum.trie.StoredMerklePatriciaTrie;
@ -164,7 +164,7 @@ public class WorldStateDownloaderTest {
final CachingTaskCollection<NodeDataRequest> taskCollection =
new CachingTaskCollection<>(new InMemoryTaskQueue<>());
final WorldStateStorage localStorage =
new KeyValueStorageWorldStateStorage(new InMemoryKeyValueStorage());
new WorldStateKeyValueStorage(new InMemoryKeyValueStorage());
final WorldStateDownloader downloader =
createDownloader(ethProtocolManager.ethContext(), localStorage, taskCollection);
@ -180,8 +180,7 @@ public class WorldStateDownloaderTest {
@Test
public void downloadAlreadyAvailableWorldState() {
// Setup existing state
final WorldStateStorage storage =
new KeyValueStorageWorldStateStorage(new InMemoryKeyValueStorage());
final WorldStateStorage storage = new WorldStateKeyValueStorage(new InMemoryKeyValueStorage());
final WorldStateArchive worldStateArchive = new WorldStateArchive(storage);
final MutableWorldState worldState = worldStateArchive.getMutable();
@ -224,7 +223,7 @@ public class WorldStateDownloaderTest {
// Setup "remote" state
final WorldStateStorage remoteStorage =
new KeyValueStorageWorldStateStorage(new InMemoryKeyValueStorage());
new WorldStateKeyValueStorage(new InMemoryKeyValueStorage());
final WorldStateArchive remoteWorldStateArchive = new WorldStateArchive(remoteStorage);
final MutableWorldState remoteWorldState = remoteWorldStateArchive.getMutable();
@ -245,7 +244,7 @@ public class WorldStateDownloaderTest {
final CachingTaskCollection<NodeDataRequest> taskCollection =
new CachingTaskCollection<>(new InMemoryTaskQueue<>());
final WorldStateStorage localStorage =
new KeyValueStorageWorldStateStorage(new InMemoryKeyValueStorage());
new WorldStateKeyValueStorage(new InMemoryKeyValueStorage());
final WorldStateDownloader downloader =
createDownloader(ethProtocolManager.ethContext(), localStorage, taskCollection);
@ -275,7 +274,7 @@ public class WorldStateDownloaderTest {
public void doesNotRequestKnownCodeFromNetwork() {
// Setup "remote" state
final WorldStateStorage remoteStorage =
new KeyValueStorageWorldStateStorage(new InMemoryKeyValueStorage());
new WorldStateKeyValueStorage(new InMemoryKeyValueStorage());
final WorldStateArchive remoteWorldStateArchive = new WorldStateArchive(remoteStorage);
final MutableWorldState remoteWorldState = remoteWorldStateArchive.getMutable();
@ -296,7 +295,7 @@ public class WorldStateDownloaderTest {
final CachingTaskCollection<NodeDataRequest> taskCollection =
new CachingTaskCollection<>(new InMemoryTaskQueue<>());
final WorldStateStorage localStorage =
new KeyValueStorageWorldStateStorage(new InMemoryKeyValueStorage());
new WorldStateKeyValueStorage(new InMemoryKeyValueStorage());
// Seed local storage with some contract values
final Map<Bytes32, BytesValue> knownCode = new HashMap<>();
@ -357,7 +356,7 @@ public class WorldStateDownloaderTest {
// Setup "remote" state
final WorldStateStorage remoteStorage =
new KeyValueStorageWorldStateStorage(new InMemoryKeyValueStorage());
new WorldStateKeyValueStorage(new InMemoryKeyValueStorage());
final WorldStateArchive remoteWorldStateArchive = new WorldStateArchive(remoteStorage);
final MutableWorldState remoteWorldState = remoteWorldStateArchive.getMutable();
@ -377,7 +376,7 @@ public class WorldStateDownloaderTest {
final CachingTaskCollection<NodeDataRequest> taskCollection =
spy(new CachingTaskCollection<>(new InMemoryTaskQueue<>()));
final WorldStateStorage localStorage =
new KeyValueStorageWorldStateStorage(new InMemoryKeyValueStorage());
new WorldStateKeyValueStorage(new InMemoryKeyValueStorage());
final WorldStateDownloader downloader =
createDownloader(ethProtocolManager.ethContext(), localStorage, taskCollection);
@ -427,7 +426,7 @@ public class WorldStateDownloaderTest {
public void doesNotRequestKnownAccountTrieNodesFromNetwork() {
// Setup "remote" state
final WorldStateStorage remoteStorage =
new KeyValueStorageWorldStateStorage(new InMemoryKeyValueStorage());
new WorldStateKeyValueStorage(new InMemoryKeyValueStorage());
final WorldStateArchive remoteWorldStateArchive = new WorldStateArchive(remoteStorage);
final MutableWorldState remoteWorldState = remoteWorldStateArchive.getMutable();
@ -448,7 +447,7 @@ public class WorldStateDownloaderTest {
final CachingTaskCollection<NodeDataRequest> taskCollection =
new CachingTaskCollection<>(new InMemoryTaskQueue<>());
final WorldStateStorage localStorage =
new KeyValueStorageWorldStateStorage(new InMemoryKeyValueStorage());
new WorldStateKeyValueStorage(new InMemoryKeyValueStorage());
// Seed local storage with some trie node values
final Map<Bytes32, BytesValue> allNodes =
@ -506,7 +505,7 @@ public class WorldStateDownloaderTest {
public void doesNotRequestKnownStorageTrieNodesFromNetwork() {
// Setup "remote" state
final WorldStateStorage remoteStorage =
new KeyValueStorageWorldStateStorage(new InMemoryKeyValueStorage());
new WorldStateKeyValueStorage(new InMemoryKeyValueStorage());
final WorldStateArchive remoteWorldStateArchive = new WorldStateArchive(remoteStorage);
final MutableWorldState remoteWorldState = remoteWorldStateArchive.getMutable();
@ -527,7 +526,7 @@ public class WorldStateDownloaderTest {
final CachingTaskCollection<NodeDataRequest> taskCollection =
new CachingTaskCollection<>(new InMemoryTaskQueue<>());
final WorldStateStorage localStorage =
new KeyValueStorageWorldStateStorage(new InMemoryKeyValueStorage());
new WorldStateKeyValueStorage(new InMemoryKeyValueStorage());
// Seed local storage with some trie node values
final List<Bytes32> storageRootHashes =
@ -605,7 +604,7 @@ public class WorldStateDownloaderTest {
// Setup "remote" state
final WorldStateStorage remoteStorage =
new KeyValueStorageWorldStateStorage(new InMemoryKeyValueStorage());
new WorldStateKeyValueStorage(new InMemoryKeyValueStorage());
final WorldStateArchive remoteWorldStateArchive = new WorldStateArchive(remoteStorage);
final MutableWorldState remoteWorldState = remoteWorldStateArchive.getMutable();
@ -619,7 +618,7 @@ public class WorldStateDownloaderTest {
final CachingTaskCollection<NodeDataRequest> taskCollection =
new CachingTaskCollection<>(new InMemoryTaskQueue<>());
final WorldStateStorage localStorage =
new KeyValueStorageWorldStateStorage(new InMemoryKeyValueStorage());
new WorldStateKeyValueStorage(new InMemoryKeyValueStorage());
final SynchronizerConfiguration syncConfig =
SynchronizerConfiguration.builder().worldStateMaxRequestsWithoutProgress(10).build();
final WorldStateDownloader downloader =
@ -658,7 +657,7 @@ public class WorldStateDownloaderTest {
public void resumesFromNonEmptyQueue() {
// Setup "remote" state
final WorldStateStorage remoteStorage =
new KeyValueStorageWorldStateStorage(new InMemoryKeyValueStorage());
new WorldStateKeyValueStorage(new InMemoryKeyValueStorage());
final WorldStateArchive remoteWorldStateArchive = new WorldStateArchive(remoteStorage);
final MutableWorldState remoteWorldState = remoteWorldStateArchive.getMutable();
@ -684,7 +683,7 @@ public class WorldStateDownloaderTest {
}
final WorldStateStorage localStorage =
new KeyValueStorageWorldStateStorage(new InMemoryKeyValueStorage());
new WorldStateKeyValueStorage(new InMemoryKeyValueStorage());
final SynchronizerConfiguration syncConfig =
SynchronizerConfiguration.builder().worldStateMaxRequestsWithoutProgress(10).build();
final WorldStateDownloader downloader =
@ -794,7 +793,7 @@ public class WorldStateDownloaderTest {
// Setup "remote" state
final WorldStateStorage remoteStorage =
new KeyValueStorageWorldStateStorage(new InMemoryKeyValueStorage());
new WorldStateKeyValueStorage(new InMemoryKeyValueStorage());
final WorldStateArchive remoteWorldStateArchive = new WorldStateArchive(remoteStorage);
final MutableWorldState remoteWorldState = remoteWorldStateArchive.getMutable();
@ -817,7 +816,7 @@ public class WorldStateDownloaderTest {
final CachingTaskCollection<NodeDataRequest> taskCollection =
new CachingTaskCollection<>(new InMemoryTaskQueue<>());
final WorldStateStorage localStorage =
new KeyValueStorageWorldStateStorage(new InMemoryKeyValueStorage());
new WorldStateKeyValueStorage(new InMemoryKeyValueStorage());
final WorldStateArchive localWorldStateArchive = new WorldStateArchive(localStorage);
final SynchronizerConfiguration syncConfig =
SynchronizerConfiguration.builder()

@ -0,0 +1,326 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.services.kvstore;
import tech.pegasys.pantheon.metrics.Counter;
import tech.pegasys.pantheon.metrics.MetricsSystem;
import tech.pegasys.pantheon.metrics.OperationTimer;
import tech.pegasys.pantheon.metrics.PantheonMetricCategory;
import tech.pegasys.pantheon.metrics.prometheus.PrometheusMetricsSystem;
import tech.pegasys.pantheon.metrics.rocksdb.RocksDBStats;
import tech.pegasys.pantheon.services.util.RocksDbUtil;
import tech.pegasys.pantheon.util.bytes.BytesValue;
import java.io.Closeable;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import com.google.common.collect.ImmutableMap;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.rocksdb.ColumnFamilyDescriptor;
import org.rocksdb.ColumnFamilyHandle;
import org.rocksdb.ColumnFamilyOptions;
import org.rocksdb.DBOptions;
import org.rocksdb.Env;
import org.rocksdb.RocksDBException;
import org.rocksdb.RocksIterator;
import org.rocksdb.Statistics;
import org.rocksdb.TransactionDB;
import org.rocksdb.TransactionDBOptions;
import org.rocksdb.WriteOptions;
public class ColumnarRocksDbKeyValueStorage
implements SegmentedKeyValueStorage<ColumnFamilyHandle>, Closeable {
private static final Logger LOG = LogManager.getLogger();
private static final String DEFAULT_COLUMN = "default";
private final DBOptions options;
private final TransactionDBOptions txOptions;
private final TransactionDB db;
private final AtomicBoolean closed = new AtomicBoolean(false);
private final OperationTimer readLatency;
private final OperationTimer removeLatency;
private final OperationTimer writeLatency;
private final OperationTimer commitLatency;
private final Counter rollbackCount;
private final Statistics stats;
private final Map<String, ColumnFamilyHandle> columnHandlesByName;
public static ColumnarRocksDbKeyValueStorage create(
final RocksDbConfiguration rocksDbConfiguration,
final List<Segment> segments,
final MetricsSystem metricsSystem)
throws StorageException {
return new ColumnarRocksDbKeyValueStorage(rocksDbConfiguration, segments, metricsSystem);
}
private ColumnarRocksDbKeyValueStorage(
final RocksDbConfiguration rocksDbConfiguration,
final List<Segment> segments,
final MetricsSystem metricsSystem) {
RocksDbUtil.loadNativeLibrary();
try {
final List<ColumnFamilyDescriptor> columnDescriptors =
segments.stream()
.map(segment -> new ColumnFamilyDescriptor(segment.getId()))
.collect(Collectors.toList());
columnDescriptors.add(
new ColumnFamilyDescriptor(
DEFAULT_COLUMN.getBytes(StandardCharsets.UTF_8),
new ColumnFamilyOptions()
.setTableFormatConfig(rocksDbConfiguration.getBlockBasedTableConfig())));
stats = new Statistics();
options =
new DBOptions()
.setCreateIfMissing(true)
.setMaxOpenFiles(rocksDbConfiguration.getMaxOpenFiles())
.setMaxBackgroundCompactions(rocksDbConfiguration.getMaxBackgroundCompactions())
.setStatistics(stats)
.setCreateMissingColumnFamilies(true)
.setEnv(
Env.getDefault()
.setBackgroundThreads(rocksDbConfiguration.getBackgroundThreadCount()));
txOptions = new TransactionDBOptions();
final List<ColumnFamilyHandle> columnHandles = new ArrayList<>(columnDescriptors.size());
db =
TransactionDB.open(
options,
txOptions,
rocksDbConfiguration.getDatabaseDir().toString(),
columnDescriptors,
columnHandles);
final Map<BytesValue, String> segmentsById =
segments.stream()
.collect(
Collectors.toMap(segment -> BytesValue.wrap(segment.getId()), Segment::getName));
final ImmutableMap.Builder<String, ColumnFamilyHandle> builder = ImmutableMap.builder();
for (final ColumnFamilyHandle columnHandle : columnHandles) {
final String segmentName = segmentsById.get(BytesValue.wrap(columnHandle.getName()));
if (segmentName != null) {
builder.put(segmentName, columnHandle);
} else {
builder.put(DEFAULT_COLUMN, columnHandle);
}
}
columnHandlesByName = builder.build();
readLatency =
metricsSystem
.createLabelledTimer(
PantheonMetricCategory.KVSTORE_ROCKSDB,
"read_latency_seconds",
"Latency for read from RocksDB.",
"database")
.labels(rocksDbConfiguration.getLabel());
removeLatency =
metricsSystem
.createLabelledTimer(
PantheonMetricCategory.KVSTORE_ROCKSDB,
"remove_latency_seconds",
"Latency of remove requests from RocksDB.",
"database")
.labels(rocksDbConfiguration.getLabel());
writeLatency =
metricsSystem
.createLabelledTimer(
PantheonMetricCategory.KVSTORE_ROCKSDB,
"write_latency_seconds",
"Latency for write to RocksDB.",
"database")
.labels(rocksDbConfiguration.getLabel());
commitLatency =
metricsSystem
.createLabelledTimer(
PantheonMetricCategory.KVSTORE_ROCKSDB,
"commit_latency_seconds",
"Latency for commits to RocksDB.",
"database")
.labels(rocksDbConfiguration.getLabel());
if (metricsSystem instanceof PrometheusMetricsSystem) {
RocksDBStats.registerRocksDBMetrics(stats, (PrometheusMetricsSystem) metricsSystem);
}
metricsSystem.createLongGauge(
PantheonMetricCategory.KVSTORE_ROCKSDB,
"rocks_db_table_readers_memory_bytes",
"Estimated memory used for RocksDB index and filter blocks in bytes",
() -> {
try {
return db.getLongProperty("rocksdb.estimate-table-readers-mem");
} catch (final RocksDBException e) {
LOG.debug("Failed to get RocksDB metric", e);
return 0L;
}
});
rollbackCount =
metricsSystem
.createLabelledCounter(
PantheonMetricCategory.KVSTORE_ROCKSDB,
"rollback_count",
"Number of RocksDB transactions rolled back.",
"database")
.labels(rocksDbConfiguration.getLabel());
} catch (final RocksDBException e) {
throw new StorageException(e);
}
}
@Override
public ColumnFamilyHandle getSegmentIdentifierByName(final Segment segment) {
return columnHandlesByName.get(segment.getName());
}
@Override
public Optional<BytesValue> get(final ColumnFamilyHandle segment, final BytesValue key)
throws StorageException {
throwIfClosed();
try (final OperationTimer.TimingContext ignored = readLatency.startTimer()) {
return Optional.ofNullable(db.get(segment, key.getArrayUnsafe())).map(BytesValue::wrap);
} catch (final RocksDBException e) {
throw new StorageException(e);
}
}
@Override
public Transaction<ColumnFamilyHandle> startTransaction() throws StorageException {
throwIfClosed();
final WriteOptions options = new WriteOptions();
return new RocksDbTransaction(db.beginTransaction(options), options);
}
@Override
public long removeUnless(
final ColumnFamilyHandle segmentHandle, final Predicate<BytesValue> inUseCheck) {
long removedNodeCounter = 0;
try (final RocksIterator rocksIterator = db.newIterator(segmentHandle)) {
rocksIterator.seekToFirst();
while (rocksIterator.isValid()) {
final byte[] key = rocksIterator.key();
if (!inUseCheck.test(BytesValue.wrap(key))) {
removedNodeCounter++;
db.delete(segmentHandle, key);
}
rocksIterator.next();
}
} catch (final RocksDBException e) {
throw new KeyValueStorage.StorageException(e);
}
return removedNodeCounter;
}
@Override
public void clear(final ColumnFamilyHandle segmentHandle) {
try (final RocksIterator rocksIterator = db.newIterator(segmentHandle)) {
rocksIterator.seekToFirst();
if (rocksIterator.isValid()) {
final byte[] firstKey = rocksIterator.key();
rocksIterator.seekToLast();
if (rocksIterator.isValid()) {
db.deleteRange(segmentHandle, firstKey, rocksIterator.key());
}
}
} catch (final RocksDBException e) {
throw new KeyValueStorage.StorageException(e);
}
}
@Override
public void close() {
if (closed.compareAndSet(false, true)) {
txOptions.close();
options.close();
columnHandlesByName.values().forEach(ColumnFamilyHandle::close);
db.close();
}
}
private void throwIfClosed() {
if (closed.get()) {
LOG.error("Attempting to use a closed RocksDbKeyValueStorage");
throw new IllegalStateException("Storage has been closed");
}
}
private class RocksDbTransaction extends AbstractTransaction<ColumnFamilyHandle> {
private final org.rocksdb.Transaction innerTx;
private final WriteOptions options;
RocksDbTransaction(final org.rocksdb.Transaction innerTx, final WriteOptions options) {
this.innerTx = innerTx;
this.options = options;
}
@Override
protected void doPut(
final ColumnFamilyHandle segment, final BytesValue key, final BytesValue value) {
try (final OperationTimer.TimingContext ignored = writeLatency.startTimer()) {
innerTx.put(segment, key.getArrayUnsafe(), value.getArrayUnsafe());
} catch (final RocksDBException e) {
throw new StorageException(e);
}
}
@Override
protected void doRemove(final ColumnFamilyHandle segment, final BytesValue key) {
try (final OperationTimer.TimingContext ignored = removeLatency.startTimer()) {
innerTx.delete(segment, key.getArrayUnsafe());
} catch (final RocksDBException e) {
throw new StorageException(e);
}
}
@Override
protected void doCommit() throws StorageException {
try (final OperationTimer.TimingContext ignored = commitLatency.startTimer()) {
innerTx.commit();
} catch (final RocksDBException e) {
throw new StorageException(e);
} finally {
close();
}
}
@Override
protected void doRollback() {
try {
innerTx.rollback();
rollbackCount.inc();
} catch (final RocksDBException e) {
throw new StorageException(e);
} finally {
close();
}
}
private void close() {
innerTx.close();
options.close();
}
}
}

@ -14,6 +14,7 @@ package tech.pegasys.pantheon.services.kvstore;
import tech.pegasys.pantheon.util.bytes.BytesValue;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@ -22,6 +23,7 @@ import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Predicate;
public class InMemoryKeyValueStorage implements KeyValueStorage {
@ -39,11 +41,43 @@ public class InMemoryKeyValueStorage implements KeyValueStorage {
}
}
@Override
public boolean containsKey(final BytesValue key) throws StorageException {
final Lock lock = rwLock.readLock();
lock.lock();
try {
return hashValueStore.containsKey(key);
} finally {
lock.unlock();
}
}
@Override
public Transaction startTransaction() {
return new InMemoryTransaction();
}
@Override
public long removeUnless(final Predicate<BytesValue> inUseCheck) {
hashValueStore.keySet().removeIf(key -> !inUseCheck.test(key));
return 0;
}
@Override
public void clear() {
final Lock lock = rwLock.writeLock();
lock.lock();
try {
hashValueStore.clear();
} finally {
lock.unlock();
}
}
public Set<BytesValue> keySet() {
return Collections.unmodifiableSet(new HashSet<>(hashValueStore.keySet()));
}
@Override
public void close() {}

@ -19,6 +19,7 @@ import tech.pegasys.pantheon.util.bytes.BytesValue;
import java.io.Closeable;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Predicate;
/** Service provided by pantheon to facilitate persistent data storage. */
public interface KeyValueStorage extends Closeable {
@ -29,6 +30,10 @@ public interface KeyValueStorage extends Closeable {
*/
Optional<BytesValue> get(BytesValue key) throws StorageException;
default boolean containsKey(final BytesValue key) throws StorageException {
return get(key).isPresent();
}
/**
* Begins a transaction. Returns a transaction object that can be updated and committed.
*
@ -36,6 +41,10 @@ public interface KeyValueStorage extends Closeable {
*/
Transaction startTransaction() throws StorageException;
long removeUnless(Predicate<BytesValue> inUseCheck);
void clear();
class Entry {
private final BytesValue key;
private final BytesValue value;

@ -28,16 +28,19 @@ public class RocksDbConfiguration {
private final String label;
private final int maxBackgroundCompactions;
private final int backgroundThreadCount;
private final boolean useColumns;
public RocksDbConfiguration(
final Path databaseDir,
final int maxOpenFiles,
final int maxBackgroundCompactions,
final int backgroundThreadCount,
final boolean useColumns,
final LRUCache cache,
final String label) {
this.maxBackgroundCompactions = maxBackgroundCompactions;
this.backgroundThreadCount = backgroundThreadCount;
this.useColumns = useColumns;
RocksDbUtil.loadNativeLibrary();
this.databaseDir = databaseDir;
this.maxOpenFiles = maxOpenFiles;
@ -69,6 +72,10 @@ public class RocksDbConfiguration {
return label;
}
public boolean useColumns() {
return useColumns;
}
public static class Builder {
Path databaseDir;
@ -108,6 +115,15 @@ public class RocksDbConfiguration {
description = "Number of RocksDB background threads (default: ${DEFAULT-VALUE})")
int backgroundThreadCount;
@CommandLine.Option(
names = {"--Xrocksdb-columns-enabled"},
hidden = true,
defaultValue = "false",
paramLabel = "<BOOLEAN>",
description =
"Whether to separate chain and world state into separate RocksDB columns (default: ${DEFAULT-VALUE})")
boolean useColumns = false;
public Builder databaseDir(final Path databaseDir) {
this.databaseDir = databaseDir;
return this;
@ -138,6 +154,11 @@ public class RocksDbConfiguration {
return this;
}
public Builder useColumns(final boolean useColumns) {
this.useColumns = useColumns;
return this;
}
private LRUCache createCache(final long cacheCapacity) {
RocksDbUtil.loadNativeLibrary();
return new LRUCache(cacheCapacity);
@ -148,7 +169,13 @@ public class RocksDbConfiguration {
cache = createCache(cacheCapacity);
}
return new RocksDbConfiguration(
databaseDir, maxOpenFiles, maxBackgroundCompactions, backgroundThreadCount, cache, label);
databaseDir,
maxOpenFiles,
maxBackgroundCompactions,
backgroundThreadCount,
useColumns,
cache,
label);
}
}
}

@ -24,11 +24,13 @@ import tech.pegasys.pantheon.util.bytes.BytesValue;
import java.io.Closeable;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Predicate;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.rocksdb.Options;
import org.rocksdb.RocksDBException;
import org.rocksdb.RocksIterator;
import org.rocksdb.Statistics;
import org.rocksdb.TransactionDB;
import org.rocksdb.TransactionDBOptions;
@ -154,6 +156,43 @@ public class RocksDbKeyValueStorage implements KeyValueStorage, Closeable {
return new RocksDbTransaction(db.beginTransaction(options), options);
}
@Override
public long removeUnless(final Predicate<BytesValue> inUseCheck) throws StorageException {
long removedNodeCounter = 0;
try (final RocksIterator rocksIterator = db.newIterator()) {
rocksIterator.seekToFirst();
while (rocksIterator.isValid()) {
final byte[] key = rocksIterator.key();
if (!inUseCheck.test(BytesValue.wrap(key))) {
removedNodeCounter++;
db.delete(key);
}
rocksIterator.next();
}
} catch (final RocksDBException e) {
throw new StorageException(e);
}
return removedNodeCounter;
}
@Override
public void clear() {
try (final RocksIterator rocksIterator = db.newIterator()) {
if (!rocksIterator.isValid()) {
return;
}
rocksIterator.seekToFirst();
final byte[] firstKey = rocksIterator.key();
rocksIterator.seekToLast();
if (!rocksIterator.isValid()) {
return;
}
db.deleteRange(firstKey, rocksIterator.key());
} catch (final RocksDBException e) {
throw new StorageException(e);
}
}
@Override
public void close() {
if (closed.compareAndSet(false, true)) {

@ -0,0 +1,143 @@
/*
* Copyright 2018 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.services.kvstore;
import static com.google.common.base.Preconditions.checkState;
import tech.pegasys.pantheon.util.bytes.BytesValue;
import java.io.Closeable;
import java.util.Optional;
import java.util.function.Predicate;
/**
* Service provided by pantheon to facilitate persistent data storage.
*
* @param <S> the segment identifier type
*/
public interface SegmentedKeyValueStorage<S> extends Closeable {
S getSegmentIdentifierByName(Segment segment);
/**
* @param segment the segment
* @param key Index into persistent data repository.
* @return The value persisted at the key index.
*/
Optional<BytesValue> get(S segment, BytesValue key) throws StorageException;
default boolean containsKey(final S segment, final BytesValue key) throws StorageException {
return get(segment, key).isPresent();
}
/**
* Begins a transaction. Returns a transaction object that can be updated and committed.
*
* @return An object representing the transaction.
*/
Transaction<S> startTransaction() throws StorageException;
long removeUnless(S segmentHandle, Predicate<BytesValue> inUseCheck);
void clear(S segmentHandle);
class StorageException extends RuntimeException {
public StorageException(final Throwable t) {
super(t);
}
}
/**
* Represents a set of changes to be committed atomically. A single transaction is not
* thread-safe, but multiple transactions can execute concurrently.
*
* @param <S> the segment identifier type
*/
interface Transaction<S> {
/**
* Add the given key-value pair to the set of updates to be committed.
*
* @param segment the database segment
* @param key The key to set / modify.
* @param value The value to be set.
*/
void put(S segment, BytesValue key, BytesValue value);
/**
* Schedules the given key to be deleted from storage.
*
* @param segment the database segment
* @param key The key to delete
*/
void remove(S segment, BytesValue key);
/**
* Atomically commit the set of changes contained in this transaction to the underlying
* key-value storage from which this transaction was started. After committing, the transaction
* is no longer usable and will throw exceptions if modifications are attempted.
*/
void commit() throws StorageException;
/**
* Cancel this transaction. After rolling back, the transaction is no longer usable and will
* throw exceptions if modifications are attempted.
*/
void rollback();
}
interface Segment {
String getName();
byte[] getId();
}
abstract class AbstractTransaction<S> implements Transaction<S> {
private boolean active = true;
@Override
public final void put(final S segment, final BytesValue key, final BytesValue value) {
checkState(active, "Cannot invoke put() on a completed transaction.");
doPut(segment, key, value);
}
@Override
public final void remove(final S segment, final BytesValue key) {
checkState(active, "Cannot invoke remove() on a completed transaction.");
doRemove(segment, key);
}
@Override
public final void commit() throws StorageException {
checkState(active, "Cannot commit a completed transaction.");
active = false;
doCommit();
}
@Override
public final void rollback() {
checkState(active, "Cannot rollback a completed transaction.");
active = false;
doRollback();
}
protected abstract void doPut(S segment, BytesValue key, BytesValue value);
protected abstract void doRemove(S segment, BytesValue key);
protected abstract void doCommit() throws StorageException;
protected abstract void doRollback();
}
}

@ -0,0 +1,82 @@
/*
* Copyright 2019 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.services.kvstore;
import tech.pegasys.pantheon.services.kvstore.SegmentedKeyValueStorage.Segment;
import tech.pegasys.pantheon.util.bytes.BytesValue;
import java.io.IOException;
import java.util.Optional;
import java.util.function.Predicate;
public class SegmentedKeyValueStorageAdapter<S> implements KeyValueStorage {
private final S segmentHandle;
private final SegmentedKeyValueStorage<S> storage;
public SegmentedKeyValueStorageAdapter(
final Segment segment, final SegmentedKeyValueStorage<S> storage) {
this.segmentHandle = storage.getSegmentIdentifierByName(segment);
this.storage = storage;
}
@Override
public Optional<BytesValue> get(final BytesValue key) throws StorageException {
return storage.get(segmentHandle, key);
}
@Override
public boolean containsKey(final BytesValue key) throws StorageException {
return storage.containsKey(segmentHandle, key);
}
@Override
public Transaction startTransaction() throws StorageException {
final SegmentedKeyValueStorage.Transaction<S> transaction = storage.startTransaction();
return new Transaction() {
@Override
public void put(final BytesValue key, final BytesValue value) {
transaction.put(segmentHandle, key, value);
}
@Override
public void remove(final BytesValue key) {
transaction.remove(segmentHandle, key);
}
@Override
public void commit() throws StorageException {
transaction.commit();
}
@Override
public void rollback() {
transaction.rollback();
}
};
}
@Override
public long removeUnless(final Predicate<BytesValue> inUseCheck) {
return storage.removeUnless(segmentHandle, inUseCheck);
}
@Override
public void clear() {
storage.clear(segmentHandle);
}
@Override
public void close() throws IOException {
storage.close();
}
}

@ -0,0 +1,112 @@
/*
* Copyright 2019 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package tech.pegasys.pantheon.services.kvstore;
import static org.junit.Assert.assertEquals;
import tech.pegasys.pantheon.metrics.noop.NoOpMetricsSystem;
import tech.pegasys.pantheon.services.kvstore.SegmentedKeyValueStorage.Segment;
import tech.pegasys.pantheon.services.kvstore.SegmentedKeyValueStorage.Transaction;
import tech.pegasys.pantheon.util.bytes.BytesValue;
import java.util.Arrays;
import java.util.Optional;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.rocksdb.ColumnFamilyHandle;
public class ColumnarRocksDbKeyValueStorageTest extends AbstractKeyValueStorageTest {
@Rule public final TemporaryFolder folder = new TemporaryFolder();
@Test
public void twoSegmentsAreIndependent() throws Exception {
final SegmentedKeyValueStorage<ColumnFamilyHandle> store = createSegmentedStore();
Transaction<ColumnFamilyHandle> tx = store.startTransaction();
tx.put(
store.getSegmentIdentifierByName(TestSegment.BAR),
BytesValue.fromHexString("0001"),
BytesValue.fromHexString("0FFF"));
tx.commit();
final Optional<BytesValue> result =
store.get(
store.getSegmentIdentifierByName(TestSegment.FOO), BytesValue.fromHexString("0001"));
assertEquals(Optional.empty(), result);
}
@Test
public void canRemoveThroughSegmentIteration() throws Exception {
final SegmentedKeyValueStorage<ColumnFamilyHandle> store = createSegmentedStore();
final ColumnFamilyHandle fooSegment = store.getSegmentIdentifierByName(TestSegment.FOO);
final ColumnFamilyHandle barSegment = store.getSegmentIdentifierByName(TestSegment.BAR);
Transaction<ColumnFamilyHandle> tx = store.startTransaction();
tx.put(fooSegment, BytesValue.of(1), BytesValue.of(1));
tx.put(fooSegment, BytesValue.of(2), BytesValue.of(2));
tx.put(fooSegment, BytesValue.of(3), BytesValue.of(3));
tx.put(barSegment, BytesValue.of(4), BytesValue.of(4));
tx.put(barSegment, BytesValue.of(5), BytesValue.of(5));
tx.put(barSegment, BytesValue.of(6), BytesValue.of(6));
tx.commit();
final long removedFromFoo = store.removeUnless(fooSegment, x -> x.equals(BytesValue.of(3)));
final long removedFromBar = store.removeUnless(barSegment, x -> x.equals(BytesValue.of(4)));
assertEquals(2, removedFromFoo);
assertEquals(2, removedFromBar);
assertEquals(Optional.empty(), store.get(fooSegment, BytesValue.of(1)));
assertEquals(Optional.empty(), store.get(fooSegment, BytesValue.of(2)));
assertEquals(Optional.of(BytesValue.of(3)), store.get(fooSegment, BytesValue.of(3)));
assertEquals(Optional.of(BytesValue.of(4)), store.get(barSegment, BytesValue.of(4)));
assertEquals(Optional.empty(), store.get(barSegment, BytesValue.of(5)));
assertEquals(Optional.empty(), store.get(barSegment, BytesValue.of(6)));
}
public enum TestSegment implements Segment {
FOO(new byte[] {1}),
BAR(new byte[] {2});
private final byte[] id;
TestSegment(final byte[] id) {
this.id = id;
}
@Override
public String getName() {
return name();
}
@Override
public byte[] getId() {
return id;
}
}
private SegmentedKeyValueStorage<ColumnFamilyHandle> createSegmentedStore() throws Exception {
return ColumnarRocksDbKeyValueStorage.create(
new RocksDbConfiguration.Builder().databaseDir(folder.newFolder().toPath()).build(),
Arrays.asList(TestSegment.FOO, TestSegment.BAR),
new NoOpMetricsSystem());
}
@Override
protected KeyValueStorage createStore() throws Exception {
return new SegmentedKeyValueStorageAdapter<ColumnFamilyHandle>(
TestSegment.FOO, createSegmentedStore());
}
}

@ -24,7 +24,10 @@ public class RocksDbKeyValueStorageTest extends AbstractKeyValueStorageTest {
@Override
protected KeyValueStorage createStore() throws Exception {
return RocksDbKeyValueStorage.create(
new RocksDbConfiguration.Builder().databaseDir(folder.newFolder().toPath()).build(),
new RocksDbConfiguration.Builder()
.databaseDir(folder.newFolder().toPath())
.useColumns(false)
.build(),
new NoOpMetricsSystem());
}
}

Loading…
Cancel
Save