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

254 lines
8.0 KiB
Java
Raw Normal View History

2021-09-06 15:06:51 +02:00
package it.cavallium.dbengine.database.disk;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import it.cavallium.dbengine.database.LLSnapshot;
import it.cavallium.dbengine.lucene.LuceneCloseable;
2022-06-29 01:14:05 +02:00
import it.cavallium.dbengine.utils.SimpleResource;
2021-09-06 15:06:51 +02:00
import java.io.IOException;
import it.cavallium.dbengine.utils.DBException;
2021-09-06 15:06:51 +02:00
import java.time.Duration;
2022-02-25 15:46:32 +01:00
import java.util.concurrent.ExecutorService;
2021-11-09 02:14:21 +01:00
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
2021-09-07 02:36:11 +02:00
import java.util.concurrent.TimeUnit;
2022-02-25 15:46:32 +01:00
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.LockSupport;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
2021-09-06 15:06:51 +02:00
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.SearcherFactory;
import org.apache.lucene.search.SearcherManager;
import org.apache.lucene.search.similarities.Similarity;
2021-09-06 17:35:02 +02:00
import org.apache.lucene.store.AlreadyClosedException;
2021-09-06 15:06:51 +02:00
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
2022-06-29 01:14:05 +02:00
// todo: deduplicate code between Cached and Simple searcher managers
public class CachedIndexSearcherManager extends SimpleResource implements IndexSearcherManager, LuceneCloseable {
2021-09-06 15:06:51 +02:00
2022-06-29 01:14:05 +02:00
private static final Logger LOG = LogManager.getLogger(SimpleIndexSearcherManager.class);
2022-06-16 18:40:17 +02:00
private static final ExecutorService SEARCH_EXECUTOR = Executors.newFixedThreadPool(
2022-02-25 15:46:32 +01:00
Runtime.getRuntime().availableProcessors(),
new LuceneThreadFactory("lucene-search")
2022-02-25 15:46:32 +01:00
.setDaemon(true).withGroup(new ThreadGroup("lucene-search"))
);
2022-06-16 18:40:17 +02:00
private static final SearcherFactory SEARCHER_FACTORY = new ExecutorSearcherFactory(SEARCH_EXECUTOR);
2021-09-06 18:52:21 +02:00
2022-06-29 01:14:05 +02:00
@Nullable
2021-09-06 15:06:51 +02:00
private final SnapshotsManager snapshotsManager;
private final ScheduledExecutorService luceneHeavyTasksScheduler;
2021-09-06 15:06:51 +02:00
private final Similarity similarity;
private final SearcherManager searcherManager;
private final Duration queryRefreshDebounceTime;
2022-02-25 15:46:32 +01:00
private final AtomicLong activeSearchers = new AtomicLong(0);
private final AtomicLong activeRefreshes = new AtomicLong(0);
2021-09-06 15:06:51 +02:00
private final LoadingCache<LLSnapshot, LLIndexSearcher> cachedSnapshotSearchers;
private final ScheduledFuture<?> refreshSubscription;
2021-09-06 15:06:51 +02:00
2021-09-06 18:24:36 +02:00
public CachedIndexSearcherManager(IndexWriter indexWriter,
2022-06-29 01:14:05 +02:00
@Nullable SnapshotsManager snapshotsManager,
ScheduledExecutorService luceneHeavyTasksScheduler,
2021-09-06 15:06:51 +02:00
Similarity similarity,
boolean applyAllDeletes,
boolean writeAllDeletes,
Duration queryRefreshDebounceTime) {
2021-09-06 15:06:51 +02:00
this.snapshotsManager = snapshotsManager;
2022-04-04 20:12:29 +02:00
this.luceneHeavyTasksScheduler = luceneHeavyTasksScheduler;
2021-09-06 15:06:51 +02:00
this.similarity = similarity;
this.queryRefreshDebounceTime = queryRefreshDebounceTime;
2023-02-22 16:59:35 +01:00
try {
this.searcherManager = new SearcherManager(indexWriter, applyAllDeletes, writeAllDeletes, SEARCHER_FACTORY);
} catch (IOException e) {
throw new DBException(e);
}
2021-09-06 15:06:51 +02:00
refreshSubscription = luceneHeavyTasksScheduler.scheduleAtFixedRate(() -> {
try {
maybeRefresh();
} catch (Exception ex) {
LOG.error("Failed to refresh the searcher manager", ex);
}
},
queryRefreshDebounceTime.toMillis(),
queryRefreshDebounceTime.toMillis(),
TimeUnit.MILLISECONDS
);
2021-09-06 15:06:51 +02:00
this.cachedSnapshotSearchers = CacheBuilder.newBuilder()
.expireAfterWrite(queryRefreshDebounceTime)
2021-09-06 15:08:07 +02:00
// Max 3 cached non-main index writers
.maximumSize(3)
2021-09-06 15:06:51 +02:00
.build(new CacheLoader<>() {
@Override
public LLIndexSearcher load(@NotNull LLSnapshot snapshot) {
2021-09-06 18:24:36 +02:00
return CachedIndexSearcherManager.this.generateCachedSearcher(snapshot);
2021-09-06 15:06:51 +02:00
}
});
}
private LLIndexSearcher generateCachedSearcher(@Nullable LLSnapshot snapshot) {
if (isClosed()) {
return null;
}
activeSearchers.incrementAndGet();
try {
IndexSearcher indexSearcher;
boolean fromSnapshot;
if (snapshotsManager == null || snapshot == null) {
2023-02-22 16:59:35 +01:00
try {
indexSearcher = searcherManager.acquire();
} catch (IOException ex) {
throw new DBException(ex);
}
fromSnapshot = false;
} else {
indexSearcher = snapshotsManager.resolveSnapshot(snapshot).getIndexSearcher(SEARCH_EXECUTOR);
fromSnapshot = true;
}
indexSearcher.setSimilarity(similarity);
assert indexSearcher.getIndexReader().getRefCount() > 0;
LLIndexSearcher llIndexSearcher;
if (fromSnapshot) {
llIndexSearcher = new SnapshotIndexSearcher(indexSearcher);
} else {
llIndexSearcher = new MainIndexSearcher(indexSearcher, searcherManager);
}
return llIndexSearcher;
} catch (Throwable ex) {
activeSearchers.decrementAndGet();
throw ex;
}
2021-09-18 18:34:21 +02:00
}
2021-10-01 19:17:33 +02:00
private void dropCachedIndexSearcher() {
2021-09-18 18:34:21 +02:00
// This shouldn't happen more than once per searcher.
2022-02-25 15:46:32 +01:00
activeSearchers.decrementAndGet();
2021-09-06 15:06:51 +02:00
}
2021-09-18 18:34:21 +02:00
@Override
public void maybeRefreshBlocking() {
2021-09-06 17:35:02 +02:00
try {
2022-02-25 15:46:32 +01:00
activeRefreshes.incrementAndGet();
2021-09-06 17:35:02 +02:00
searcherManager.maybeRefreshBlocking();
} catch (AlreadyClosedException ignored) {
2023-02-22 16:59:35 +01:00
} catch (IOException e) {
throw new DBException(e);
2021-09-06 18:52:21 +02:00
} finally {
2022-02-25 15:46:32 +01:00
activeRefreshes.decrementAndGet();
2021-09-06 17:35:02 +02:00
}
2021-09-06 15:06:51 +02:00
}
2021-09-18 18:34:21 +02:00
@Override
public void maybeRefresh() {
2021-09-06 17:35:02 +02:00
try {
2022-02-25 15:46:32 +01:00
activeRefreshes.incrementAndGet();
2021-09-06 17:35:02 +02:00
searcherManager.maybeRefresh();
} catch (AlreadyClosedException ignored) {
2023-02-22 16:59:35 +01:00
} catch (IOException e) {
throw new DBException(e);
2021-09-06 18:52:21 +02:00
} finally {
2022-02-25 15:46:32 +01:00
activeRefreshes.decrementAndGet();
2021-09-06 17:35:02 +02:00
}
2021-09-06 15:06:51 +02:00
}
2021-09-18 18:34:21 +02:00
@Override
public LLIndexSearcher retrieveSearcher(@Nullable LLSnapshot snapshot) {
2021-09-06 15:06:51 +02:00
if (snapshot == null) {
return this.generateCachedSearcher(null);
2021-09-06 15:06:51 +02:00
} else {
return this.cachedSnapshotSearchers.getUnchecked(snapshot);
}
}
2021-09-18 18:34:21 +02:00
@Override
2022-06-30 13:54:55 +02:00
protected void onClose() {
LOG.debug("Closing IndexSearcherManager...");
long initTime = System.nanoTime();
refreshSubscription.cancel(false);
while (!refreshSubscription.isDone() && (System.nanoTime() - initTime) <= 240000000000L) {
LockSupport.parkNanos(50000000);
}
refreshSubscription.cancel(true);
2022-06-30 13:54:55 +02:00
LOG.debug("Closed IndexSearcherManager");
LOG.debug("Closing refreshes...");
initTime = System.nanoTime();
2022-06-30 13:54:55 +02:00
while (activeRefreshes.get() > 0 && (System.nanoTime() - initTime) <= 15000000000L) {
LockSupport.parkNanos(50000000);
}
LOG.debug("Closed refreshes...");
LOG.debug("Closing active searchers...");
initTime = System.nanoTime();
while (activeSearchers.get() > 0 && (System.nanoTime() - initTime) <= 15000000000L) {
LockSupport.parkNanos(50000000);
}
LOG.debug("Closed active searchers");
LOG.debug("Stopping searcher executor...");
cachedSnapshotSearchers.invalidateAll();
cachedSnapshotSearchers.cleanUp();
SEARCH_EXECUTOR.shutdown();
try {
if (!SEARCH_EXECUTOR.awaitTermination(15, TimeUnit.SECONDS)) {
SEARCH_EXECUTOR.shutdownNow();
}
} catch (InterruptedException e) {
LOG.error("Failed to stop executor", e);
}
LOG.debug("Stopped searcher executor");
2021-09-06 15:06:51 +02:00
}
2021-09-25 13:06:24 +02:00
2022-06-13 23:25:43 +02:00
public long getActiveSearchers() {
return activeSearchers.get();
}
public long getActiveRefreshes() {
return activeRefreshes.get();
}
2022-06-14 13:10:38 +02:00
private class MainIndexSearcher extends LLIndexSearcherImpl implements LuceneCloseable {
2022-06-14 13:10:38 +02:00
2022-06-30 17:05:32 +02:00
public MainIndexSearcher(IndexSearcher indexSearcher, SearcherManager searcherManager) {
super(indexSearcher, () -> releaseOnCleanup(searcherManager, indexSearcher));
}
private static void releaseOnCleanup(SearcherManager searcherManager, IndexSearcher indexSearcher) {
try {
LOG.warn("An index searcher was not closed!");
searcherManager.release(indexSearcher);
} catch (IOException ex) {
LOG.error("Failed to release the index searcher during cleanup: {}", indexSearcher, ex);
}
2022-06-14 13:10:38 +02:00
}
@Override
2022-06-30 13:54:55 +02:00
public void onClose() {
2022-06-14 13:10:38 +02:00
dropCachedIndexSearcher();
2022-06-30 13:54:55 +02:00
try {
2022-06-14 13:10:38 +02:00
searcherManager.release(indexSearcher);
2022-06-30 13:54:55 +02:00
} catch (IOException ex) {
throw new DBException(ex);
2022-06-14 13:10:38 +02:00
}
}
}
private class SnapshotIndexSearcher extends LLIndexSearcherImpl {
2022-06-14 13:10:38 +02:00
2022-06-30 17:05:32 +02:00
public SnapshotIndexSearcher(IndexSearcher indexSearcher) {
super(indexSearcher);
}
2022-06-14 13:10:38 +02:00
@Override
2022-06-30 13:54:55 +02:00
public void onClose() {
2022-06-14 13:10:38 +02:00
dropCachedIndexSearcher();
}
}
2021-09-06 15:06:51 +02:00
}