Implement more phasers
This commit is contained in:
parent
51b60168f7
commit
936c07406e
@ -18,6 +18,8 @@ import org.apache.lucene.search.similarities.Similarity;
|
|||||||
import org.apache.lucene.store.AlreadyClosedException;
|
import org.apache.lucene.store.AlreadyClosedException;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
import reactor.core.publisher.Sinks;
|
import reactor.core.publisher.Sinks;
|
||||||
@ -26,11 +28,14 @@ import reactor.core.scheduler.Schedulers;
|
|||||||
|
|
||||||
public class CachedIndexSearcherManager {
|
public class CachedIndexSearcherManager {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(CachedIndexSearcherManager.class);
|
||||||
|
|
||||||
private final SnapshotsManager snapshotsManager;
|
private final SnapshotsManager snapshotsManager;
|
||||||
private final Similarity similarity;
|
private final Similarity similarity;
|
||||||
private final SearcherManager searcherManager;
|
private final SearcherManager searcherManager;
|
||||||
private final Duration queryRefreshDebounceTime;
|
private final Duration queryRefreshDebounceTime;
|
||||||
private final Phaser activeSearchers = new Phaser(1);
|
private final Phaser activeSearchers = new Phaser(1);
|
||||||
|
private final Phaser activeRefreshes = new Phaser(1);
|
||||||
|
|
||||||
private final LoadingCache<LLSnapshot, Mono<CachedIndexSearcher>> cachedSnapshotSearchers;
|
private final LoadingCache<LLSnapshot, Mono<CachedIndexSearcher>> cachedSnapshotSearchers;
|
||||||
private final Mono<CachedIndexSearcher> cachedMainSearcher;
|
private final Mono<CachedIndexSearcher> cachedMainSearcher;
|
||||||
@ -55,7 +60,13 @@ public class CachedIndexSearcherManager {
|
|||||||
);
|
);
|
||||||
|
|
||||||
Mono
|
Mono
|
||||||
.fromRunnable(this::scheduledQueryRefresh)
|
.fromRunnable(() -> {
|
||||||
|
try {
|
||||||
|
maybeRefreshBlocking();
|
||||||
|
} catch (Exception ex) {
|
||||||
|
logger.error("Failed to refresh the searcher manager", ex);
|
||||||
|
}
|
||||||
|
})
|
||||||
.repeatWhen(s -> s.delayElements(queryRefreshDebounceTime, Schedulers.boundedElastic()))
|
.repeatWhen(s -> s.delayElements(queryRefreshDebounceTime, Schedulers.boundedElastic()))
|
||||||
.subscribeOn(Schedulers.boundedElastic())
|
.subscribeOn(Schedulers.boundedElastic())
|
||||||
.takeUntilOther(closeRequested.asMono())
|
.takeUntilOther(closeRequested.asMono())
|
||||||
@ -98,37 +109,31 @@ public class CachedIndexSearcherManager {
|
|||||||
try {
|
try {
|
||||||
// Mark as removed from cache
|
// Mark as removed from cache
|
||||||
indexSearcher.removeFromCache();
|
indexSearcher.removeFromCache();
|
||||||
} catch (IOException e) {
|
} catch (Exception ex) {
|
||||||
e.printStackTrace();
|
logger.error("Failed to release an old cached IndexSearcher", ex);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private void scheduledQueryRefresh() {
|
|
||||||
try {
|
|
||||||
boolean refreshStarted = searcherManager.maybeRefresh();
|
|
||||||
// if refreshStarted == false, another thread is currently already refreshing
|
|
||||||
} catch (AlreadyClosedException ignored) {
|
|
||||||
|
|
||||||
} catch (IOException ex) {
|
|
||||||
ex.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void maybeRefreshBlocking() throws IOException {
|
public void maybeRefreshBlocking() throws IOException {
|
||||||
try {
|
try {
|
||||||
|
activeRefreshes.register();
|
||||||
searcherManager.maybeRefreshBlocking();
|
searcherManager.maybeRefreshBlocking();
|
||||||
} catch (AlreadyClosedException ignored) {
|
} catch (AlreadyClosedException ignored) {
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
activeRefreshes.arriveAndDeregister();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void maybeRefresh() throws IOException {
|
public void maybeRefresh() throws IOException {
|
||||||
try {
|
try {
|
||||||
|
activeRefreshes.register();
|
||||||
searcherManager.maybeRefresh();
|
searcherManager.maybeRefresh();
|
||||||
} catch (AlreadyClosedException ignored) {
|
} catch (AlreadyClosedException ignored) {
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
activeRefreshes.arriveAndDeregister();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,8 +176,8 @@ public class CachedIndexSearcherManager {
|
|||||||
try {
|
try {
|
||||||
// Decrement reference count
|
// Decrement reference count
|
||||||
indexSearcher.decUsage();
|
indexSearcher.decUsage();
|
||||||
} catch (IOException e) {
|
} catch (Exception ex) {
|
||||||
e.printStackTrace();
|
logger.error("Failed to release an used IndexSearcher", ex);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -182,7 +187,12 @@ public class CachedIndexSearcherManager {
|
|||||||
.fromRunnable(this.closeRequested::tryEmitEmpty)
|
.fromRunnable(this.closeRequested::tryEmitEmpty)
|
||||||
.then(refresherClosed.asMono())
|
.then(refresherClosed.asMono())
|
||||||
.then(Mono.fromRunnable(() -> {
|
.then(Mono.fromRunnable(() -> {
|
||||||
activeSearchers.arriveAndAwaitAdvance();
|
if (!activeRefreshes.isTerminated()) {
|
||||||
|
activeRefreshes.arriveAndAwaitAdvance();
|
||||||
|
}
|
||||||
|
if (!activeSearchers.isTerminated()) {
|
||||||
|
activeSearchers.arriveAndAwaitAdvance();
|
||||||
|
}
|
||||||
cachedSnapshotSearchers.invalidateAll();
|
cachedSnapshotSearchers.invalidateAll();
|
||||||
cachedSnapshotSearchers.cleanUp();
|
cachedSnapshotSearchers.cleanUp();
|
||||||
}));
|
}));
|
||||||
|
@ -15,7 +15,6 @@ import it.cavallium.dbengine.database.LLTerm;
|
|||||||
import it.cavallium.dbengine.database.LLUtils;
|
import it.cavallium.dbengine.database.LLUtils;
|
||||||
import it.cavallium.dbengine.lucene.AlwaysDirectIOFSDirectory;
|
import it.cavallium.dbengine.lucene.AlwaysDirectIOFSDirectory;
|
||||||
import it.cavallium.dbengine.lucene.LuceneUtils;
|
import it.cavallium.dbengine.lucene.LuceneUtils;
|
||||||
import it.cavallium.dbengine.lucene.ScheduledTaskLifecycle;
|
|
||||||
import it.cavallium.dbengine.lucene.searcher.AdaptiveLuceneLocalSearcher;
|
import it.cavallium.dbengine.lucene.searcher.AdaptiveLuceneLocalSearcher;
|
||||||
import it.cavallium.dbengine.lucene.searcher.LocalQueryParams;
|
import it.cavallium.dbengine.lucene.searcher.LocalQueryParams;
|
||||||
import it.cavallium.dbengine.lucene.searcher.LuceneLocalSearcher;
|
import it.cavallium.dbengine.lucene.searcher.LuceneLocalSearcher;
|
||||||
@ -27,6 +26,7 @@ import java.util.HashMap;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.Phaser;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import org.apache.lucene.index.ConcurrentMergeScheduler;
|
import org.apache.lucene.index.ConcurrentMergeScheduler;
|
||||||
import org.apache.lucene.index.IndexWriter;
|
import org.apache.lucene.index.IndexWriter;
|
||||||
@ -85,7 +85,7 @@ public class LLLocalLuceneIndex implements LLLuceneIndex {
|
|||||||
private final Directory directory;
|
private final Directory directory;
|
||||||
private final boolean lowMemory;
|
private final boolean lowMemory;
|
||||||
|
|
||||||
private final ScheduledTaskLifecycle scheduledTasksLifecycle;
|
private final Phaser activeTasks = new Phaser(1);
|
||||||
|
|
||||||
public LLLocalLuceneIndex(@Nullable Path luceneBasePath,
|
public LLLocalLuceneIndex(@Nullable Path luceneBasePath,
|
||||||
String name,
|
String name,
|
||||||
@ -170,9 +170,6 @@ public class LLLocalLuceneIndex implements LLLuceneIndex {
|
|||||||
this.lowMemory = lowMemory;
|
this.lowMemory = lowMemory;
|
||||||
this.similarity = LuceneUtils.toPerFieldSimilarityWrapper(indicizerSimilarities);
|
this.similarity = LuceneUtils.toPerFieldSimilarityWrapper(indicizerSimilarities);
|
||||||
|
|
||||||
// Create scheduled tasks lifecycle manager
|
|
||||||
this.scheduledTasksLifecycle = new ScheduledTaskLifecycle();
|
|
||||||
|
|
||||||
IndexWriterConfig indexWriterConfig = new IndexWriterConfig(LuceneUtils.toPerFieldAnalyzerWrapper(indicizerAnalyzers));
|
IndexWriterConfig indexWriterConfig = new IndexWriterConfig(LuceneUtils.toPerFieldAnalyzerWrapper(indicizerAnalyzers));
|
||||||
indexWriterConfig.setOpenMode(IndexWriterConfig.OpenMode.CREATE_OR_APPEND);
|
indexWriterConfig.setOpenMode(IndexWriterConfig.OpenMode.CREATE_OR_APPEND);
|
||||||
indexWriterConfig.setIndexDeletionPolicy(snapshotter);
|
indexWriterConfig.setIndexDeletionPolicy(snapshotter);
|
||||||
@ -195,7 +192,7 @@ public class LLLocalLuceneIndex implements LLLuceneIndex {
|
|||||||
indexWriterConfig.setReaderPooling(false);
|
indexWriterConfig.setReaderPooling(false);
|
||||||
indexWriterConfig.setSimilarity(getSimilarity());
|
indexWriterConfig.setSimilarity(getSimilarity());
|
||||||
this.indexWriter = new IndexWriter(directory, indexWriterConfig);
|
this.indexWriter = new IndexWriter(directory, indexWriterConfig);
|
||||||
this.snapshotsManager = new SnapshotsManager(indexWriter, snapshotter, scheduledTasksLifecycle);
|
this.snapshotsManager = new SnapshotsManager(indexWriter, snapshotter);
|
||||||
this.searcherManager = new CachedIndexSearcherManager(indexWriter,
|
this.searcherManager = new CachedIndexSearcherManager(indexWriter,
|
||||||
snapshotsManager,
|
snapshotsManager,
|
||||||
getSimilarity(),
|
getSimilarity(),
|
||||||
@ -205,55 +202,15 @@ public class LLLocalLuceneIndex implements LLLuceneIndex {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Start scheduled tasks
|
// Start scheduled tasks
|
||||||
registerScheduledFixedTask(this::scheduledCommit, luceneOptions.commitDebounceTime());
|
var commitMillis = luceneOptions.commitDebounceTime().toMillis();
|
||||||
|
luceneHeavyTasksScheduler.schedulePeriodically(this::scheduledCommit, commitMillis, commitMillis,
|
||||||
|
TimeUnit.MILLISECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Similarity getSimilarity() {
|
private Similarity getSimilarity() {
|
||||||
return similarity;
|
return similarity;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerScheduledFixedTask(Runnable task, Duration duration) {
|
|
||||||
new PeriodicTask(task, duration).start();
|
|
||||||
}
|
|
||||||
|
|
||||||
private class PeriodicTask implements Runnable {
|
|
||||||
|
|
||||||
private final Runnable task;
|
|
||||||
private final Duration duration;
|
|
||||||
private volatile boolean cancelled = false;
|
|
||||||
|
|
||||||
public PeriodicTask(Runnable task, Duration duration) {
|
|
||||||
this.task = task;
|
|
||||||
this.duration = duration;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void start() {
|
|
||||||
luceneHeavyTasksScheduler.schedule(this,
|
|
||||||
duration.toMillis(),
|
|
||||||
TimeUnit.MILLISECONDS
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (!scheduledTasksLifecycle.tryStartScheduledTask()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
if (scheduledTasksLifecycle.isCancelled() || cancelled) return;
|
|
||||||
task.run();
|
|
||||||
if (scheduledTasksLifecycle.isCancelled() || cancelled) return;
|
|
||||||
luceneHeavyTasksScheduler.schedule(this, duration.toMillis(), TimeUnit.MILLISECONDS);
|
|
||||||
} finally {
|
|
||||||
scheduledTasksLifecycle.endScheduledTask();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void cancel() {
|
|
||||||
cancelled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getLuceneIndexName() {
|
public String getLuceneIndexName() {
|
||||||
return luceneIndexName;
|
return luceneIndexName;
|
||||||
@ -272,12 +229,12 @@ public class LLLocalLuceneIndex implements LLLuceneIndex {
|
|||||||
@Override
|
@Override
|
||||||
public Mono<Void> addDocument(LLTerm key, LLDocument doc) {
|
public Mono<Void> addDocument(LLTerm key, LLDocument doc) {
|
||||||
return Mono.fromCallable(() -> {
|
return Mono.fromCallable(() -> {
|
||||||
scheduledTasksLifecycle.startScheduledTask();
|
activeTasks.register();
|
||||||
try {
|
try {
|
||||||
indexWriter.addDocument(LLUtils.toDocument(doc));
|
indexWriter.addDocument(LLUtils.toDocument(doc));
|
||||||
return null;
|
return null;
|
||||||
} finally {
|
} finally {
|
||||||
scheduledTasksLifecycle.endScheduledTask();
|
activeTasks.arriveAndDeregister();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -288,12 +245,12 @@ public class LLLocalLuceneIndex implements LLLuceneIndex {
|
|||||||
.collectList()
|
.collectList()
|
||||||
.flatMap(documentsList -> Mono
|
.flatMap(documentsList -> Mono
|
||||||
.fromCallable(() -> {
|
.fromCallable(() -> {
|
||||||
scheduledTasksLifecycle.startScheduledTask();
|
activeTasks.register();
|
||||||
try {
|
try {
|
||||||
indexWriter.addDocuments(LLUtils.toDocumentsFromEntries(documentsList));
|
indexWriter.addDocuments(LLUtils.toDocumentsFromEntries(documentsList));
|
||||||
return null;
|
return null;
|
||||||
} finally {
|
} finally {
|
||||||
scheduledTasksLifecycle.endScheduledTask();
|
activeTasks.arriveAndDeregister();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -303,12 +260,12 @@ public class LLLocalLuceneIndex implements LLLuceneIndex {
|
|||||||
@Override
|
@Override
|
||||||
public Mono<Void> deleteDocument(LLTerm id) {
|
public Mono<Void> deleteDocument(LLTerm id) {
|
||||||
return Mono.fromCallable(() -> {
|
return Mono.fromCallable(() -> {
|
||||||
scheduledTasksLifecycle.startScheduledTask();
|
activeTasks.register();
|
||||||
try {
|
try {
|
||||||
indexWriter.deleteDocuments(LLUtils.toTerm(id));
|
indexWriter.deleteDocuments(LLUtils.toTerm(id));
|
||||||
return null;
|
return null;
|
||||||
} finally {
|
} finally {
|
||||||
scheduledTasksLifecycle.endScheduledTask();
|
activeTasks.arriveAndDeregister();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -316,11 +273,11 @@ public class LLLocalLuceneIndex implements LLLuceneIndex {
|
|||||||
@Override
|
@Override
|
||||||
public Mono<Void> updateDocument(LLTerm id, LLDocument document) {
|
public Mono<Void> updateDocument(LLTerm id, LLDocument document) {
|
||||||
return Mono.fromCallable(() -> {
|
return Mono.fromCallable(() -> {
|
||||||
scheduledTasksLifecycle.startScheduledTask();
|
activeTasks.register();
|
||||||
try {
|
try {
|
||||||
indexWriter.updateDocument(LLUtils.toTerm(id), LLUtils.toDocument(document));
|
indexWriter.updateDocument(LLUtils.toTerm(id), LLUtils.toDocument(document));
|
||||||
} finally {
|
} finally {
|
||||||
scheduledTasksLifecycle.endScheduledTask();
|
activeTasks.arriveAndDeregister();
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
@ -334,7 +291,7 @@ public class LLLocalLuceneIndex implements LLLuceneIndex {
|
|||||||
private Mono<Void> updateDocuments(Map<LLTerm, LLDocument> documentsMap) {
|
private Mono<Void> updateDocuments(Map<LLTerm, LLDocument> documentsMap) {
|
||||||
return Mono
|
return Mono
|
||||||
.fromCallable(() -> {
|
.fromCallable(() -> {
|
||||||
scheduledTasksLifecycle.startScheduledTask();
|
activeTasks.register();
|
||||||
try {
|
try {
|
||||||
for (Entry<LLTerm, LLDocument> entry : documentsMap.entrySet()) {
|
for (Entry<LLTerm, LLDocument> entry : documentsMap.entrySet()) {
|
||||||
LLTerm key = entry.getKey();
|
LLTerm key = entry.getKey();
|
||||||
@ -343,7 +300,7 @@ public class LLLocalLuceneIndex implements LLLuceneIndex {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
} finally {
|
} finally {
|
||||||
scheduledTasksLifecycle.endScheduledTask();
|
activeTasks.arriveAndDeregister();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -351,7 +308,7 @@ public class LLLocalLuceneIndex implements LLLuceneIndex {
|
|||||||
@Override
|
@Override
|
||||||
public Mono<Void> deleteAll() {
|
public Mono<Void> deleteAll() {
|
||||||
return Mono.<Void>fromCallable(() -> {
|
return Mono.<Void>fromCallable(() -> {
|
||||||
scheduledTasksLifecycle.startScheduledTask();
|
activeTasks.register();
|
||||||
try {
|
try {
|
||||||
//noinspection BlockingMethodInNonBlockingContext
|
//noinspection BlockingMethodInNonBlockingContext
|
||||||
indexWriter.deleteAll();
|
indexWriter.deleteAll();
|
||||||
@ -361,7 +318,7 @@ public class LLLocalLuceneIndex implements LLLuceneIndex {
|
|||||||
indexWriter.commit();
|
indexWriter.commit();
|
||||||
return null;
|
return null;
|
||||||
} finally {
|
} finally {
|
||||||
scheduledTasksLifecycle.endScheduledTask();
|
activeTasks.arriveAndDeregister();
|
||||||
}
|
}
|
||||||
}).subscribeOn(luceneHeavyTasksScheduler);
|
}).subscribeOn(luceneHeavyTasksScheduler);
|
||||||
}
|
}
|
||||||
@ -492,7 +449,7 @@ public class LLLocalLuceneIndex implements LLLuceneIndex {
|
|||||||
return Mono
|
return Mono
|
||||||
.<Void>fromCallable(() -> {
|
.<Void>fromCallable(() -> {
|
||||||
logger.debug("Closing IndexWriter...");
|
logger.debug("Closing IndexWriter...");
|
||||||
scheduledTasksLifecycle.cancelAndWait();
|
activeTasks.arriveAndAwaitAdvance();
|
||||||
return null;
|
return null;
|
||||||
})
|
})
|
||||||
.subscribeOn(luceneHeavyTasksScheduler)
|
.subscribeOn(luceneHeavyTasksScheduler)
|
||||||
@ -511,13 +468,13 @@ public class LLLocalLuceneIndex implements LLLuceneIndex {
|
|||||||
public Mono<Void> flush() {
|
public Mono<Void> flush() {
|
||||||
return Mono
|
return Mono
|
||||||
.<Void>fromCallable(() -> {
|
.<Void>fromCallable(() -> {
|
||||||
scheduledTasksLifecycle.startScheduledTask();
|
activeTasks.register();
|
||||||
try {
|
try {
|
||||||
if (scheduledTasksLifecycle.isCancelled()) return null;
|
if (activeTasks.isTerminated()) return null;
|
||||||
//noinspection BlockingMethodInNonBlockingContext
|
//noinspection BlockingMethodInNonBlockingContext
|
||||||
indexWriter.commit();
|
indexWriter.commit();
|
||||||
} finally {
|
} finally {
|
||||||
scheduledTasksLifecycle.endScheduledTask();
|
activeTasks.arriveAndDeregister();
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
})
|
})
|
||||||
@ -528,11 +485,11 @@ public class LLLocalLuceneIndex implements LLLuceneIndex {
|
|||||||
public Mono<Void> refresh(boolean force) {
|
public Mono<Void> refresh(boolean force) {
|
||||||
return Mono
|
return Mono
|
||||||
.<Void>fromCallable(() -> {
|
.<Void>fromCallable(() -> {
|
||||||
scheduledTasksLifecycle.startScheduledTask();
|
activeTasks.register();
|
||||||
try {
|
try {
|
||||||
if (scheduledTasksLifecycle.isCancelled()) return null;
|
if (activeTasks.isTerminated()) return null;
|
||||||
if (force) {
|
if (force) {
|
||||||
if (scheduledTasksLifecycle.isCancelled()) return null;
|
if (activeTasks.isTerminated()) return null;
|
||||||
//noinspection BlockingMethodInNonBlockingContext
|
//noinspection BlockingMethodInNonBlockingContext
|
||||||
searcherManager.maybeRefreshBlocking();
|
searcherManager.maybeRefreshBlocking();
|
||||||
} else {
|
} else {
|
||||||
@ -540,7 +497,7 @@ public class LLLocalLuceneIndex implements LLLuceneIndex {
|
|||||||
searcherManager.maybeRefresh();
|
searcherManager.maybeRefresh();
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
scheduledTasksLifecycle.endScheduledTask();
|
activeTasks.arriveAndDeregister();
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
})
|
})
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
package it.cavallium.dbengine.database.disk;
|
package it.cavallium.dbengine.database.disk;
|
||||||
|
|
||||||
import it.cavallium.dbengine.database.LLSnapshot;
|
import it.cavallium.dbengine.database.LLSnapshot;
|
||||||
import it.cavallium.dbengine.lucene.ScheduledTaskLifecycle;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.Phaser;
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
import org.apache.lucene.index.IndexCommit;
|
import org.apache.lucene.index.IndexCommit;
|
||||||
import org.apache.lucene.index.IndexWriter;
|
import org.apache.lucene.index.IndexWriter;
|
||||||
@ -17,7 +17,7 @@ public class SnapshotsManager {
|
|||||||
|
|
||||||
private final IndexWriter indexWriter;
|
private final IndexWriter indexWriter;
|
||||||
private final SnapshotDeletionPolicy snapshotter;
|
private final SnapshotDeletionPolicy snapshotter;
|
||||||
private final ScheduledTaskLifecycle scheduledTasksLifecycle;
|
private final Phaser activeTasks = new Phaser(1);
|
||||||
/**
|
/**
|
||||||
* Last snapshot sequence number. 0 is not used
|
* Last snapshot sequence number. 0 is not used
|
||||||
*/
|
*/
|
||||||
@ -28,11 +28,9 @@ public class SnapshotsManager {
|
|||||||
private final ConcurrentHashMap<Long, LuceneIndexSnapshot> snapshots = new ConcurrentHashMap<>();
|
private final ConcurrentHashMap<Long, LuceneIndexSnapshot> snapshots = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
public SnapshotsManager(IndexWriter indexWriter,
|
public SnapshotsManager(IndexWriter indexWriter,
|
||||||
SnapshotDeletionPolicy snapshotter,
|
SnapshotDeletionPolicy snapshotter) {
|
||||||
ScheduledTaskLifecycle scheduledTasksLifecycle) {
|
|
||||||
this.indexWriter = indexWriter;
|
this.indexWriter = indexWriter;
|
||||||
this.snapshotter = snapshotter;
|
this.snapshotter = snapshotter;
|
||||||
this.scheduledTasksLifecycle = scheduledTasksLifecycle;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public LuceneIndexSnapshot resolveSnapshot(@Nullable LLSnapshot snapshot) {
|
public LuceneIndexSnapshot resolveSnapshot(@Nullable LLSnapshot snapshot) {
|
||||||
@ -64,12 +62,12 @@ public class SnapshotsManager {
|
|||||||
.defer(() -> {
|
.defer(() -> {
|
||||||
if (ex instanceof IllegalStateException && "No index commit to snapshot".equals(ex.getMessage())) {
|
if (ex instanceof IllegalStateException && "No index commit to snapshot".equals(ex.getMessage())) {
|
||||||
return Mono.fromCallable(() -> {
|
return Mono.fromCallable(() -> {
|
||||||
scheduledTasksLifecycle.startScheduledTask();
|
activeTasks.register();
|
||||||
try {
|
try {
|
||||||
indexWriter.commit();
|
indexWriter.commit();
|
||||||
return snapshotter.snapshot();
|
return snapshotter.snapshot();
|
||||||
} finally {
|
} finally {
|
||||||
scheduledTasksLifecycle.endScheduledTask();
|
activeTasks.arriveAndDeregister();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@ -81,7 +79,7 @@ public class SnapshotsManager {
|
|||||||
|
|
||||||
public Mono<Void> releaseSnapshot(LLSnapshot snapshot) {
|
public Mono<Void> releaseSnapshot(LLSnapshot snapshot) {
|
||||||
return Mono.<Void>fromCallable(() -> {
|
return Mono.<Void>fromCallable(() -> {
|
||||||
scheduledTasksLifecycle.startScheduledTask();
|
activeTasks.register();
|
||||||
try {
|
try {
|
||||||
var indexSnapshot = this.snapshots.remove(snapshot.getSequenceNumber());
|
var indexSnapshot = this.snapshots.remove(snapshot.getSequenceNumber());
|
||||||
if (indexSnapshot == null) {
|
if (indexSnapshot == null) {
|
||||||
@ -96,8 +94,14 @@ public class SnapshotsManager {
|
|||||||
indexWriter.deleteUnusedFiles();
|
indexWriter.deleteUnusedFiles();
|
||||||
return null;
|
return null;
|
||||||
} finally {
|
} finally {
|
||||||
scheduledTasksLifecycle.endScheduledTask();
|
activeTasks.arriveAndDeregister();
|
||||||
}
|
}
|
||||||
}).subscribeOn(Schedulers.boundedElastic());
|
}).subscribeOn(Schedulers.boundedElastic());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
if (!activeTasks.isTerminated()) {
|
||||||
|
activeTasks.arriveAndAwaitAdvance();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,63 +0,0 @@
|
|||||||
package it.cavallium.dbengine.lucene;
|
|
||||||
|
|
||||||
import java.nio.channels.ClosedChannelException;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.concurrent.locks.StampedLock;
|
|
||||||
import org.warp.commonutils.concurrency.atomicity.Atomic;
|
|
||||||
import reactor.core.Disposable;
|
|
||||||
|
|
||||||
@Atomic
|
|
||||||
public class ScheduledTaskLifecycle {
|
|
||||||
|
|
||||||
private final StampedLock lock;
|
|
||||||
private volatile boolean cancelled = false;
|
|
||||||
|
|
||||||
public ScheduledTaskLifecycle() {
|
|
||||||
this.lock = new StampedLock();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mark this task as running.
|
|
||||||
* After calling this method, please call {@method endScheduledTask} inside a finally block!
|
|
||||||
*/
|
|
||||||
public void startScheduledTask() {
|
|
||||||
if (cancelled) {
|
|
||||||
throw new IllegalStateException("Already closed");
|
|
||||||
}
|
|
||||||
this.lock.readLock();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mark this task as running.
|
|
||||||
* After calling this method, please call {@method endScheduledTask} inside a finally block!
|
|
||||||
* @return false if failed
|
|
||||||
*/
|
|
||||||
public boolean tryStartScheduledTask() {
|
|
||||||
if (cancelled) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
this.lock.readLock();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mark this task as ended. Must be called after {@method startScheduledTask}
|
|
||||||
*/
|
|
||||||
public void endScheduledTask() {
|
|
||||||
this.lock.tryUnlockRead();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cancel all scheduled tasks and wait all running methods to finish
|
|
||||||
*/
|
|
||||||
public void cancelAndWait() {
|
|
||||||
cancelled = true;
|
|
||||||
|
|
||||||
// Acquire a write lock to wait all tasks to end
|
|
||||||
lock.unlockWrite(lock.writeLock());
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isCancelled() {
|
|
||||||
return cancelled;
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user