de.otto.synapse.state.ChronicleMapStateRepository Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of synapse-core Show documentation
Show all versions of synapse-core Show documentation
A library used at otto.de to implement Spring Boot based event-sourcing microservices.
package de.otto.synapse.state;
import com.fasterxml.jackson.databind.ObjectMapper;
import net.openhft.chronicle.hash.ChronicleHashClosedException;
import net.openhft.chronicle.map.ChronicleMap;
import net.openhft.chronicle.map.ChronicleMapBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Optional;
import static de.otto.synapse.translator.ObjectMappers.currentObjectMapper;
/**
* A {@code StateRepository} that is using a {@code ChronicleMap} to store the event-sourced entities off the heap.
*
* Suitable for larger amounts of data, without problems because of garbage-collection issues because of the
* usage of off-heap memory.
*
* In order to be able to access the {@link #keySet()} of the stored entities, this implementation is storing
* a copy of the keys in a separate {@link java.util.concurrent.ConcurrentSkipListSet}
*
* @param The type of the event-sourced entities stored in the {@code StateRepository}
*/
public class ChronicleMapStateRepository extends ConcurrentMapStateRepository {
private static final Logger LOG = LoggerFactory.getLogger(ChronicleMapStateRepository.class);
private static final int DEFAULT_KEY_SIZE_BYTES = 128;
private static final double DEFAULT_VALUE_SIZE_BYTES = 512;
private static final long DEFAULT_ENTRY_COUNT = 100_000;
private ChronicleMapStateRepository(final String name,
final ChronicleMap chronicleMap) {
super(name, chronicleMap);
}
@Override
public Optional put(final String key, final V value) {
try {
return super.put(key, value);
} catch (ChronicleHashClosedException e) {
LOG.warn("could not put on closed state repository", e);
return Optional.empty();
}
}
@Override
public Optional get(final String key) {
try {
return super.get(key);
} catch (ChronicleHashClosedException e) {
LOG.warn("could not get on closed state repository", e);
return Optional.empty();
}
}
@Override
public long size() {
try {
return super.size();
} catch (final ChronicleHashClosedException e) {
LOG.warn("could not get size on closed state repository", e);
return 0;
}
}
public static Builder builder(Class clazz) {
return new Builder<>(clazz);
}
public static final class Builder {
private ObjectMapper objectMapper = currentObjectMapper();
private final Class clazz;
private String name;
private ChronicleMapBuilder chronicleMapBuilder;
private boolean customValueMarshaller = false;
private Builder(Class clazz) {
this.clazz = clazz;
this.name = clazz.getSimpleName();
}
public Builder withObjectMapper(ObjectMapper val) {
objectMapper = val;
return this;
}
public Builder withName(final String val) {
name = val;
return this;
}
public Builder withMapBuilder(ChronicleMapBuilder val) {
chronicleMapBuilder = val;
return this;
}
public Builder withCustomValueMarshaller(boolean val) {
this.customValueMarshaller = val;
return this;
}
public ChronicleMapStateRepository build() {
if (chronicleMapBuilder == null) {
chronicleMapBuilder = ChronicleMapBuilder.of(String.class, clazz)
.averageKeySize(DEFAULT_KEY_SIZE_BYTES)
.averageValueSize(DEFAULT_VALUE_SIZE_BYTES)
.entries(DEFAULT_ENTRY_COUNT);
}
boolean doesClassNeedToBeSerialized = clazz != String.class;
if (!customValueMarshaller && doesClassNeedToBeSerialized) {
chronicleMapBuilder.valueMarshaller(new ChronicleMapBytesMarshaller<>(objectMapper, clazz));
}
return new ChronicleMapStateRepository<>(name, chronicleMapBuilder.create());
}
}
}