CavalliumDBEngine/src/main/java/it/cavallium/dbengine/database/disk/AbstractRocksDBColumn.java

893 lines
28 KiB
Java
Raw Normal View History

2021-10-20 01:51:34 +02:00
package it.cavallium.dbengine.database.disk;
2022-03-16 13:47:56 +01:00
import static io.netty5.buffer.api.StandardAllocationTypes.OFF_HEAP;
2021-12-12 02:17:36 +01:00
import static it.cavallium.dbengine.database.LLUtils.INITIAL_DIRECT_READ_BYTE_BUF_SIZE_BYTES;
import static it.cavallium.dbengine.database.LLUtils.isReadOnlyDirect;
2022-05-28 14:34:35 +02:00
import static java.lang.Boolean.parseBoolean;
import static java.lang.System.getProperty;
2021-10-20 01:51:34 +02:00
import static java.util.Objects.requireNonNull;
2022-02-11 12:19:32 +01:00
import static org.rocksdb.KeyMayExist.KeyMayExistEnum.kExistsWithValue;
import static org.rocksdb.KeyMayExist.KeyMayExistEnum.kExistsWithoutValue;
2021-10-20 01:51:34 +02:00
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.DistributionSummary;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Timer;
2022-03-16 13:47:56 +01:00
import io.netty5.buffer.api.Buffer;
import io.netty5.buffer.api.BufferAllocator;
2022-03-16 19:19:26 +01:00
import io.netty5.buffer.api.DefaultBufferAllocators;
import io.netty5.buffer.api.ReadableComponent;
2022-03-16 13:47:56 +01:00
import io.netty5.buffer.api.WritableComponent;
import it.cavallium.dbengine.database.LLRange;
2021-10-20 01:51:34 +02:00
import it.cavallium.dbengine.database.LLUtils;
import it.cavallium.dbengine.database.RepeatedElementList;
2022-05-12 19:14:27 +02:00
import it.cavallium.dbengine.database.disk.rocksdb.RocksIteratorObj;
import it.cavallium.dbengine.database.serialization.SerializationException;
import java.io.IOException;
2021-10-20 01:51:34 +02:00
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
2021-10-20 01:51:34 +02:00
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.StampedLock;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
2021-10-20 01:51:34 +02:00
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.rocksdb.AbstractImmutableNativeReference;
import org.rocksdb.AbstractSlice;
2021-10-20 01:51:34 +02:00
import org.rocksdb.ColumnFamilyHandle;
import org.rocksdb.CompactRangeOptions;
import org.rocksdb.DirectSlice;
2021-10-20 01:51:34 +02:00
import org.rocksdb.FlushOptions;
import org.rocksdb.Holder;
2022-06-09 19:45:03 +02:00
import org.rocksdb.KeyMayExist;
2022-02-11 12:19:32 +01:00
import org.rocksdb.KeyMayExist.KeyMayExistEnum;
2021-10-20 01:51:34 +02:00
import org.rocksdb.ReadOptions;
import org.rocksdb.RocksDB;
import org.rocksdb.RocksDBException;
2022-05-12 19:14:27 +02:00
import org.rocksdb.RocksObject;
import org.rocksdb.Slice;
2021-10-20 01:51:34 +02:00
import org.rocksdb.Transaction;
2022-05-12 19:14:27 +02:00
import org.rocksdb.TransactionOptions;
2021-10-20 01:51:34 +02:00
import org.rocksdb.WriteBatch;
import org.rocksdb.WriteOptions;
import reactor.core.scheduler.Schedulers;
public sealed abstract class AbstractRocksDBColumn<T extends RocksDB> implements RocksDBColumn
permits StandardRocksDBColumn, OptimisticRocksDBColumn, PessimisticRocksDBColumn {
/**
* Default: true
*/
private static final boolean USE_DIRECT_BUFFER_BOUNDS = true;
2021-10-20 01:51:34 +02:00
private static final byte[] NO_DATA = new byte[0];
protected static final UpdateAtomicResult RESULT_NOTHING = new UpdateAtomicResultNothing();
protected final Logger logger = LogManager.getLogger(this.getClass());
2021-10-20 01:51:34 +02:00
private final T db;
2021-10-30 12:39:56 +02:00
private final boolean nettyDirect;
2021-10-20 01:51:34 +02:00
private final BufferAllocator alloc;
private final ColumnFamilyHandle cfh;
2021-10-20 01:51:34 +02:00
2022-03-30 18:36:07 +02:00
protected final MeterRegistry meterRegistry;
protected final StampedLock closeLock;
2022-03-30 18:36:07 +02:00
protected final String columnName;
protected final DistributionSummary keyBufferSize;
protected final DistributionSummary readValueNotFoundWithoutBloomBufferSize;
protected final DistributionSummary readValueNotFoundWithBloomBufferSize;
protected final DistributionSummary readValueNotFoundWithMayExistBloomBufferSize;
protected final DistributionSummary readValueFoundWithBloomUncachedBufferSize;
protected final DistributionSummary readValueFoundWithBloomCacheBufferSize;
protected final DistributionSummary readValueFoundWithBloomSimpleBufferSize;
protected final DistributionSummary readValueFoundWithoutBloomBufferSize;
protected final DistributionSummary writeValueBufferSize;
protected final DistributionSummary readAttempts;
private final Counter startedIterSeek;
private final Counter endedIterSeek;
private final Timer iterSeekTime;
private final Counter startedIterNext;
private final Counter endedIterNext;
private final Timer iterNextTime;
private final Counter startedUpdate;
private final Counter endedUpdate;
private final Timer updateAddedTime;
private final Timer updateReplacedTime;
private final Timer updateRemovedTime;
private final Timer updateUnchangedTime;
2022-06-09 19:45:03 +02:00
private final DBColumnKeyMayExistGetter keyMayExistGetter;
public AbstractRocksDBColumn(T db,
boolean nettyDirect,
BufferAllocator alloc,
String databaseName,
ColumnFamilyHandle cfh,
MeterRegistry meterRegistry,
StampedLock closeLock) {
2021-10-20 01:51:34 +02:00
this.db = db;
this.nettyDirect = nettyDirect && alloc.getAllocationType() == OFF_HEAP;
2021-10-20 01:51:34 +02:00
this.alloc = alloc;
this.cfh = cfh;
String columnName;
try {
columnName = new String(cfh.getName(), StandardCharsets.UTF_8);
} catch (RocksDBException e) {
throw new IllegalStateException(e);
}
2022-03-30 18:36:07 +02:00
this.columnName = columnName;
this.meterRegistry = meterRegistry;
this.closeLock = closeLock;
this.keyBufferSize = DistributionSummary
.builder("buffer.size.distribution")
.publishPercentiles(0.2, 0.5, 0.95)
.baseUnit("bytes")
.scale(1)
.publishPercentileHistogram()
.tags("db.name", databaseName, "db.column", columnName, "buffer.type", "key")
.register(meterRegistry);
this.readValueNotFoundWithoutBloomBufferSize = DistributionSummary
.builder("buffer.size.distribution")
.publishPercentiles(0.2, 0.5, 0.95)
.baseUnit("bytes")
.scale(1)
.publishPercentileHistogram()
2022-04-07 22:19:11 +02:00
.tags("db.name", databaseName, "db.column", columnName, "buffer.type", "val.read", "found", "false", "bloom", "disabled")
.register(meterRegistry);
this.readValueNotFoundWithBloomBufferSize = DistributionSummary
.builder("buffer.size.distribution")
.publishPercentiles(0.2, 0.5, 0.95)
.baseUnit("bytes")
.scale(1)
.publishPercentileHistogram()
2022-04-07 22:19:11 +02:00
.tags("db.name", databaseName, "db.column", columnName, "buffer.type", "val.read", "found", "false", "bloom", "enabled", "bloom.mayexist", "false")
.register(meterRegistry);
this.readValueNotFoundWithMayExistBloomBufferSize = DistributionSummary
.builder("buffer.size.distribution")
.publishPercentiles(0.2, 0.5, 0.95)
.baseUnit("bytes")
.scale(1)
.publishPercentileHistogram()
2022-04-07 22:19:11 +02:00
.tags("db.name", databaseName, "db.column", columnName, "buffer.type", "val.read", "found", "false", "bloom", "enabled", "bloom.mayexist", "true", "bloom.mayexist.result", "notexists", "bloom.mayexist.cached", "false")
.register(meterRegistry);
this.readValueFoundWithBloomUncachedBufferSize = DistributionSummary
.builder("buffer.size.distribution")
.publishPercentiles(0.2, 0.5, 0.95)
.baseUnit("bytes")
.scale(1)
.publishPercentileHistogram()
2022-04-07 22:19:11 +02:00
.tags("db.name", databaseName, "db.column", columnName, "buffer.type", "val.read", "found", "true", "bloom", "enabled", "bloom.mayexist", "true", "bloom.mayexist.result", "exists", "bloom.mayexist.cached", "false")
.register(meterRegistry);
this.readValueFoundWithBloomCacheBufferSize = DistributionSummary
.builder("buffer.size.distribution")
.publishPercentiles(0.2, 0.5, 0.95)
.baseUnit("bytes")
.scale(1)
.publishPercentileHistogram()
2022-04-07 22:19:11 +02:00
.tags("db.name", databaseName, "db.column", columnName, "buffer.type", "val.read", "found", "true", "bloom", "enabled", "bloom.mayexist", "true", "bloom.mayexist.result", "exists", "bloom.mayexist.cached", "true")
.register(meterRegistry);
this.readValueFoundWithBloomSimpleBufferSize = DistributionSummary
.builder("buffer.size.distribution")
.publishPercentiles(0.2, 0.5, 0.95)
.baseUnit("bytes")
.scale(1)
.publishPercentileHistogram()
2022-04-07 22:19:11 +02:00
.tags("db.name", databaseName, "db.column", columnName, "buffer.type", "val.read", "found", "true", "bloom", "enabled", "bloom.mayexist", "true", "bloom.mayexist.result", "exists", "bloom.mayexist.cached", "false")
.register(meterRegistry);
this.readValueFoundWithoutBloomBufferSize = DistributionSummary
.builder("buffer.size.distribution")
.publishPercentiles(0.2, 0.5, 0.95)
.baseUnit("bytes")
.scale(1)
.publishPercentileHistogram()
2022-04-07 22:19:11 +02:00
.tags("db.name", databaseName, "db.column", columnName, "buffer.type", "val.read", "found", "true", "bloom", "disabled")
.register(meterRegistry);
this.writeValueBufferSize = DistributionSummary
.builder("buffer.size.distribution")
.publishPercentiles(0.2, 0.5, 0.95)
.baseUnit("bytes")
.scale(1)
.publishPercentileHistogram()
.tags("db.name", databaseName, "db.column", columnName, "buffer.type", "val.write")
.register(meterRegistry);
this.readAttempts = DistributionSummary
.builder("db.read.attempts.distribution")
.publishPercentiles(0.2, 0.5, 0.95)
.baseUnit("times")
.scale(1)
.publishPercentileHistogram()
.tags("db.name", databaseName, "db.column", columnName)
.register(meterRegistry);
this.startedIterSeek = meterRegistry.counter("db.read.iter.seek.started.counter", "db.name", databaseName, "db.column", columnName);
this.endedIterSeek = meterRegistry.counter("db.read.iter.seek.ended.counter", "db.name", databaseName, "db.column", columnName);
this.iterSeekTime = Timer
.builder("db.read.iter.seek.timer")
.publishPercentiles(0.2, 0.5, 0.95)
.publishPercentileHistogram()
.tags("db.name", databaseName, "db.column", columnName)
.register(meterRegistry);
this.startedIterNext = meterRegistry.counter("db.read.iter.next.started.counter", "db.name", databaseName, "db.column", columnName);
this.endedIterNext = meterRegistry.counter("db.read.iter.next.ended.counter", "db.name", databaseName, "db.column", columnName);
this.iterNextTime = Timer
.builder("db.read.iter.next.timer")
.publishPercentiles(0.2, 0.5, 0.95)
.publishPercentileHistogram()
.tags("db.name", databaseName, "db.column", columnName)
.register(meterRegistry);
this.startedUpdate = meterRegistry.counter("db.write.update.started.counter", "db.name", databaseName, "db.column", columnName);
this.endedUpdate = meterRegistry.counter("db.write.update.ended.counter", "db.name", databaseName, "db.column", columnName);
this.updateAddedTime = Timer
.builder("db.write.update.timer")
.publishPercentiles(0.2, 0.5, 0.95)
.publishPercentileHistogram()
.tags("db.name", databaseName, "db.column", columnName, "update.type", "added")
.register(meterRegistry);
this.updateReplacedTime = Timer
.builder("db.write.update.timer")
.publishPercentiles(0.2, 0.5, 0.95)
.publishPercentileHistogram()
.tags("db.name", databaseName, "db.column", columnName, "update.type", "replaced")
.register(meterRegistry);
this.updateRemovedTime = Timer
.builder("db.write.update.timer")
.publishPercentiles(0.2, 0.5, 0.95)
.publishPercentileHistogram()
.tags("db.name", databaseName, "db.column", columnName, "update.type", "removed")
.register(meterRegistry);
this.updateUnchangedTime = Timer
.builder("db.write.update.timer")
.publishPercentiles(0.2, 0.5, 0.95)
.publishPercentileHistogram()
.tags("db.name", databaseName, "db.column", columnName, "update.type", "unchanged")
.register(meterRegistry);
2022-06-09 19:45:03 +02:00
this.keyMayExistGetter = new DBColumnKeyMayExistGetter();
2021-10-20 01:51:34 +02:00
}
/**
* This method should not modify or move the writerIndex/readerIndex of the key
*/
static AbstractSlice<?> setIterateBound(boolean allowNettyDirect,
ReadOptions readOpts, IterateBound boundType, Buffer key) {
requireNonNull(key);
AbstractSlice<?> slice;
if (allowNettyDirect && USE_DIRECT_BUFFER_BOUNDS && isReadOnlyDirect(key)) {
ByteBuffer keyInternalByteBuffer = ((ReadableComponent) key).readableBuffer();
assert keyInternalByteBuffer.position() == 0;
slice = new DirectSlice(keyInternalByteBuffer, key.readableBytes());
assert slice.size() == key.readableBytes();
} else {
slice = new Slice(requireNonNull(LLUtils.toArray(key)));
}
if (boundType == IterateBound.LOWER) {
readOpts.setIterateLowerBound(slice);
} else {
readOpts.setIterateUpperBound(slice);
}
2022-05-12 19:14:27 +02:00
return slice;
}
static Slice newEmptyReleasableSlice() {
var arr = new byte[0];
return new Slice(arr);
}
/**
* This method should not modify or move the writerIndex/readerIndex of the buffers inside the range
*/
@NotNull
2022-05-12 19:14:27 +02:00
public RocksIteratorObj newRocksIterator(boolean allowNettyDirect,
ReadOptions readOptions,
LLRange range,
boolean reverse) throws RocksDBException {
assert !Schedulers.isInNonBlockingThread() : "Called getRocksIterator in a nonblocking thread";
2022-05-12 19:14:27 +02:00
var rocksIterator = this.newIterator(readOptions, range.getMinUnsafe(), range.getMaxUnsafe());
2022-05-04 12:36:32 +02:00
try {
if (reverse) {
if (!LLLocalDictionary.PREFER_AUTO_SEEK_BOUND && range.hasMax()) {
2022-05-12 19:14:27 +02:00
rocksIterator.seekFrom(range.getMaxUnsafe());
2022-05-04 12:36:32 +02:00
} else {
rocksIterator.seekToLast();
}
} else {
2022-05-04 12:36:32 +02:00
if (!LLLocalDictionary.PREFER_AUTO_SEEK_BOUND && range.hasMin()) {
2022-05-12 19:14:27 +02:00
rocksIterator.seekTo(range.getMinUnsafe());
2022-05-04 12:36:32 +02:00
} else {
rocksIterator.seekToFirst();
}
}
2022-05-12 19:14:27 +02:00
return rocksIterator;
2022-05-04 12:36:32 +02:00
} catch (Throwable ex) {
rocksIterator.close();
throw ex;
}
}
2021-10-20 01:51:34 +02:00
protected T getDb() {
return db;
}
protected ColumnFamilyHandle getCfh() {
2021-10-20 01:51:34 +02:00
return cfh;
}
protected void ensureOpen() {
2022-04-30 14:21:20 +02:00
RocksDBUtils.ensureOpen(db, cfh);
}
protected void ensureOwned(AbstractImmutableNativeReference rocksObject) {
2022-04-30 14:21:20 +02:00
RocksDBUtils.ensureOwned(rocksObject);
}
2022-05-12 19:14:27 +02:00
protected void ensureOwned(RocksObject rocksObject) {
RocksDBUtils.ensureOwned(rocksObject);
}
protected void ensureOwned(Buffer buffer) {
if (buffer != null && !buffer.isAccessible()) {
throw new IllegalStateException("Buffer is not accessible");
}
}
@Override
public @Nullable Buffer get(@NotNull ReadOptions readOptions, Buffer key) throws RocksDBException {
var closeReadLock = closeLock.readLock();
try {
ensureOpen();
ensureOwned(readOptions);
2022-06-09 19:45:03 +02:00
return keyMayExistGetter.get(readOptions, key);
} finally {
closeLock.unlockRead(closeReadLock);
2021-12-12 02:17:36 +01:00
}
}
@Override
public void put(@NotNull WriteOptions writeOptions, Buffer key, Buffer value) throws RocksDBException {
var closeReadLock = closeLock.readLock();
try {
2022-05-10 16:57:41 +02:00
ensureOpen();
ensureOwned(writeOptions);
assert key.isAccessible();
assert value.isAccessible();
this.keyBufferSize.record(key.readableBytes());
this.writeValueBufferSize.record(value.readableBytes());
if (nettyDirect) {
// Get the key nio buffer to pass to RocksDB
ByteBuffer keyNioBuffer;
boolean mustCloseKey;
{
if (!LLUtils.isReadOnlyDirect(key)) {
// If the nio buffer is not available, copy the netty buffer into a new direct buffer
mustCloseKey = true;
var directKey = DefaultBufferAllocators.offHeapAllocator().allocate(key.readableBytes());
key.copyInto(key.readerOffset(), directKey, 0, key.readableBytes());
key = directKey;
} else {
mustCloseKey = false;
}
keyNioBuffer = ((ReadableComponent) key).readableBuffer();
assert keyNioBuffer.isDirect();
assert keyNioBuffer.limit() == key.readableBytes();
}
try {
// Get the value nio buffer to pass to RocksDB
ByteBuffer valueNioBuffer;
boolean mustCloseValue;
{
2022-05-10 16:57:41 +02:00
if (!LLUtils.isReadOnlyDirect(value)) {
// If the nio buffer is not available, copy the netty buffer into a new direct buffer
2022-05-10 16:57:41 +02:00
mustCloseValue = true;
var directValue = DefaultBufferAllocators.offHeapAllocator().allocate(value.readableBytes());
value.copyInto(value.readerOffset(), directValue, 0, value.readableBytes());
value = directValue;
} else {
2022-05-10 16:57:41 +02:00
mustCloseValue = false;
}
2022-05-10 16:57:41 +02:00
valueNioBuffer = ((ReadableComponent) value).readableBuffer();
assert valueNioBuffer.isDirect();
assert valueNioBuffer.limit() == value.readableBytes();
}
2022-05-10 16:57:41 +02:00
try {
db.put(cfh, writeOptions, keyNioBuffer, valueNioBuffer);
} finally {
2022-05-10 16:57:41 +02:00
if (mustCloseValue) {
value.close();
}
}
2022-05-10 16:57:41 +02:00
} finally {
if (mustCloseKey) {
key.close();
}
}
2022-05-10 16:57:41 +02:00
} else {
db.put(cfh, writeOptions, LLUtils.toArray(key), LLUtils.toArray(value));
}
} finally {
closeLock.unlockRead(closeReadLock);
2021-12-12 02:17:36 +01:00
}
}
@Override
public boolean exists(@NotNull ReadOptions readOptions, Buffer key) throws RocksDBException {
var closeReadLock = closeLock.readLock();
try {
ensureOpen();
ensureOwned(readOptions);
if (nettyDirect) {
// Get the key nio buffer to pass to RocksDB
ByteBuffer keyNioBuffer;
boolean mustCloseKey;
{
if (!LLUtils.isReadOnlyDirect(key)) {
// If the nio buffer is not available, copy the netty buffer into a new direct buffer
mustCloseKey = true;
var directKey = DefaultBufferAllocators.offHeapAllocator().allocate(key.readableBytes());
key.copyInto(key.readerOffset(), directKey, 0, key.readableBytes());
key = directKey;
} else {
mustCloseKey = false;
}
keyNioBuffer = ((ReadableComponent) key).readableBuffer();
assert keyNioBuffer.isDirect();
assert keyNioBuffer.limit() == key.readableBytes();
2022-03-16 19:19:26 +01:00
}
2021-12-12 02:17:36 +01:00
try {
if (db.keyMayExist(cfh, keyNioBuffer)) {
int size = db.get(cfh, readOptions, keyNioBuffer.position(0), LLUtils.EMPTY_BYTE_BUFFER);
boolean found = size != RocksDB.NOT_FOUND;
if (found) {
readValueFoundWithBloomSimpleBufferSize.record(size);
return true;
} else {
readValueNotFoundWithMayExistBloomBufferSize.record(0);
return false;
2021-10-20 01:51:34 +02:00
}
} else {
readValueNotFoundWithBloomBufferSize.record(0);
return false;
}
} finally {
if (mustCloseKey) {
key.close();
2021-10-20 01:51:34 +02:00
}
2021-12-12 02:17:36 +01:00
}
} else {
int size = RocksDB.NOT_FOUND;
byte[] keyBytes = LLUtils.toArray(key);
Holder<byte[]> data = new Holder<>();
boolean mayExistHit = false;
if (db.keyMayExist(cfh, readOptions, keyBytes, data)) {
2022-05-10 16:57:41 +02:00
mayExistHit = true;
if (data.getValue() != null) {
size = data.getValue().length;
} else {
size = db.get(cfh, readOptions, keyBytes, NO_DATA);
2021-10-20 01:51:34 +02:00
}
}
boolean found = size != RocksDB.NOT_FOUND;
if (found) {
readValueFoundWithBloomSimpleBufferSize.record(size);
} else {
if (mayExistHit) {
readValueNotFoundWithMayExistBloomBufferSize.record(0);
} else {
readValueNotFoundWithBloomBufferSize.record(0);
}
}
return found;
2021-10-20 01:51:34 +02:00
}
} finally {
closeLock.unlockRead(closeReadLock);
2021-10-20 01:51:34 +02:00
}
}
@Override
public boolean mayExists(@NotNull ReadOptions readOptions, Buffer key) throws RocksDBException {
var closeReadLock = closeLock.readLock();
2021-10-20 01:51:34 +02:00
try {
ensureOpen();
ensureOwned(readOptions);
2021-12-12 02:17:36 +01:00
if (nettyDirect) {
// Get the key nio buffer to pass to RocksDB
2022-03-16 19:19:26 +01:00
ByteBuffer keyNioBuffer;
2021-12-12 02:17:36 +01:00
boolean mustCloseKey;
2022-03-16 19:19:26 +01:00
{
if (!LLUtils.isReadOnlyDirect(key)) {
// If the nio buffer is not available, copy the netty buffer into a new direct buffer
mustCloseKey = true;
var directKey = DefaultBufferAllocators.offHeapAllocator().allocate(key.readableBytes());
key.copyInto(key.readerOffset(), directKey, 0, key.readableBytes());
key = directKey;
} else {
mustCloseKey = false;
}
keyNioBuffer = ((ReadableComponent) key).readableBuffer();
assert keyNioBuffer.isDirect();
assert keyNioBuffer.limit() == key.readableBytes();
2021-12-12 02:17:36 +01:00
}
try {
return db.keyMayExist(cfh, readOptions, keyNioBuffer);
2021-12-12 02:17:36 +01:00
} finally {
if (mustCloseKey) {
2022-03-16 19:19:26 +01:00
key.close();
2021-10-20 01:51:34 +02:00
}
}
2021-12-12 02:17:36 +01:00
} else {
byte[] keyBytes = LLUtils.toArray(key);
return db.keyMayExist(cfh, readOptions, keyBytes, null);
2021-10-20 01:51:34 +02:00
}
} finally {
closeLock.unlockRead(closeReadLock);
2021-10-20 01:51:34 +02:00
}
}
@Override
public void delete(WriteOptions writeOptions, Buffer key) throws RocksDBException {
var closeReadLock = closeLock.readLock();
try {
ensureOpen();
ensureOwned(writeOptions);
keyBufferSize.record(key.readableBytes());
if (nettyDirect) {
// Get the key nio buffer to pass to RocksDB
ByteBuffer keyNioBuffer;
boolean mustCloseKey;
{
if (!LLUtils.isReadOnlyDirect(key)) {
// If the nio buffer is not available, copy the netty buffer into a new direct buffer
mustCloseKey = true;
var directKey = DefaultBufferAllocators.offHeapAllocator().allocate(key.readableBytes());
key.copyInto(key.readerOffset(), directKey, 0, key.readableBytes());
key = directKey;
} else {
mustCloseKey = false;
}
keyNioBuffer = ((ReadableComponent) key).readableBuffer();
assert keyNioBuffer.isDirect();
assert keyNioBuffer.limit() == key.readableBytes();
2021-12-12 02:17:36 +01:00
}
try {
db.delete(cfh, writeOptions, keyNioBuffer);
} finally {
if (mustCloseKey) {
key.close();
2021-10-20 01:51:34 +02:00
}
}
} else {
db.delete(cfh, writeOptions, LLUtils.toArray(key));
2022-04-19 23:23:12 +02:00
}
} finally {
closeLock.unlockRead(closeReadLock);
2021-10-20 01:51:34 +02:00
}
}
@Override
public void delete(WriteOptions writeOptions, byte[] key) throws RocksDBException {
var closeReadLock = closeLock.readLock();
try {
ensureOpen();
ensureOwned(writeOptions);
keyBufferSize.record(key.length);
db.delete(cfh, writeOptions, key);
} finally {
closeLock.unlockRead(closeReadLock);
}
2021-10-20 01:51:34 +02:00
}
@Override
public List<byte[]> multiGetAsList(ReadOptions readOptions, List<byte[]> keys) throws RocksDBException {
var closeReadLock = closeLock.readLock();
try {
ensureOpen();
ensureOwned(readOptions);
for (byte[] key : keys) {
keyBufferSize.record(key.length);
}
var columnFamilyHandles = new RepeatedElementList<>(cfh, keys.size());
return db.multiGetAsList(readOptions, columnFamilyHandles, keys);
} finally {
closeLock.unlockRead(closeReadLock);
}
2021-10-20 01:51:34 +02:00
}
@Override
public void suggestCompactRange() throws RocksDBException {
var closeReadLock = closeLock.readLock();
try {
ensureOpen();
db.suggestCompactRange(cfh);
} finally {
closeLock.unlockRead(closeReadLock);
}
2021-10-20 01:51:34 +02:00
}
@Override
public void compactRange(byte[] begin, byte[] end, CompactRangeOptions options) throws RocksDBException {
var closeReadLock = closeLock.readLock();
try {
ensureOpen();
ensureOwned(options);
db.compactRange(cfh, begin, end, options);
} finally {
closeLock.unlockRead(closeReadLock);
}
2021-10-20 01:51:34 +02:00
}
@Override
public void flush(FlushOptions options) throws RocksDBException {
var closeReadLock = closeLock.readLock();
try {
ensureOpen();
ensureOwned(options);
db.flush(options, cfh);
} finally {
closeLock.unlockRead(closeReadLock);
}
2021-10-20 01:51:34 +02:00
}
@Override
public void flushWal(boolean sync) throws RocksDBException {
var closeReadLock = closeLock.readLock();
try {
ensureOpen();
db.flushWal(sync);
} finally {
closeLock.unlockRead(closeReadLock);
}
2021-10-20 01:51:34 +02:00
}
@Override
public long getLongProperty(String property) throws RocksDBException {
var closeReadLock = closeLock.readLock();
try {
ensureOpen();
return db.getLongProperty(cfh, property);
} finally {
closeLock.unlockRead(closeReadLock);
}
2021-10-20 01:51:34 +02:00
}
@Override
public void write(WriteOptions writeOptions, WriteBatch writeBatch) throws RocksDBException {
var closeReadLock = closeLock.readLock();
try {
ensureOpen();
ensureOwned(writeOptions);
ensureOwned(writeBatch);
db.write(writeOptions, writeBatch);
} finally {
closeLock.unlockRead(closeReadLock);
}
2021-10-20 01:51:34 +02:00
}
/**
* @return true if committed successfully
*/
protected abstract boolean commitOptimistically(Transaction tx) throws RocksDBException;
2021-10-20 01:51:34 +02:00
protected abstract Transaction beginTransaction(@NotNull WriteOptions writeOptions,
TransactionOptions txOpts);
2021-10-20 01:51:34 +02:00
@Override
public final @NotNull UpdateAtomicResult updateAtomic(@NotNull ReadOptions readOptions,
@NotNull WriteOptions writeOptions,
Buffer key,
BinarySerializationFunction updater,
UpdateAtomicResultMode returnMode) throws IOException {
var closeReadLock = closeLock.readLock();
try {
ensureOpen();
ensureOwned(readOptions);
try {
keyBufferSize.record(key.readableBytes());
startedUpdate.increment();
return updateAtomicImpl(readOptions, writeOptions, key, updater, returnMode);
} catch (IOException e) {
throw e;
} catch (Exception e) {
throw new IOException(e);
} finally {
endedUpdate.increment();
}
} finally {
closeLock.unlockRead(closeReadLock);
}
}
protected final void recordAtomicUpdateTime(boolean changed, boolean prevSet, boolean newSet, long initTime) {
long duration = System.nanoTime() - initTime;
Timer timer;
if (changed) {
if (prevSet && newSet) {
timer = updateReplacedTime;
} else if (newSet) {
timer = updateAddedTime;
} else {
timer = updateRemovedTime;
}
} else {
timer = updateUnchangedTime;
}
timer.record(duration, TimeUnit.NANOSECONDS);
}
protected abstract @NotNull UpdateAtomicResult updateAtomicImpl(@NotNull ReadOptions readOptions,
@NotNull WriteOptions writeOptions,
Buffer key,
BinarySerializationFunction updater,
UpdateAtomicResultMode returnMode) throws IOException;
2021-10-20 01:51:34 +02:00
@Override
@NotNull
public RocksIteratorObj newIterator(@NotNull ReadOptions readOptions,
2022-05-12 19:14:27 +02:00
@Nullable Buffer min,
@Nullable Buffer max) {
var closeReadLock = closeLock.readLock();
try {
ensureOpen();
ensureOwned(readOptions);
2022-05-12 19:14:27 +02:00
ensureOwned(min);
ensureOwned(max);
AbstractSlice<?> sliceMin;
AbstractSlice<?> sliceMax;
2022-05-12 19:14:27 +02:00
if (min != null) {
sliceMin = setIterateBound(nettyDirect, readOptions, IterateBound.LOWER, min);
} else {
sliceMin = null;
}
2022-05-04 12:36:32 +02:00
try {
2022-05-12 19:14:27 +02:00
if (max != null) {
sliceMax = setIterateBound(nettyDirect, readOptions, IterateBound.UPPER, max);
} else {
sliceMax = null;
}
try {
var it = db.newIterator(cfh, readOptions);
2022-05-12 19:14:27 +02:00
try {
return new RocksIteratorObj(it,
sliceMin,
sliceMax,
min,
max,
nettyDirect,
this.startedIterSeek,
this.endedIterSeek,
this.iterSeekTime,
this.startedIterNext,
this.endedIterNext,
this.iterNextTime
);
} catch (Throwable ex) {
it.close();
throw ex;
}
} catch (Throwable ex) {
if (sliceMax != null) {
sliceMax.close();
}
throw ex;
}
2022-05-04 12:36:32 +02:00
} catch (Throwable ex) {
2022-05-12 19:14:27 +02:00
if (sliceMin != null) {
sliceMin.close();
}
2022-05-04 12:36:32 +02:00
throw ex;
}
} finally {
closeLock.unlockRead(closeReadLock);
}
2021-10-20 01:51:34 +02:00
}
2022-04-30 02:14:44 +02:00
protected int getLevels() {
2022-04-30 14:21:20 +02:00
var closeReadLock = closeLock.readLock();
try {
ensureOpen();
return RocksDBUtils.getLevels(db, cfh);
2022-04-30 14:21:20 +02:00
} finally {
closeLock.unlockRead(closeReadLock);
}
}
@Override
public final void forceCompaction(int volumeId) {
var closeReadLock = closeLock.readLock();
try {
ensureOpen();
RocksDBUtils.forceCompaction(db, db.getName(), cfh, volumeId, logger);
} finally {
closeLock.unlockRead(closeReadLock);
}
}
2021-10-20 01:51:34 +02:00
@Override
public ColumnFamilyHandle getColumnFamilyHandle() {
2021-10-20 01:51:34 +02:00
return cfh;
}
@Override
public BufferAllocator getAllocator() {
return alloc;
}
public MeterRegistry getMeterRegistry() {
return meterRegistry;
}
public Timer getIterNextTime() {
return iterNextTime;
}
public Counter getStartedIterNext() {
return startedIterNext;
}
public Counter getEndedIterNext() {
return endedIterNext;
}
2022-06-09 19:45:03 +02:00
private class DBColumnKeyMayExistGetter extends KeyMayExistGetter {
public DBColumnKeyMayExistGetter() {
super(alloc, nettyDirect);
}
@Override
protected KeyMayExist keyMayExist(ReadOptions readOptions, ByteBuffer key, ByteBuffer value) {
return db.keyMayExist(cfh, readOptions, key, value);
}
@Override
protected boolean keyMayExist(ReadOptions readOptions, byte[] key, @Nullable Holder<byte[]> valueHolder) {
return db.keyMayExist(cfh, readOptions, key, valueHolder);
}
@Override
protected int get(ReadOptions readOptions, ByteBuffer key, ByteBuffer value) throws RocksDBException {
return db.get(cfh, readOptions, key, value);
}
@Override
protected byte[] get(ReadOptions readOptions, byte[] key) throws RocksDBException, IllegalArgumentException {
return db.get(cfh, readOptions, key);
}
@Override
protected void recordReadValueNotFoundWithMayExistBloomBufferSize(int value) {
readValueNotFoundWithMayExistBloomBufferSize.record(value);
}
@Override
protected void recordReadValueFoundWithBloomUncachedBufferSize(int value) {
readValueFoundWithBloomUncachedBufferSize.record(value);
}
@Override
protected void recordReadValueFoundWithBloomCacheBufferSize(int value) {
readValueFoundWithBloomCacheBufferSize.record(value);
}
@Override
protected void recordReadAttempts(int value) {
readAttempts.record(value);
}
@Override
protected void recordReadValueNotFoundWithBloomBufferSize(int value) {
readValueNotFoundWithBloomBufferSize.record(value);
}
@Override
protected void recordKeyBufferSize(int value) {
keyBufferSize.record(value);
}
}
2021-10-20 01:51:34 +02:00
}