Skip to content

Commit

Permalink
refactor: checkstyle and javadoc changes
Browse files Browse the repository at this point in the history
Signed-off-by: Kevin O'Donnell <kevin@blockchaintp.com>
  • Loading branch information
scealiontach committed Jul 17, 2021
1 parent aa86a7b commit 965f0ea
Show file tree
Hide file tree
Showing 16 changed files with 183 additions and 10 deletions.
20 changes: 20 additions & 0 deletions src/main/java/com/blockchaintp/daml/stores/LRUCache.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,21 @@
import com.blockchaintp.daml.stores.service.Key;
import com.blockchaintp.daml.stores.service.Value;

/**
* A simple LRU cache.
* @param <K> the key type
* @param <V> the value type
*/
public class LRUCache<K, V> extends LinkedHashMap<Key<K>, Value<V>> {
private static final float DEFAULT_LOAD_FACTOR = 0.75f;
private static final int INITIAL_SIZE = 16;
private static final long serialVersionUID = 1L;
private int cacheSize;

/**
* Construct the cache with the specified max size.
* @param maxSize the maximum size of the cache
*/
public LRUCache(final int maxSize) {
super(INITIAL_SIZE, DEFAULT_LOAD_FACTOR, true);
this.cacheSize = maxSize;
Expand All @@ -21,4 +30,15 @@ public LRUCache(final int maxSize) {
protected final boolean removeEldestEntry(final Map.Entry<Key<K>, Value<V>> eldest) {
return size() >= cacheSize;
}

@Override
public final int hashCode() {
return super.hashCode();
}

@Override
public final boolean equals(final Object o) {
// Two LRUCaches may be equal iff they are the same instance.
return this == o;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,15 @@

import java.security.NoSuchAlgorithmException;

/**
* A serious error that indicates that the JVM environment does not support SHA-512.
*/
public class NoSHA512SupportException extends RuntimeException {

/**
* Exception with cause.
* @param cause
*/
public NoSHA512SupportException(final NoSuchAlgorithmException cause) {
super("No SHA-512 support available", cause);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/**
* Exceptions for the stores package.
*/
package com.blockchaintp.daml.stores.exception;
10 changes: 10 additions & 0 deletions src/main/java/com/blockchaintp/daml/stores/layers/Caching.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,21 @@
import com.blockchaintp.daml.stores.service.Value;
import com.google.common.collect.Sets;

/**
* Layer that caches the results of a store.
* @param <K> the type of key
* @param <V> the type of value
*/
public class Caching<K, V> implements Store<K, V> {

private final Store<K, V> store;
private final LRUCache<K, V> innerCache;

/**
* Build a cache for the given store using the given cache.
* @param cache the cache to use
* @param wrappedStore the store to cache
*/
public Caching(final LRUCache<K, V> cache, final Store<K, V> wrappedStore) {
this.store = wrappedStore;
innerCache = cache;
Expand Down
31 changes: 28 additions & 3 deletions src/main/java/com/blockchaintp/daml/stores/layers/Retrying.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
import kr.pe.kwonnam.slf4jlambda.LambdaLogger;
import kr.pe.kwonnam.slf4jlambda.LambdaLoggerFactory;

/**
* A {@link Store} layer which retries the read operation if an exception occurs.
* @param <K> Key type
* @param <V> Value type
*/
public class Retrying<K, V> implements Store<K, V> {

private static final LambdaLogger LOG = LambdaLoggerFactory.getLogger(Retrying.class);
Expand All @@ -25,14 +30,19 @@ public class Retrying<K, V> implements Store<K, V> {
private final Retry getRetry;
private final Retry putRetry;

/**
* Construct the {@link Retrying} layer around the provided {@link Store}.
* @param config the configuration for the retry
* @param wrappedStore the {@link Store} to wrap
*/
public Retrying(final Config config, final Store<K, V> wrappedStore) {
this.store = wrappedStore;

this.getRetry = Retry.of(String.format("%s#get", store.getClass().getCanonicalName()), RetryConfig.custom()
.maxAttempts(config.maxRetries).retryOnException(StoreReadException.class::isInstance).build());
.maxAttempts(config.getMaxRetries()).retryOnException(StoreReadException.class::isInstance).build());

this.putRetry = Retry.of(String.format("%s#put", store.getClass().getCanonicalName()), RetryConfig.custom()
.maxAttempts(config.maxRetries).retryOnException(StoreWriteException.class::isInstance).build());
.maxAttempts(config.getMaxRetries()).retryOnException(StoreWriteException.class::isInstance).build());

getRetry.getEventPublisher().onRetry(r -> LOG.info("Retrying {} attempt {} due to {}", r::getName,
r::getNumberOfRetryAttempts, r::getLastThrowable, () -> r.getLastThrowable().getMessage()));
Expand Down Expand Up @@ -101,11 +111,26 @@ public void put(final List<Map.Entry<Key<K>, Value<V>>> listOfPairs) throws Stor
decoratePut(() -> store.put(listOfPairs));
}

/**
* Configuration for a{@link Retrying} layer.
*/
public static class Config {
private static final int DEFAULT_MAX_RETRIES = 3;
/**
* The maximum number of retries.
*/
public int maxRetries = DEFAULT_MAX_RETRIES;
private int maxRetries = DEFAULT_MAX_RETRIES;
/**
* @return the maxRetries
*/
public int getMaxRetries() {
return maxRetries;
}
/**
* @param retries the maxRetries to set
*/
public void setMaxRetries(final int retries) {
this.maxRetries = retries;
}
}
}
14 changes: 14 additions & 0 deletions src/main/java/com/blockchaintp/daml/stores/layers/SplitStore.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@
import com.blockchaintp.daml.stores.service.Value;
import com.google.protobuf.ByteString;

/**
* A Store that keeps its values in a blob store which is keyed by the hash of
* the value. That original key is stored along with that hash in the
* TransactionLog.
*/
public class SplitStore implements TransactionLog<ByteString, ByteString> {

private final boolean writeS3Index;
Expand All @@ -30,6 +35,15 @@ public class SplitStore implements TransactionLog<ByteString, ByteString> {
private final IonSystem ion;
private final UnaryOperator<byte[]> hashFn;

/**
* Constructs a new SplitStore.
* @param s3Index whether to allow fetching from the blob store if the value has been verified
* @param indexReader a reader for the index, verified or not
* @param txLog the TransactionLog to use
* @param blobStore the blob store to use
* @param sys the IonSystem to use
* @param hashingFn the hash function to use
*/
public SplitStore(final boolean s3Index, final StoreReader<ByteString, ByteString> indexReader,
final TransactionLog<IonValue, IonStruct> txLog, final Store<String, byte[]> blobStore, final IonSystem sys,
final UnaryOperator<byte[]> hashingFn) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
import com.blockchaintp.daml.stores.service.TransactionLog;
import com.google.protobuf.ByteString;

/**
* A builder of a {@link SplitStore}.
*/
public class SplitStoreBuilder {
private IonSystem ion;
private TransactionLog<IonValue, IonStruct> txLog;
Expand All @@ -21,6 +24,12 @@ public class SplitStoreBuilder {
private UnaryOperator<byte[]> hashFn;
private boolean writeS3Index = false;

/**
* Create a SplitStoreBuilder.
* @param sys the IonSystem
* @param transactionLog the transaction log to use
* @param blobStore the blob store to use
*/
public SplitStoreBuilder(final IonSystem sys, final TransactionLog<IonValue, IonStruct> transactionLog,
final Store<String, byte[]> blobStore) {
this.ion = sys;
Expand All @@ -40,6 +49,11 @@ public SplitStoreBuilder(final IonSystem sys, final TransactionLog<IonValue, Ion
this.reader = new VerifiedReader(transactionLog, blobStore, sys);
}

/**
* Whether or not to allow verified reads.
* @param verified {@code true} to allow verified reads
* @return the builder
*/
public final SplitStoreBuilder verified(final boolean verified) {
if (verified) {
this.reader = new VerifiedReader(txLog, s3Store, ion);
Expand All @@ -50,18 +64,32 @@ public final SplitStoreBuilder verified(final boolean verified) {
return this;
}

/**
* Whether to write the S3 index.
* @param s3Index {@code true} to write the S3 index
* @return the builder
*/
public final SplitStoreBuilder withS3Index(final boolean s3Index) {
this.writeS3Index = s3Index;

return this;
}

/**
* Use the given hash function to hash the contents of a blob.
* @param hasherFn the hash function
* @return the builder
*/
public final SplitStoreBuilder withHasher(final UnaryOperator<byte[]> hasherFn) {
this.hashFn = hasherFn;

return this;
}

/**
* Build the split store.
* @return the split store
*/
public final SplitStore build() {
return new SplitStore(writeS3Index, reader, txLog, s3Store, ion, hashFn);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ public class UnVerifiedReader implements StoreReader<ByteString, ByteString> {

private final Store<String, byte[]> blobStore;

/**
* Construct an unverified reader around the provided store.
* @param blobs
*/
public UnVerifiedReader(final Store<String, byte[]> blobs) {
this.blobStore = blobs;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ public class VerifiedReader implements StoreReader<ByteString, ByteString> {
private final Store<String, byte[]> blobStore;
private final IonSystem ion;

/**
* Construct a VerifiedReader around the provided stores.
* @param txlog the transaction log which masters the K->Hash map.
* @param blobs the blob store which masters the Hash->Value map.
* @param sys the IonSystem to use for ION serialization.
*/
public VerifiedReader(final TransactionLog<IonValue, IonStruct> txlog, final Store<String, byte[]> blobs,
final IonSystem sys) {
this.txLog = txlog;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,16 @@ public class QldbRetryStrategy<K, V> extends Retrying<K, V> implements Transacti
private final Retry putRetry;
private int qldbMaxDocuments = DEFAULT_MAX_DOCUMENTS;

/**
* Constructor.
* @param config the retry config
* @param store the store, specifically meant to be used with a QLDBStore, but not required to do so
*/
public QldbRetryStrategy(final Config config, final Store<K, V> store) {
super(config, store);

this.putRetry = Retry.of(String.format("%s#put-qldb-batch", store.getClass().getCanonicalName()),
RetryConfig.custom().maxAttempts(config.maxRetries)
RetryConfig.custom().maxAttempts(config.getMaxRetries())
.retryOnException(QldbRetryStrategy::specificallyHandleCapacityExceptions).build());

putRetry.getEventPublisher().onRetry(r -> LOG.info("Retrying {} attempt {} due to {}", r::getName,
Expand Down
15 changes: 15 additions & 0 deletions src/main/java/com/blockchaintp/daml/stores/qldb/QldbStore.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,37 @@
import software.amazon.qldb.Result;
import software.amazon.qldb.exceptions.QldbDriverException;

/**
* A K/V store using Amazon QLDB as a backend.
*/
public class QldbStore implements TransactionLog<IonValue, IonStruct> {

private static final LambdaLogger LOG = LambdaLoggerFactory.getLogger(QldbStore.class);

private final QldbDriver driver;
private final String table;

/**
* Constructor for QldbStore.
* @param qldbDriver the driver to use
* @param tableName the table name to use
*/
public QldbStore(final QldbDriver qldbDriver, final String tableName) {
this.driver = qldbDriver;
this.table = tableName;
}

/**
* Return a builder for the specified driver.
* @param driver the driver to use
* @return the builder
*/
public static QldbStoreBuilder forDriver(final QldbDriver driver) {
return QldbStoreBuilder.forDriver(driver);
}

@Override
@SuppressWarnings("java:S1905")
public final Optional<Value<IonStruct>> get(final Key<IonValue> key) throws StoreReadException {
LOG.info("get id={} in table={}", () -> key, () -> table);
final var query = String.format("select o.* from %s AS o where o.id = ?", table);
Expand All @@ -62,6 +76,7 @@ public final Optional<Value<IonStruct>> get(final Key<IonValue> key) throws Stor
}

@Override
@SuppressWarnings("java:S1905")
public final Map<Key<IonValue>, Value<IonStruct>> get(final List<Key<IonValue>> listOfKeys)
throws StoreReadException {
LOG.info("get ids=({}) in table={}", () -> listOfKeys, () -> table);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

import software.amazon.qldb.QldbDriver;

/**
* A builder of QLDBStore instances.
*/
public final class QldbStoreBuilder {

private final QldbDriver driver;
Expand All @@ -11,16 +14,30 @@ private QldbStoreBuilder(final QldbDriver qldbDriver) {
this.driver = qldbDriver;
}

/**
* Use the given QLDB driver.
* @param driver the driver
* @return the builder
*/
public static QldbStoreBuilder forDriver(final QldbDriver driver) {
return new QldbStoreBuilder(driver);
}

/**
* Use the given table name.
* @param tableName the table name
* @return the builder
*/
public QldbStoreBuilder tableName(final String tableName) {
this.table = tableName;
return this;
}

public QldbStore build() throws QldbStoreBuilderException {
/**
* Construct a QLDBStore instance.
* @return the instance
*/
public QldbStore build() {
if (table == null) {
throw new QldbStoreBuilderException("No table name specified in builder");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
package com.blockchaintp.daml.stores.qldb;

/**
* An exception that is thrown when a QldbStoreBuilder is unable to build a
* QldbStore.
*/
public class QldbStoreBuilderException extends RuntimeException {
public QldbStoreBuilderException(final String cause) {
super(cause);
/**
* Exception with message.
* @param message the message
*/
public QldbStoreBuilderException(final String message) {
super(message);
}
}
Loading

0 comments on commit 965f0ea

Please sign in to comment.