Use Micrometer registry, better optimistic commit failure logging
This commit is contained in:
parent
70f76b660d
commit
1ea004630e
1
pom.xml
1
pom.xml
@ -250,7 +250,6 @@
|
||||
<dependency>
|
||||
<groupId>io.micrometer</groupId>
|
||||
<artifactId>micrometer-core</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.micrometer</groupId>
|
||||
|
@ -1,5 +1,6 @@
|
||||
package it.cavallium.dbengine.client;
|
||||
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
import io.net5.buffer.api.BufferAllocator;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
@ -20,6 +21,8 @@ public interface CompositeDatabase {
|
||||
|
||||
BufferAllocator getAllocator();
|
||||
|
||||
MeterRegistry getMeterRegistry();
|
||||
|
||||
/**
|
||||
* Find corrupted items
|
||||
*/
|
||||
|
@ -1,5 +1,6 @@
|
||||
package it.cavallium.dbengine.client;
|
||||
|
||||
import com.google.common.util.concurrent.Uninterruptibles;
|
||||
import io.net5.buffer.api.Send;
|
||||
import it.cavallium.dbengine.client.IndexAction.Add;
|
||||
import it.cavallium.dbengine.client.IndexAction.AddMulti;
|
||||
@ -24,11 +25,15 @@ import java.lang.ref.Cleaner;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
import java.util.logging.Level;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.warp.commonutils.log.Logger;
|
||||
import org.warp.commonutils.log.LoggerFactory;
|
||||
import org.warp.commonutils.type.ShortNamedThreadFactory;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.publisher.Sinks;
|
||||
@ -42,108 +47,12 @@ import reactor.util.function.Tuple2;
|
||||
public class LuceneIndexImpl<T, U> implements LuceneIndex<T, U> {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(LuceneIndex.class);
|
||||
private static final Cleaner cleaner = Cleaner.create();
|
||||
private final LLLuceneIndex luceneIndex;
|
||||
private final Indicizer<T,U> indicizer;
|
||||
private final Many<IndexAction> actions;
|
||||
private final Empty<Void> actionsClosed;
|
||||
|
||||
public LuceneIndexImpl(LLLuceneIndex luceneIndex, Indicizer<T, U> indicizer) {
|
||||
this.luceneIndex = luceneIndex;
|
||||
this.indicizer = indicizer;
|
||||
this.actions = Sinks
|
||||
.many()
|
||||
.unicast()
|
||||
.onBackpressureBuffer(Queues.<IndexAction>get(1024).get());
|
||||
this.actionsClosed = Sinks.empty();
|
||||
|
||||
subscribeToActions();
|
||||
}
|
||||
|
||||
private void subscribeToActions() {
|
||||
var d = actions
|
||||
.asFlux()
|
||||
.doAfterTerminate(actionsClosed::tryEmitEmpty)
|
||||
.flatMap(this::onParallelAction)
|
||||
.concatMap(this::onOrderedAction)
|
||||
.then()
|
||||
.subscribeOn(Schedulers.boundedElastic())
|
||||
.subscribe();
|
||||
|
||||
cleaner.register(LuceneIndexImpl.this, d::dispose);
|
||||
}
|
||||
|
||||
/**
|
||||
* Actions that don't require any kind of order
|
||||
*/
|
||||
private Mono<IndexAction> onParallelAction(IndexAction action) {
|
||||
return (switch (action.getType()) {
|
||||
case TAKE_SNAPSHOT, RELEASE_SNAPSHOT, FLUSH, CLOSE -> Mono.empty();
|
||||
|
||||
case ADD -> luceneIndex.addDocument(((Add) action).key(), ((Add) action).doc())
|
||||
.doOnError(e -> ((Add) action).addedFuture().error(e))
|
||||
.onErrorResume(ex -> Mono.empty())
|
||||
.doOnSuccess(s -> ((Add) action).addedFuture().success());
|
||||
case ADD_MULTI -> luceneIndex.addDocuments(((AddMulti) action).docsFlux())
|
||||
.doOnError(e -> ((AddMulti) action).addedMultiFuture().error(e))
|
||||
.onErrorResume(ex -> Mono.empty())
|
||||
.doOnSuccess(s -> ((AddMulti) action).addedMultiFuture().success());
|
||||
case UPDATE -> luceneIndex
|
||||
.updateDocument(((Update) action).key(),((Update) action).doc())
|
||||
.doOnError(e -> ((Update) action).updatedFuture().error(e))
|
||||
.onErrorResume(ex -> Mono.empty())
|
||||
.doOnSuccess(s -> ((Update) action).updatedFuture().success());
|
||||
case UPDATE_MULTI -> luceneIndex.updateDocuments(Mono.just(((UpdateMulti) action).docs()))
|
||||
.doOnError(e -> ((UpdateMulti) action).updatedMultiFuture().error(e))
|
||||
.onErrorResume(ex -> Mono.empty())
|
||||
.doOnSuccess(s -> ((UpdateMulti) action).updatedMultiFuture().success());
|
||||
case DELETE -> luceneIndex.deleteDocument(((Delete) action).key())
|
||||
.doOnError(e -> ((Delete) action).deletedFuture().error(e))
|
||||
.onErrorResume(ex -> Mono.empty())
|
||||
.doOnSuccess(s -> ((Delete) action).deletedFuture().success());
|
||||
case DELETE_ALL -> luceneIndex.deleteAll()
|
||||
.doOnError(e -> ((DeleteAll) action).deletedAllFuture().error(e))
|
||||
.onErrorResume(ex -> Mono.empty())
|
||||
.doOnSuccess(s -> ((DeleteAll) action).deletedAllFuture().success());
|
||||
case REFRESH -> luceneIndex.refresh(((Refresh) action).force())
|
||||
.doOnError(e -> ((Refresh) action).refreshFuture().error(e))
|
||||
.onErrorResume(ex -> Mono.empty())
|
||||
.doOnSuccess(s -> ((Refresh) action).refreshFuture().success());
|
||||
})
|
||||
.doOnError(ex -> log.error("Uncaught error when handling parallel index action " + action.getType(), ex))
|
||||
.onErrorResume(ex -> Mono.empty())
|
||||
.thenReturn(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Actions that require absolute order
|
||||
*/
|
||||
private Mono<IndexAction> onOrderedAction(IndexAction action) {
|
||||
return (switch (action.getType()) {
|
||||
case ADD, REFRESH, DELETE_ALL, DELETE, UPDATE_MULTI, UPDATE, ADD_MULTI -> Mono.empty();
|
||||
|
||||
case TAKE_SNAPSHOT -> luceneIndex.takeSnapshot().single()
|
||||
.doOnError(e -> ((TakeSnapshot) action).snapshotFuture().error(e))
|
||||
.onErrorResume(ex -> Mono.empty())
|
||||
.doOnNext(s -> ((TakeSnapshot) action).snapshotFuture().success(s));
|
||||
case RELEASE_SNAPSHOT -> luceneIndex.releaseSnapshot(((ReleaseSnapshot) action).snapshot())
|
||||
.doOnError(e -> ((ReleaseSnapshot) action).releasedFuture().error(e))
|
||||
.onErrorResume(ex -> Mono.empty())
|
||||
.doOnSuccess(s -> ((ReleaseSnapshot) action).releasedFuture().success());
|
||||
case FLUSH -> luceneIndex.flush()
|
||||
.doOnError(e -> ((Flush) action).flushFuture().error(e))
|
||||
.onErrorResume(ex -> Mono.empty())
|
||||
.doOnSuccess(s -> ((Flush) action).flushFuture().success());
|
||||
case CLOSE -> luceneIndex.close()
|
||||
.doOnError(e -> ((Close) action).closeFuture().error(e))
|
||||
.onErrorResume(ex -> Mono.empty())
|
||||
.doOnSuccess(s -> ((Close) action).closeFuture().success())
|
||||
.doAfterTerminate(() -> emitActionOptimistically(null));
|
||||
})
|
||||
.doOnError(ex -> log.error("Uncaught error when handling ordered index action " + action.getType(), ex))
|
||||
.onErrorResume(ex -> Mono.empty())
|
||||
.onErrorResume(ex -> Mono.empty())
|
||||
.thenReturn(action);
|
||||
}
|
||||
|
||||
private LLSnapshot resolveSnapshot(CompositeSnapshot snapshot) {
|
||||
@ -158,45 +67,46 @@ public class LuceneIndexImpl<T, U> implements LuceneIndex<T, U> {
|
||||
public Mono<Void> addDocument(T key, U value) {
|
||||
return indicizer
|
||||
.toDocument(key, value)
|
||||
.flatMap(doc -> Mono
|
||||
.create(sink -> emitActionOptimistically(new IndexAction.Add(indicizer.toIndex(key), doc, sink))));
|
||||
.flatMap(doc -> luceneIndex.addDocument(indicizer.toIndex(key), doc));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> addDocuments(Flux<Entry<T, U>> entries) {
|
||||
var convertedEntries = entries.flatMap(entry -> indicizer
|
||||
.toDocument(entry.getKey(), entry.getValue())
|
||||
.map(doc -> Map.entry(indicizer.toIndex(entry.getKey()), doc))
|
||||
);
|
||||
return Mono.create(sink -> emitActionOptimistically(new IndexAction.AddMulti(convertedEntries, sink)));
|
||||
return luceneIndex
|
||||
.addDocuments(entries
|
||||
.flatMap(entry -> indicizer
|
||||
.toDocument(entry.getKey(), entry.getValue())
|
||||
.map(doc -> Map.entry(indicizer.toIndex(entry.getKey()), doc)))
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> deleteDocument(T key) {
|
||||
LLTerm id = indicizer.toIndex(key);
|
||||
return Mono.create(sink -> emitActionOptimistically(new IndexAction.Delete(id, sink)));
|
||||
return luceneIndex.deleteDocument(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> updateDocument(T key, @NotNull U value) {
|
||||
return indicizer
|
||||
.toDocument(key, value)
|
||||
.flatMap(doc -> Mono.create(sink -> emitActionOptimistically(new Update(indicizer.toIndex(key), doc, sink))));
|
||||
.flatMap(doc -> luceneIndex.updateDocument(indicizer.toIndex(key), doc));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> updateDocuments(Flux<Entry<T, U>> entries) {
|
||||
return entries
|
||||
.flatMap(entry -> indicizer
|
||||
.toDocument(entry.getKey(), entry.getValue())
|
||||
.map(doc -> Map.entry(indicizer.toIndex(entry.getKey()), doc)))
|
||||
.collectMap(Entry::getKey, Entry::getValue)
|
||||
.flatMap(docs -> Mono.create(sink -> emitActionOptimistically(new IndexAction.UpdateMulti(docs, sink))));
|
||||
return luceneIndex
|
||||
.updateDocuments(entries
|
||||
.flatMap(entry -> indicizer
|
||||
.toDocument(entry.getKey(), entry.getValue())
|
||||
.map(doc -> Map.entry(indicizer.toIndex(entry.getKey()), doc)))
|
||||
.collectMap(Entry::getKey, Entry::getValue)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> deleteAll() {
|
||||
return Mono.create(sink -> emitActionOptimistically(new IndexAction.DeleteAll(sink)));
|
||||
return luceneIndex.deleteAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -255,19 +165,7 @@ public class LuceneIndexImpl<T, U> implements LuceneIndex<T, U> {
|
||||
|
||||
@Override
|
||||
public Mono<Void> close() {
|
||||
return Mono
|
||||
.<Void>create(sink -> emitActionOptimistically(new Close(sink)))
|
||||
.then(this.actionsClosed.asMono());
|
||||
}
|
||||
|
||||
private void emitActionOptimistically(IndexAction action) {
|
||||
EmitResult emitResult;
|
||||
while ((emitResult = (action == null ? actions.tryEmitComplete() : actions.tryEmitNext(action)))
|
||||
== EmitResult.FAIL_NON_SERIALIZED || emitResult == EmitResult.FAIL_OVERFLOW) {
|
||||
// 10ms
|
||||
LockSupport.parkNanos(10000000);
|
||||
}
|
||||
emitResult.orThrow();
|
||||
return luceneIndex.close();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -275,7 +173,7 @@ public class LuceneIndexImpl<T, U> implements LuceneIndex<T, U> {
|
||||
*/
|
||||
@Override
|
||||
public Mono<Void> flush() {
|
||||
return Mono.create(sink -> emitActionOptimistically(new IndexAction.Flush(sink)));
|
||||
return luceneIndex.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -283,16 +181,16 @@ public class LuceneIndexImpl<T, U> implements LuceneIndex<T, U> {
|
||||
*/
|
||||
@Override
|
||||
public Mono<Void> refresh(boolean force) {
|
||||
return Mono.create(sink -> emitActionOptimistically(new IndexAction.Refresh(force, sink)));
|
||||
return luceneIndex.refresh(force);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<LLSnapshot> takeSnapshot() {
|
||||
return Mono.create(sink -> emitActionOptimistically(new IndexAction.TakeSnapshot(sink)));
|
||||
return luceneIndex.takeSnapshot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> releaseSnapshot(LLSnapshot snapshot) {
|
||||
return Mono.create(sink -> emitActionOptimistically(new IndexAction.ReleaseSnapshot(snapshot, sink)));
|
||||
return luceneIndex.releaseSnapshot(snapshot);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package it.cavallium.dbengine.database;
|
||||
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
import io.net5.buffer.api.BufferAllocator;
|
||||
import it.cavallium.dbengine.client.DatabaseOptions;
|
||||
import it.cavallium.dbengine.client.IndicizerAnalyzers;
|
||||
@ -15,6 +16,8 @@ public interface LLDatabaseConnection {
|
||||
|
||||
BufferAllocator getAllocator();
|
||||
|
||||
MeterRegistry getMeterRegistry();
|
||||
|
||||
Mono<? extends LLDatabaseConnection> connect();
|
||||
|
||||
Mono<? extends LLKeyValueDatabase> getDatabase(String name,
|
||||
|
@ -2,6 +2,7 @@ package it.cavallium.dbengine.database;
|
||||
|
||||
import com.google.common.primitives.Ints;
|
||||
import com.google.common.primitives.Longs;
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
import io.net5.buffer.api.BufferAllocator;
|
||||
import it.cavallium.dbengine.database.collections.DatabaseInt;
|
||||
import it.cavallium.dbengine.database.collections.DatabaseLong;
|
||||
@ -47,5 +48,7 @@ public interface LLKeyValueDatabase extends LLSnapshottable, LLKeyValueDatabaseS
|
||||
|
||||
BufferAllocator getAllocator();
|
||||
|
||||
MeterRegistry getMeterRegistry();
|
||||
|
||||
Mono<Void> close();
|
||||
}
|
||||
|
@ -1,27 +1,20 @@
|
||||
package it.cavallium.dbengine.database.disk;
|
||||
|
||||
import static it.cavallium.dbengine.database.LLUtils.MARKER_ROCKSDB;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static java.util.Objects.requireNonNullElse;
|
||||
|
||||
import io.micrometer.core.instrument.Gauge;
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
import io.net5.buffer.api.Buffer;
|
||||
import io.net5.buffer.api.BufferAllocator;
|
||||
import io.net5.buffer.api.MemoryManager;
|
||||
import io.net5.buffer.api.Send;
|
||||
import io.net5.util.internal.PlatformDependent;
|
||||
import it.cavallium.dbengine.client.DatabaseOptions;
|
||||
import it.cavallium.dbengine.database.LLDelta;
|
||||
import it.cavallium.dbengine.database.LLDictionaryResultType;
|
||||
import it.cavallium.dbengine.database.LLUtils;
|
||||
import it.cavallium.dbengine.database.LLUtils.DirectBuffer;
|
||||
import it.cavallium.dbengine.database.RepeatedElementList;
|
||||
import it.cavallium.dbengine.database.serialization.SerializationFunction;
|
||||
import it.cavallium.dbengine.lucene.ExponentialPageLimits;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.rocksdb.ColumnFamilyHandle;
|
||||
@ -37,7 +30,6 @@ import org.rocksdb.WriteBatch;
|
||||
import org.rocksdb.WriteOptions;
|
||||
import org.warp.commonutils.log.Logger;
|
||||
import org.warp.commonutils.log.LoggerFactory;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.scheduler.Schedulers;
|
||||
|
||||
public sealed abstract class AbstractRocksDBColumn<T extends RocksDB> implements RocksDBColumn
|
||||
@ -54,11 +46,24 @@ public sealed abstract class AbstractRocksDBColumn<T extends RocksDB> implements
|
||||
private final BufferAllocator alloc;
|
||||
private final ColumnFamilyHandle cfh;
|
||||
|
||||
public AbstractRocksDBColumn(T db, DatabaseOptions databaseOptions, BufferAllocator alloc, ColumnFamilyHandle cfh) {
|
||||
private final MeterRegistry meterRegistry;
|
||||
private final AtomicInteger lastDataSizeMetric = new AtomicInteger(0);
|
||||
|
||||
public AbstractRocksDBColumn(T db,
|
||||
DatabaseOptions databaseOptions,
|
||||
BufferAllocator alloc,
|
||||
ColumnFamilyHandle cfh,
|
||||
MeterRegistry meterRegistry) {
|
||||
this.db = db;
|
||||
this.opts = databaseOptions;
|
||||
this.alloc = alloc;
|
||||
this.cfh = cfh;
|
||||
|
||||
this.meterRegistry = meterRegistry;
|
||||
Gauge
|
||||
.builder("it.cavallium.dbengine.database.disk.column.lastdatasize", lastDataSizeMetric::get)
|
||||
.description("Last data size read using get()")
|
||||
.register(meterRegistry);
|
||||
}
|
||||
|
||||
protected T getDb() {
|
||||
@ -81,6 +86,15 @@ public sealed abstract class AbstractRocksDBColumn<T extends RocksDB> implements
|
||||
if (Schedulers.isInNonBlockingThread()) {
|
||||
throw new UnsupportedOperationException("Called dbGet in a nonblocking thread");
|
||||
}
|
||||
if (!db.isOwningHandle()) {
|
||||
throw new IllegalStateException("Database is closed");
|
||||
}
|
||||
if (!readOptions.isOwningHandle()) {
|
||||
throw new IllegalStateException("ReadOptions is closed");
|
||||
}
|
||||
if (!cfh.isOwningHandle()) {
|
||||
throw new IllegalStateException("Column family is closed");
|
||||
}
|
||||
if (opts.allowNettyDirect()) {
|
||||
|
||||
//todo: implement keyMayExist if existsAlmostCertainly is false.
|
||||
@ -91,7 +105,6 @@ public sealed abstract class AbstractRocksDBColumn<T extends RocksDB> implements
|
||||
// Create a direct result buffer because RocksDB works only with direct buffers
|
||||
try (Buffer resultBuf = alloc.allocate(INITIAL_DIRECT_READ_BYTE_BUF_SIZE_BYTES)) {
|
||||
int valueSize;
|
||||
int assertionReadData = -1;
|
||||
ByteBuffer resultNioBuf;
|
||||
do {
|
||||
// Create the result nio buffer to pass to RocksDB
|
||||
@ -115,6 +128,9 @@ public sealed abstract class AbstractRocksDBColumn<T extends RocksDB> implements
|
||||
// of the result into the result buffer more than once.
|
||||
assert resultNioBuf.limit() <= valueSize;
|
||||
|
||||
// Update data size metrics
|
||||
this.lastDataSizeMetric.set(valueSize);
|
||||
|
||||
if (valueSize <= resultNioBuf.limit()) {
|
||||
// Return the result ready to be read
|
||||
return resultBuf.readerOffset(0).writerOffset(valueSize).send();
|
||||
@ -175,6 +191,15 @@ public sealed abstract class AbstractRocksDBColumn<T extends RocksDB> implements
|
||||
if (Schedulers.isInNonBlockingThread()) {
|
||||
throw new UnsupportedOperationException("Called dbPut in a nonblocking thread");
|
||||
}
|
||||
if (!db.isOwningHandle()) {
|
||||
throw new IllegalStateException("Database is closed");
|
||||
}
|
||||
if (!writeOptions.isOwningHandle()) {
|
||||
throw new IllegalStateException("WriteOptions is closed");
|
||||
}
|
||||
if (!cfh.isOwningHandle()) {
|
||||
throw new IllegalStateException("Column family is closed");
|
||||
}
|
||||
assert key.isAccessible();
|
||||
assert value.isAccessible();
|
||||
if (opts.allowNettyDirect()) {
|
||||
@ -197,7 +222,7 @@ public sealed abstract class AbstractRocksDBColumn<T extends RocksDB> implements
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (writeOptions != null && !(writeOptions instanceof UnreleasableWriteOptions)) {
|
||||
if (!(writeOptions instanceof UnreleasableWriteOptions)) {
|
||||
writeOptions.close();
|
||||
}
|
||||
}
|
||||
@ -209,6 +234,15 @@ public sealed abstract class AbstractRocksDBColumn<T extends RocksDB> implements
|
||||
if (Schedulers.isInNonBlockingThread()) {
|
||||
throw new UnsupportedOperationException("Called containsKey in a nonblocking thread");
|
||||
}
|
||||
if (!db.isOwningHandle()) {
|
||||
throw new IllegalStateException("Database is closed");
|
||||
}
|
||||
if (!readOptions.isOwningHandle()) {
|
||||
throw new IllegalStateException("ReadOptions is closed");
|
||||
}
|
||||
if (!cfh.isOwningHandle()) {
|
||||
throw new IllegalStateException("Column family is closed");
|
||||
}
|
||||
int size = RocksDB.NOT_FOUND;
|
||||
byte[] keyBytes = LLUtils.toArray(key);
|
||||
Holder<byte[]> data = new Holder<>();
|
||||
@ -221,7 +255,7 @@ public sealed abstract class AbstractRocksDBColumn<T extends RocksDB> implements
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (readOptions != null && !(readOptions instanceof UnreleasableReadOptions)) {
|
||||
if (!(readOptions instanceof UnreleasableReadOptions)) {
|
||||
readOptions.close();
|
||||
}
|
||||
}
|
||||
@ -232,6 +266,15 @@ public sealed abstract class AbstractRocksDBColumn<T extends RocksDB> implements
|
||||
@Override
|
||||
public void delete(WriteOptions writeOptions, Send<Buffer> keySend) throws RocksDBException {
|
||||
try (var key = keySend.receive()) {
|
||||
if (!db.isOwningHandle()) {
|
||||
throw new IllegalStateException("Database is closed");
|
||||
}
|
||||
if (!writeOptions.isOwningHandle()) {
|
||||
throw new IllegalStateException("WriteOptions is closed");
|
||||
}
|
||||
if (!cfh.isOwningHandle()) {
|
||||
throw new IllegalStateException("Column family is closed");
|
||||
}
|
||||
if (opts.allowNettyDirect()) {
|
||||
DirectBuffer keyNioBuffer = LLUtils.convertToReadableDirect(alloc, key.send());
|
||||
try {
|
||||
@ -248,43 +291,106 @@ public sealed abstract class AbstractRocksDBColumn<T extends RocksDB> implements
|
||||
|
||||
@Override
|
||||
public void delete(WriteOptions writeOptions, byte[] key) throws RocksDBException {
|
||||
if (!db.isOwningHandle()) {
|
||||
throw new IllegalStateException("Database is closed");
|
||||
}
|
||||
if (!writeOptions.isOwningHandle()) {
|
||||
throw new IllegalStateException("WriteOptions is closed");
|
||||
}
|
||||
if (!cfh.isOwningHandle()) {
|
||||
throw new IllegalStateException("Column family is closed");
|
||||
}
|
||||
db.delete(cfh, writeOptions, key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<byte[]> multiGetAsList(ReadOptions readOptions, List<byte[]> keys) throws RocksDBException {
|
||||
if (!db.isOwningHandle()) {
|
||||
throw new IllegalStateException("Database is closed");
|
||||
}
|
||||
if (!readOptions.isOwningHandle()) {
|
||||
throw new IllegalStateException("ReadOptions is closed");
|
||||
}
|
||||
if (!cfh.isOwningHandle()) {
|
||||
throw new IllegalStateException("Column family is closed");
|
||||
}
|
||||
var columnFamilyHandles = new RepeatedElementList<>(cfh, keys.size());
|
||||
return db.multiGetAsList(readOptions, columnFamilyHandles, keys);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void suggestCompactRange() throws RocksDBException {
|
||||
if (!db.isOwningHandle()) {
|
||||
throw new IllegalStateException("Database is closed");
|
||||
}
|
||||
if (!cfh.isOwningHandle()) {
|
||||
throw new IllegalStateException("Column family is closed");
|
||||
}
|
||||
db.suggestCompactRange(cfh);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void compactRange(byte[] begin, byte[] end, CompactRangeOptions options)
|
||||
throws RocksDBException {
|
||||
if (!db.isOwningHandle()) {
|
||||
throw new IllegalStateException("Database is closed");
|
||||
}
|
||||
if (!options.isOwningHandle()) {
|
||||
throw new IllegalStateException("CompactRangeOptions is closed");
|
||||
}
|
||||
if (!cfh.isOwningHandle()) {
|
||||
throw new IllegalStateException("Column family is closed");
|
||||
}
|
||||
db.compactRange(cfh, begin, end, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush(FlushOptions options) throws RocksDBException {
|
||||
if (!db.isOwningHandle()) {
|
||||
throw new IllegalStateException("Database is closed");
|
||||
}
|
||||
if (!options.isOwningHandle()) {
|
||||
throw new IllegalStateException("FlushOptions is closed");
|
||||
}
|
||||
if (!cfh.isOwningHandle()) {
|
||||
throw new IllegalStateException("Column family is closed");
|
||||
}
|
||||
db.flush(options, cfh);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flushWal(boolean sync) throws RocksDBException {
|
||||
if (!db.isOwningHandle()) {
|
||||
throw new IllegalStateException("Database is closed");
|
||||
}
|
||||
if (!cfh.isOwningHandle()) {
|
||||
throw new IllegalStateException("Column family is closed");
|
||||
}
|
||||
db.flushWal(sync);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLongProperty(String property) throws RocksDBException {
|
||||
if (!db.isOwningHandle()) {
|
||||
throw new IllegalStateException("Database is closed");
|
||||
}
|
||||
if (!cfh.isOwningHandle()) {
|
||||
throw new IllegalStateException("Column family is closed");
|
||||
}
|
||||
return db.getLongProperty(cfh, property);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(WriteOptions writeOptions, WriteBatch writeBatch) throws RocksDBException {
|
||||
if (!db.isOwningHandle()) {
|
||||
throw new IllegalStateException("Database is closed");
|
||||
}
|
||||
if (!writeOptions.isOwningHandle()) {
|
||||
throw new IllegalStateException("WriteOptions is closed");
|
||||
}
|
||||
if (!cfh.isOwningHandle()) {
|
||||
throw new IllegalStateException("Column family is closed");
|
||||
}
|
||||
db.write(writeOptions, writeBatch);
|
||||
}
|
||||
|
||||
@ -298,6 +404,15 @@ public sealed abstract class AbstractRocksDBColumn<T extends RocksDB> implements
|
||||
@Override
|
||||
@NotNull
|
||||
public RocksIterator newIterator(@NotNull ReadOptions readOptions) {
|
||||
if (!db.isOwningHandle()) {
|
||||
throw new IllegalStateException("Database is closed");
|
||||
}
|
||||
if (!readOptions.isOwningHandle()) {
|
||||
throw new IllegalStateException("ReadOptions is closed");
|
||||
}
|
||||
if (!cfh.isOwningHandle()) {
|
||||
throw new IllegalStateException("Column family is closed");
|
||||
}
|
||||
return db.newIterator(cfh, readOptions);
|
||||
}
|
||||
|
||||
@ -310,4 +425,8 @@ public sealed abstract class AbstractRocksDBColumn<T extends RocksDB> implements
|
||||
public BufferAllocator getAllocator() {
|
||||
return alloc;
|
||||
}
|
||||
|
||||
public MeterRegistry getMeterRegistry() {
|
||||
return meterRegistry;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package it.cavallium.dbengine.database.disk;
|
||||
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
import io.net5.buffer.api.BufferAllocator;
|
||||
import it.cavallium.dbengine.client.IndicizerAnalyzers;
|
||||
import it.cavallium.dbengine.client.IndicizerSimilarities;
|
||||
@ -31,11 +32,13 @@ public class LLLocalDatabaseConnection implements LLDatabaseConnection {
|
||||
|
||||
private final AtomicBoolean connected = new AtomicBoolean();
|
||||
private final BufferAllocator allocator;
|
||||
private final MeterRegistry meterRegistry;
|
||||
private final Path basePath;
|
||||
private final AtomicReference<LLTempLMDBEnv> env = new AtomicReference<>();
|
||||
|
||||
public LLLocalDatabaseConnection(BufferAllocator allocator, Path basePath) {
|
||||
public LLLocalDatabaseConnection(BufferAllocator allocator, MeterRegistry meterRegistry, Path basePath) {
|
||||
this.allocator = allocator;
|
||||
this.meterRegistry = meterRegistry;
|
||||
this.basePath = basePath;
|
||||
}
|
||||
|
||||
@ -44,6 +47,10 @@ public class LLLocalDatabaseConnection implements LLDatabaseConnection {
|
||||
return allocator;
|
||||
}
|
||||
|
||||
public MeterRegistry getMeterRegistry() {
|
||||
return meterRegistry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<LLDatabaseConnection> connect() {
|
||||
return Mono
|
||||
@ -70,6 +77,7 @@ public class LLLocalDatabaseConnection implements LLDatabaseConnection {
|
||||
return Mono
|
||||
.fromCallable(() -> new LLLocalKeyValueDatabase(
|
||||
allocator,
|
||||
meterRegistry,
|
||||
name,
|
||||
basePath.resolve("database_" + name),
|
||||
columns,
|
||||
@ -93,6 +101,7 @@ public class LLLocalDatabaseConnection implements LLDatabaseConnection {
|
||||
Objects.requireNonNull(env, "Environment not set");
|
||||
return new LLLocalMultiLuceneIndex(env,
|
||||
luceneOptions.inMemory() ? null : basePath.resolve("lucene"),
|
||||
meterRegistry,
|
||||
name,
|
||||
instancesCount,
|
||||
indicizerAnalyzers,
|
||||
@ -102,6 +111,7 @@ public class LLLocalDatabaseConnection implements LLDatabaseConnection {
|
||||
);
|
||||
} else {
|
||||
return new LLLocalLuceneIndex(luceneOptions.inMemory() ? null : basePath.resolve("lucene"),
|
||||
meterRegistry,
|
||||
name,
|
||||
indicizerAnalyzers,
|
||||
indicizerSimilarities,
|
||||
|
@ -808,7 +808,7 @@ public class LLLocalDictionary implements LLDictionary {
|
||||
rangeSend -> Flux.using(
|
||||
() -> new LLLocalEntryReactiveRocksIterator(db, rangeSend,
|
||||
databaseOptions.allowNettyDirect(), resolveSnapshot(snapshot)),
|
||||
llLocalEntryReactiveRocksIterator -> llLocalEntryReactiveRocksIterator.flux().subscribeOn(dbScheduler),
|
||||
iterator -> iterator.flux().subscribeOn(dbScheduler, false),
|
||||
LLLocalReactiveRocksIterator::close
|
||||
).transform(LLUtils::handleDiscard),
|
||||
rangeSend -> Mono.fromRunnable(rangeSend::close)
|
||||
@ -821,7 +821,7 @@ public class LLLocalDictionary implements LLDictionary {
|
||||
rangeSend -> Flux.using(
|
||||
() -> new LLLocalGroupedEntryReactiveRocksIterator(db, prefixLength, rangeSend,
|
||||
databaseOptions.allowNettyDirect(), resolveSnapshot(snapshot)),
|
||||
reactiveRocksIterator -> reactiveRocksIterator.flux().subscribeOn(dbScheduler),
|
||||
iterator -> iterator.flux().subscribeOn(dbScheduler, false),
|
||||
LLLocalGroupedReactiveRocksIterator::close
|
||||
).transform(LLUtils::handleDiscard),
|
||||
rangeSend -> Mono.fromRunnable(rangeSend::close)
|
||||
@ -852,7 +852,7 @@ public class LLLocalDictionary implements LLDictionary {
|
||||
rangeSend -> Flux.using(
|
||||
() -> new LLLocalGroupedKeyReactiveRocksIterator(db, prefixLength, rangeSend,
|
||||
databaseOptions.allowNettyDirect(), resolveSnapshot(snapshot)),
|
||||
reactiveRocksIterator -> reactiveRocksIterator.flux().subscribeOn(dbScheduler),
|
||||
iterator -> iterator.flux().subscribeOn(dbScheduler, false),
|
||||
LLLocalGroupedReactiveRocksIterator::close
|
||||
).transform(LLUtils::handleDiscard),
|
||||
rangeSend -> Mono.fromRunnable(rangeSend::close)
|
||||
@ -951,7 +951,7 @@ public class LLLocalDictionary implements LLDictionary {
|
||||
() -> new LLLocalKeyReactiveRocksIterator(db, rangeSend,
|
||||
databaseOptions.allowNettyDirect(), resolveSnapshot(snapshot)
|
||||
),
|
||||
llLocalKeyReactiveRocksIterator -> llLocalKeyReactiveRocksIterator.flux().subscribeOn(dbScheduler),
|
||||
iterator -> iterator.flux().subscribeOn(dbScheduler, false),
|
||||
LLLocalReactiveRocksIterator::close
|
||||
).transform(LLUtils::handleDiscard),
|
||||
rangeSend -> Mono.fromRunnable(rangeSend::close)
|
||||
|
@ -2,6 +2,7 @@ package it.cavallium.dbengine.database.disk;
|
||||
|
||||
import static it.cavallium.dbengine.database.LLUtils.MARKER_ROCKSDB;
|
||||
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
import io.net5.buffer.api.BufferAllocator;
|
||||
import io.net5.util.internal.PlatformDependent;
|
||||
import it.cavallium.dbengine.database.Column;
|
||||
@ -73,6 +74,7 @@ public class LLLocalKeyValueDatabase implements LLKeyValueDatabase {
|
||||
RocksDB.DEFAULT_COLUMN_FAMILY);
|
||||
|
||||
private final BufferAllocator allocator;
|
||||
private final MeterRegistry meterRegistry;
|
||||
private final Scheduler dbScheduler;
|
||||
|
||||
// Configurations
|
||||
@ -89,6 +91,7 @@ public class LLLocalKeyValueDatabase implements LLKeyValueDatabase {
|
||||
|
||||
@SuppressWarnings("SwitchStatementWithTooFewBranches")
|
||||
public LLLocalKeyValueDatabase(BufferAllocator allocator,
|
||||
MeterRegistry meterRegistry,
|
||||
String name,
|
||||
@Nullable Path path,
|
||||
List<Column> columns,
|
||||
@ -96,6 +99,7 @@ public class LLLocalKeyValueDatabase implements LLKeyValueDatabase {
|
||||
DatabaseOptions databaseOptions) throws IOException {
|
||||
this.name = name;
|
||||
this.allocator = allocator;
|
||||
this.meterRegistry = meterRegistry;
|
||||
|
||||
if (databaseOptions.allowNettyDirect()) {
|
||||
if (!PlatformDependent.hasUnsafe()) {
|
||||
@ -496,11 +500,11 @@ public class LLLocalKeyValueDatabase implements LLKeyValueDatabase {
|
||||
|
||||
private RocksDBColumn getRocksDBColumn(RocksDB db, ColumnFamilyHandle cfh) {
|
||||
if (db instanceof OptimisticTransactionDB optimisticTransactionDB) {
|
||||
return new OptimisticRocksDBColumn(optimisticTransactionDB, databaseOptions, allocator, cfh);
|
||||
return new OptimisticRocksDBColumn(optimisticTransactionDB, databaseOptions, allocator, cfh, meterRegistry);
|
||||
} else if (db instanceof TransactionDB) {
|
||||
return new PessimisticRocksDBColumn((TransactionDB) db, databaseOptions, allocator, cfh);
|
||||
return new PessimisticRocksDBColumn((TransactionDB) db, databaseOptions, allocator, cfh, meterRegistry);
|
||||
} else {
|
||||
return new StandardRocksDBColumn(db, databaseOptions, allocator, cfh);
|
||||
return new StandardRocksDBColumn(db, databaseOptions, allocator, cfh, meterRegistry);
|
||||
}
|
||||
}
|
||||
|
||||
@ -547,6 +551,11 @@ public class LLLocalKeyValueDatabase implements LLKeyValueDatabase {
|
||||
return allocator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MeterRegistry getMeterRegistry() {
|
||||
return meterRegistry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<LLSnapshot> takeSnapshot() {
|
||||
return Mono
|
||||
|
@ -3,6 +3,8 @@ package it.cavallium.dbengine.database.disk;
|
||||
import static it.cavallium.dbengine.database.LLUtils.MARKER_LUCENE;
|
||||
import static it.cavallium.dbengine.lucene.searcher.LLSearchTransformer.NO_TRANSFORMATION;
|
||||
|
||||
import com.google.common.util.concurrent.Uninterruptibles;
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
import io.net5.buffer.api.Send;
|
||||
import it.cavallium.dbengine.client.DirectIOOptions;
|
||||
import it.cavallium.dbengine.client.IndicizerAnalyzers;
|
||||
@ -28,6 +30,9 @@ import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Phaser;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
@ -49,8 +54,10 @@ import org.apache.lucene.store.NIOFSDirectory;
|
||||
import org.apache.lucene.store.NRTCachingDirectory;
|
||||
import org.apache.lucene.util.Constants;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.warp.commonutils.functional.IORunnable;
|
||||
import org.warp.commonutils.log.Logger;
|
||||
import org.warp.commonutils.log.LoggerFactory;
|
||||
import org.warp.commonutils.type.ShortNamedThreadFactory;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.scheduler.Scheduler;
|
||||
@ -67,7 +74,9 @@ public class LLLocalLuceneIndex implements LLLuceneIndex {
|
||||
* concurrent commits or concurrent refreshes.
|
||||
*/
|
||||
private static final Scheduler luceneHeavyTasksScheduler = Schedulers.single(Schedulers.boundedElastic());
|
||||
private static final ExecutorService SAFE_EXECUTOR = Executors.newCachedThreadPool(new ShortNamedThreadFactory("lucene-index-impl"));
|
||||
|
||||
private final MeterRegistry meterRegistry;
|
||||
private final String luceneIndexName;
|
||||
private final IndexWriter indexWriter;
|
||||
private final SnapshotsManager snapshotsManager;
|
||||
@ -81,11 +90,13 @@ public class LLLocalLuceneIndex implements LLLuceneIndex {
|
||||
private final AtomicBoolean closeRequested = new AtomicBoolean();
|
||||
|
||||
public LLLocalLuceneIndex(@Nullable Path luceneBasePath,
|
||||
MeterRegistry meterRegistry,
|
||||
String name,
|
||||
IndicizerAnalyzers indicizerAnalyzers,
|
||||
IndicizerSimilarities indicizerSimilarities,
|
||||
LuceneOptions luceneOptions,
|
||||
@Nullable LuceneHacks luceneHacks) throws IOException {
|
||||
this.meterRegistry = meterRegistry;
|
||||
Path directoryPath;
|
||||
if (luceneOptions.inMemory() != (luceneBasePath == null)) {
|
||||
throw new IllegalArgumentException();
|
||||
@ -234,13 +245,45 @@ public class LLLocalLuceneIndex implements LLLuceneIndex {
|
||||
}
|
||||
|
||||
private <V> Mono<V> ensureOpen(Mono<V> mono) {
|
||||
return Mono.defer(() -> {
|
||||
return Mono.<Void>fromCallable(() -> {
|
||||
if (closeRequested.get()) {
|
||||
return Mono.error(new IllegalStateException("Lucene index is closed"));
|
||||
throw new IllegalStateException("Lucene index is closed");
|
||||
} else {
|
||||
return mono;
|
||||
return null;
|
||||
}
|
||||
}).doFirst(activeTasks::register).doFinally(s -> activeTasks.arriveAndDeregister());
|
||||
}).then(mono).doFirst(activeTasks::register).doFinally(s -> activeTasks.arriveAndDeregister());
|
||||
}
|
||||
|
||||
private <V> Mono<V> runSafe(Callable<V> callable) {
|
||||
return Mono.<V>create(sink -> {
|
||||
var future = SAFE_EXECUTOR.submit(() -> {
|
||||
try {
|
||||
var result = callable.call();
|
||||
if (result != null) {
|
||||
sink.success(result);
|
||||
} else {
|
||||
sink.success();
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
sink.error(e);
|
||||
}
|
||||
});
|
||||
sink.onDispose(() -> future.cancel(false));
|
||||
});
|
||||
}
|
||||
|
||||
private <V> Mono<V> runSafe(IORunnable runnable) {
|
||||
return Mono.create(sink -> {
|
||||
var future = SAFE_EXECUTOR.submit(() -> {
|
||||
try {
|
||||
runnable.run();
|
||||
sink.success();
|
||||
} catch (Throwable e) {
|
||||
sink.error(e);
|
||||
}
|
||||
});
|
||||
sink.onDispose(() -> future.cancel(false));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -250,40 +293,29 @@ public class LLLocalLuceneIndex implements LLLuceneIndex {
|
||||
|
||||
@Override
|
||||
public Mono<Void> addDocument(LLTerm key, LLDocument doc) {
|
||||
return Mono.<Void>fromCallable(() -> {
|
||||
indexWriter.addDocument(LLUtils.toDocument(doc));
|
||||
return null;
|
||||
}).subscribeOn(Schedulers.boundedElastic()).transform(this::ensureOpen);
|
||||
return this.<Void>runSafe(() -> indexWriter.addDocument(LLUtils.toDocument(doc))).transform(this::ensureOpen);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> addDocuments(Flux<Entry<LLTerm, LLDocument>> documents) {
|
||||
return documents
|
||||
.collectList()
|
||||
.flatMap(documentsList -> Mono
|
||||
.<Void>fromCallable(() -> {
|
||||
indexWriter.addDocuments(LLUtils.toDocumentsFromEntries(documentsList));
|
||||
return null;
|
||||
}).subscribeOn(Schedulers.boundedElastic())
|
||||
)
|
||||
.flatMap(documentsList -> this.<Void>runSafe(() -> indexWriter.addDocuments(LLUtils
|
||||
.toDocumentsFromEntries(documentsList))))
|
||||
.transform(this::ensureOpen);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Mono<Void> deleteDocument(LLTerm id) {
|
||||
return Mono.<Void>fromCallable(() -> {
|
||||
indexWriter.deleteDocuments(LLUtils.toTerm(id));
|
||||
return null;
|
||||
}).subscribeOn(Schedulers.boundedElastic()).transform(this::ensureOpen);
|
||||
return this.<Void>runSafe(() -> indexWriter.deleteDocuments(LLUtils.toTerm(id))).transform(this::ensureOpen);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> updateDocument(LLTerm id, LLDocument document) {
|
||||
return Mono.<Void>fromCallable(() -> {
|
||||
indexWriter.updateDocument(LLUtils.toTerm(id), LLUtils.toDocument(document));
|
||||
return null;
|
||||
}).subscribeOn(Schedulers.boundedElastic()).transform(this::ensureOpen);
|
||||
return this
|
||||
.<Void>runSafe(() -> indexWriter.updateDocument(LLUtils.toTerm(id), LLUtils.toDocument(document)))
|
||||
.transform(this::ensureOpen);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -292,29 +324,21 @@ public class LLLocalLuceneIndex implements LLLuceneIndex {
|
||||
}
|
||||
|
||||
private Mono<Void> updateDocuments(Map<LLTerm, LLDocument> documentsMap) {
|
||||
return Mono
|
||||
.<Void>fromCallable(() -> {
|
||||
for (Entry<LLTerm, LLDocument> entry : documentsMap.entrySet()) {
|
||||
LLTerm key = entry.getKey();
|
||||
LLDocument value = entry.getValue();
|
||||
indexWriter.updateDocument(LLUtils.toTerm(key), LLUtils.toDocument(value));
|
||||
}
|
||||
return null;
|
||||
})
|
||||
.subscribeOn(Schedulers.boundedElastic())
|
||||
.transform(this::ensureOpen);
|
||||
return this.<Void>runSafe(() -> {
|
||||
for (Entry<LLTerm, LLDocument> entry : documentsMap.entrySet()) {
|
||||
LLTerm key = entry.getKey();
|
||||
LLDocument value = entry.getValue();
|
||||
indexWriter.updateDocument(LLUtils.toTerm(key), LLUtils.toDocument(value));
|
||||
}
|
||||
}).transform(this::ensureOpen);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> deleteAll() {
|
||||
return Mono.<Void>fromCallable(() -> {
|
||||
//noinspection BlockingMethodInNonBlockingContext
|
||||
return this.<Void>runSafe(() -> {
|
||||
indexWriter.deleteAll();
|
||||
//noinspection BlockingMethodInNonBlockingContext
|
||||
indexWriter.forceMergeDeletes(true);
|
||||
//noinspection BlockingMethodInNonBlockingContext
|
||||
indexWriter.commit();
|
||||
return null;
|
||||
}).subscribeOn(luceneHeavyTasksScheduler).transform(this::ensureOpen);
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
package it.cavallium.dbengine.database.disk;
|
||||
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
import io.net5.buffer.api.Send;
|
||||
import it.cavallium.dbengine.client.IndicizerAnalyzers;
|
||||
import it.cavallium.dbengine.client.IndicizerSimilarities;
|
||||
@ -39,6 +40,7 @@ import reactor.util.function.Tuple2;
|
||||
|
||||
public class LLLocalMultiLuceneIndex implements LLLuceneIndex {
|
||||
|
||||
private final MeterRegistry meterRegistry;
|
||||
private final ConcurrentHashMap<Long, LLSnapshot[]> registeredSnapshots = new ConcurrentHashMap<>();
|
||||
private final AtomicLong nextSnapshotNumber = new AtomicLong(1);
|
||||
private final LLLocalLuceneIndex[] luceneIndices;
|
||||
@ -49,6 +51,7 @@ public class LLLocalMultiLuceneIndex implements LLLuceneIndex {
|
||||
|
||||
public LLLocalMultiLuceneIndex(LLTempLMDBEnv env,
|
||||
Path lucene,
|
||||
MeterRegistry meterRegistry,
|
||||
String name,
|
||||
int instancesCount,
|
||||
IndicizerAnalyzers indicizerAnalyzers,
|
||||
@ -60,6 +63,7 @@ public class LLLocalMultiLuceneIndex implements LLLuceneIndex {
|
||||
throw new IOException("Unsupported instances count: " + instancesCount);
|
||||
}
|
||||
|
||||
this.meterRegistry = meterRegistry;
|
||||
LLLocalLuceneIndex[] luceneIndices = new LLLocalLuceneIndex[instancesCount];
|
||||
for (int i = 0; i < instancesCount; i++) {
|
||||
String instanceName;
|
||||
@ -69,6 +73,7 @@ public class LLLocalMultiLuceneIndex implements LLLuceneIndex {
|
||||
instanceName = name + "_" + String.format("%03d", i);
|
||||
}
|
||||
luceneIndices[i] = new LLLocalLuceneIndex(lucene,
|
||||
meterRegistry,
|
||||
instanceName,
|
||||
indicizerAnalyzers,
|
||||
indicizerSimilarities,
|
||||
|
@ -2,6 +2,7 @@ package it.cavallium.dbengine.database.disk;
|
||||
|
||||
import static it.cavallium.dbengine.database.LLUtils.MARKER_ROCKSDB;
|
||||
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
import io.net5.buffer.api.Buffer;
|
||||
import io.net5.buffer.api.BufferAllocator;
|
||||
import io.net5.buffer.api.MemoryManager;
|
||||
@ -27,11 +28,14 @@ import reactor.core.scheduler.Schedulers;
|
||||
|
||||
public final class OptimisticRocksDBColumn extends AbstractRocksDBColumn<OptimisticTransactionDB> {
|
||||
|
||||
private static final boolean ALWAYS_PRINT_OPTIMISTIC_RETRIES = false;
|
||||
|
||||
public OptimisticRocksDBColumn(OptimisticTransactionDB db,
|
||||
DatabaseOptions databaseOptions,
|
||||
BufferAllocator alloc,
|
||||
ColumnFamilyHandle cfh) {
|
||||
super(db, databaseOptions, alloc, cfh);
|
||||
ColumnFamilyHandle cfh,
|
||||
MeterRegistry meterRegistry) {
|
||||
super(db, databaseOptions, alloc, cfh, meterRegistry);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -156,20 +160,23 @@ public final class OptimisticRocksDBColumn extends AbstractRocksDBColumn<Optimis
|
||||
sentCurData.close();
|
||||
}
|
||||
retries++;
|
||||
if (retries >= 100 && retries % 100 == 0) {
|
||||
logger.warn(MARKER_ROCKSDB, "Failed optimistic transaction {} (update):"
|
||||
+ " waiting 5ms before retrying for the {} time", LLUtils.toStringSafe(key), retries);
|
||||
} else if (logger.isDebugEnabled(MARKER_ROCKSDB)) {
|
||||
logger.debug(MARKER_ROCKSDB, "Failed optimistic transaction {} (update):"
|
||||
+ " waiting 5ms before retrying", LLUtils.toStringSafe(key));
|
||||
}
|
||||
|
||||
if (retries == 1) {
|
||||
retryTime = new ExponentialPageLimits(0, 5, 2000);
|
||||
}
|
||||
long retryMs = retryTime.getPageLimit(retries);
|
||||
|
||||
// +- 20%
|
||||
retryMs = retryMs + (long) (retryMs * 0.2d * ThreadLocalRandom.current().nextDouble(-1.0d, 1.0d));
|
||||
// Wait for 5ms
|
||||
|
||||
if (retries >= 5 && retries % 5 == 0 || ALWAYS_PRINT_OPTIMISTIC_RETRIES) {
|
||||
logger.warn(MARKER_ROCKSDB, "Failed optimistic transaction {} (update):"
|
||||
+ " waiting {} ms before retrying for the {} time", LLUtils.toStringSafe(key), retryMs, retries);
|
||||
} else if (logger.isDebugEnabled(MARKER_ROCKSDB)) {
|
||||
logger.debug(MARKER_ROCKSDB, "Failed optimistic transaction {} (update):"
|
||||
+ " waiting {} ms before retrying for the {} time", LLUtils.toStringSafe(key), retryMs, retries);
|
||||
}
|
||||
// Wait for n milliseconds
|
||||
try {
|
||||
Thread.sleep(retryMs);
|
||||
} catch (InterruptedException e) {
|
||||
|
@ -2,6 +2,7 @@ package it.cavallium.dbengine.database.disk;
|
||||
|
||||
import static it.cavallium.dbengine.database.LLUtils.MARKER_ROCKSDB;
|
||||
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
import io.net5.buffer.api.Buffer;
|
||||
import io.net5.buffer.api.BufferAllocator;
|
||||
import io.net5.buffer.api.MemoryManager;
|
||||
@ -29,8 +30,8 @@ public final class PessimisticRocksDBColumn extends AbstractRocksDBColumn<Transa
|
||||
public PessimisticRocksDBColumn(TransactionDB db,
|
||||
DatabaseOptions databaseOptions,
|
||||
BufferAllocator alloc,
|
||||
ColumnFamilyHandle cfh) {
|
||||
super(db, databaseOptions, alloc, cfh);
|
||||
ColumnFamilyHandle cfh, MeterRegistry meterRegistry) {
|
||||
super(db, databaseOptions, alloc, cfh, meterRegistry);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,5 +1,6 @@
|
||||
package it.cavallium.dbengine.database.disk;
|
||||
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
import io.net5.buffer.api.Buffer;
|
||||
import io.net5.buffer.api.BufferAllocator;
|
||||
import io.net5.buffer.api.Send;
|
||||
@ -62,5 +63,7 @@ public sealed interface RocksDBColumn permits AbstractRocksDBColumn {
|
||||
|
||||
BufferAllocator getAllocator();
|
||||
|
||||
MeterRegistry getMeterRegistry();
|
||||
|
||||
boolean supportsTransactions();
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package it.cavallium.dbengine.database.disk;
|
||||
|
||||
import static it.cavallium.dbengine.database.LLUtils.MARKER_ROCKSDB;
|
||||
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
import io.net5.buffer.api.Buffer;
|
||||
import io.net5.buffer.api.BufferAllocator;
|
||||
import io.net5.buffer.api.Send;
|
||||
@ -25,8 +26,8 @@ public final class StandardRocksDBColumn extends AbstractRocksDBColumn<RocksDB>
|
||||
public StandardRocksDBColumn(RocksDB db,
|
||||
DatabaseOptions databaseOptions,
|
||||
BufferAllocator alloc,
|
||||
ColumnFamilyHandle cfh) {
|
||||
super(db, databaseOptions, alloc, cfh);
|
||||
ColumnFamilyHandle cfh, MeterRegistry meterRegistry) {
|
||||
super(db, databaseOptions, alloc, cfh, meterRegistry);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,5 +1,6 @@
|
||||
package it.cavallium.dbengine.database.memory;
|
||||
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
import io.net5.buffer.api.BufferAllocator;
|
||||
import it.cavallium.dbengine.client.DatabaseOptions;
|
||||
import it.cavallium.dbengine.client.IndicizerAnalyzers;
|
||||
@ -24,9 +25,11 @@ public class LLMemoryDatabaseConnection implements LLDatabaseConnection {
|
||||
}
|
||||
|
||||
private final BufferAllocator allocator;
|
||||
private final MeterRegistry meterRegistry;
|
||||
|
||||
public LLMemoryDatabaseConnection(BufferAllocator allocator) {
|
||||
public LLMemoryDatabaseConnection(BufferAllocator allocator, MeterRegistry meterRegistry) {
|
||||
this.allocator = allocator;
|
||||
this.meterRegistry = meterRegistry;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -34,6 +37,11 @@ public class LLMemoryDatabaseConnection implements LLDatabaseConnection {
|
||||
return allocator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MeterRegistry getMeterRegistry() {
|
||||
return meterRegistry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<LLDatabaseConnection> connect() {
|
||||
return Mono.empty();
|
||||
@ -46,6 +54,7 @@ public class LLMemoryDatabaseConnection implements LLDatabaseConnection {
|
||||
return Mono
|
||||
.<LLKeyValueDatabase>fromCallable(() -> new LLMemoryKeyValueDatabase(
|
||||
allocator,
|
||||
meterRegistry,
|
||||
name,
|
||||
columns
|
||||
))
|
||||
@ -61,6 +70,7 @@ public class LLMemoryDatabaseConnection implements LLDatabaseConnection {
|
||||
@Nullable LuceneHacks luceneHacks) {
|
||||
return Mono
|
||||
.<LLLuceneIndex>fromCallable(() -> new LLLocalLuceneIndex(null,
|
||||
meterRegistry,
|
||||
name,
|
||||
indicizerAnalyzers,
|
||||
indicizerSimilarities,
|
||||
|
@ -1,5 +1,6 @@
|
||||
package it.cavallium.dbengine.database.memory;
|
||||
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
import io.net5.buffer.api.BufferAllocator;
|
||||
import it.cavallium.dbengine.database.Column;
|
||||
import it.cavallium.dbengine.database.LLDictionary;
|
||||
@ -18,6 +19,7 @@ import reactor.core.publisher.Mono;
|
||||
public class LLMemoryKeyValueDatabase implements LLKeyValueDatabase {
|
||||
|
||||
private final BufferAllocator allocator;
|
||||
private final MeterRegistry meterRegistry;
|
||||
private final String name;
|
||||
private final AtomicLong nextSnapshotNumber = new AtomicLong(1);
|
||||
|
||||
@ -25,8 +27,12 @@ public class LLMemoryKeyValueDatabase implements LLKeyValueDatabase {
|
||||
private final ConcurrentHashMap<String, ConcurrentSkipListMap<ByteList, ByteList>> mainDb;
|
||||
private final ConcurrentHashMap<String, LLMemoryDictionary> singletons = new ConcurrentHashMap<>();
|
||||
|
||||
public LLMemoryKeyValueDatabase(BufferAllocator allocator, String name, List<Column> columns) {
|
||||
public LLMemoryKeyValueDatabase(BufferAllocator allocator,
|
||||
MeterRegistry meterRegistry,
|
||||
String name,
|
||||
List<Column> columns) {
|
||||
this.allocator = allocator;
|
||||
this.meterRegistry = meterRegistry;
|
||||
this.name = name;
|
||||
this.mainDb = new ConcurrentHashMap<>();
|
||||
for (Column column : columns) {
|
||||
@ -80,6 +86,11 @@ public class LLMemoryKeyValueDatabase implements LLKeyValueDatabase {
|
||||
return allocator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MeterRegistry getMeterRegistry() {
|
||||
return meterRegistry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> close() {
|
||||
return Mono
|
||||
|
@ -2,6 +2,7 @@ package it.cavallium.dbengine;
|
||||
|
||||
import static it.cavallium.dbengine.DbTestUtils.ensureNoLeaks;
|
||||
|
||||
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
|
||||
import it.cavallium.dbengine.DbTestUtils.TempDb;
|
||||
import it.cavallium.dbengine.DbTestUtils.TestAllocator;
|
||||
import it.cavallium.dbengine.client.DatabaseOptions;
|
||||
@ -56,7 +57,7 @@ public class LocalTemporaryDbGenerator implements TemporaryDbGenerator {
|
||||
return null;
|
||||
})
|
||||
.subscribeOn(Schedulers.boundedElastic())
|
||||
.then(new LLLocalDatabaseConnection(allocator.allocator(), wrkspcPath).connect())
|
||||
.then(new LLLocalDatabaseConnection(allocator.allocator(), new SimpleMeterRegistry(), wrkspcPath).connect())
|
||||
.flatMap(conn -> {
|
||||
SwappableLuceneSearcher searcher = new SwappableLuceneSearcher();
|
||||
var luceneHacks = new LuceneHacks(() -> searcher, () -> searcher);
|
||||
|
@ -1,5 +1,6 @@
|
||||
package it.cavallium.dbengine;
|
||||
|
||||
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
|
||||
import it.cavallium.dbengine.DbTestUtils.TempDb;
|
||||
import it.cavallium.dbengine.DbTestUtils.TestAllocator;
|
||||
import it.cavallium.dbengine.client.DatabaseOptions;
|
||||
@ -28,7 +29,7 @@ public class MemoryTemporaryDbGenerator implements TemporaryDbGenerator {
|
||||
public Mono<TempDb> openTempDb(TestAllocator allocator) {
|
||||
boolean canUseNettyDirect = DbTestUtils.computeCanUseNettyDirect();
|
||||
return Mono
|
||||
.fromCallable(() -> new LLMemoryDatabaseConnection(allocator.allocator()))
|
||||
.fromCallable(() -> new LLMemoryDatabaseConnection(allocator.allocator(), new SimpleMeterRegistry()))
|
||||
.flatMap(conn -> {
|
||||
SwappableLuceneSearcher searcher = new SwappableLuceneSearcher();
|
||||
var luceneHacks = new LuceneHacks(() -> searcher, () -> searcher);
|
||||
|
Loading…
x
Reference in New Issue
Block a user