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

110 lines
3.5 KiB
Java
Raw Normal View History

2021-09-06 15:06:51 +02:00
package it.cavallium.dbengine.database.disk;
import static it.cavallium.dbengine.client.UninterruptibleScheduler.uninterruptibleScheduler;
2021-09-06 15:06:51 +02:00
import it.cavallium.dbengine.database.LLSnapshot;
import java.io.IOException;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
2021-09-25 13:06:24 +02:00
import java.util.concurrent.Executor;
2021-09-06 18:52:21 +02:00
import java.util.concurrent.Phaser;
2021-09-06 15:06:51 +02:00
import java.util.concurrent.atomic.AtomicLong;
import org.apache.lucene.index.IndexCommit;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.SnapshotDeletionPolicy;
import org.jetbrains.annotations.Nullable;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
public class SnapshotsManager {
private final IndexWriter indexWriter;
private final SnapshotDeletionPolicy snapshotter;
2021-09-06 18:52:21 +02:00
private final Phaser activeTasks = new Phaser(1);
2021-09-06 15:06:51 +02:00
/**
* Last snapshot sequence number. 0 is not used
*/
private final AtomicLong lastSnapshotSeqNo = new AtomicLong(0);
/**
* LLSnapshot seq no to index commit point
*/
private final ConcurrentHashMap<Long, LuceneIndexSnapshot> snapshots = new ConcurrentHashMap<>();
public SnapshotsManager(IndexWriter indexWriter,
2021-09-06 18:52:21 +02:00
SnapshotDeletionPolicy snapshotter) {
2021-09-06 15:06:51 +02:00
this.indexWriter = indexWriter;
this.snapshotter = snapshotter;
}
public LuceneIndexSnapshot resolveSnapshot(@Nullable LLSnapshot snapshot) {
if (snapshot == null) {
return null;
}
return Objects.requireNonNull(snapshots.get(snapshot.getSequenceNumber()),
() -> "Can't resolve snapshot " + snapshot.getSequenceNumber()
);
}
public Mono<LLSnapshot> takeSnapshot() {
return takeLuceneSnapshot().map(snapshot -> {
var snapshotSeqNo = lastSnapshotSeqNo.incrementAndGet();
this.snapshots.put(snapshotSeqNo, new LuceneIndexSnapshot(snapshot));
return new LLSnapshot(snapshotSeqNo);
});
}
/**
* Use internally. This method commits before taking the snapshot if there are no commits in a new database,
* avoiding the exception.
*/
private Mono<IndexCommit> takeLuceneSnapshot() {
return Mono
.fromCallable(snapshotter::snapshot)
.subscribeOn(uninterruptibleScheduler(Schedulers.boundedElastic()))
2021-12-15 16:47:59 +01:00
.onErrorResume(ex -> {
if (ex instanceof IllegalStateException && "No index commit to snapshot".equals(ex.getMessage())) {
return Mono.fromCallable(() -> {
2021-12-15 16:47:59 +01:00
activeTasks.register();
try {
indexWriter.commit();
return snapshotter.snapshot();
2021-12-15 16:47:59 +01:00
} finally {
activeTasks.arriveAndDeregister();
2021-09-06 15:06:51 +02:00
}
}).subscribeOn(uninterruptibleScheduler(Schedulers.boundedElastic()));
2021-12-15 16:47:59 +01:00
} else {
return Mono.error(ex);
}
})
.publishOn(Schedulers.parallel());
2021-09-06 15:06:51 +02:00
}
public Mono<Void> releaseSnapshot(LLSnapshot snapshot) {
return Mono.<Void>fromCallable(() -> {
2021-09-06 18:52:21 +02:00
activeTasks.register();
2021-09-06 15:06:51 +02:00
try {
var indexSnapshot = this.snapshots.remove(snapshot.getSequenceNumber());
if (indexSnapshot == null) {
throw new IOException("LLSnapshot " + snapshot.getSequenceNumber() + " not found!");
}
indexSnapshot.close();
var luceneIndexSnapshot = indexSnapshot.getSnapshot();
snapshotter.release(luceneIndexSnapshot);
// Delete unused files after releasing the snapshot
indexWriter.deleteUnusedFiles();
return null;
2021-09-06 15:06:51 +02:00
} finally {
2021-09-06 18:52:21 +02:00
activeTasks.arriveAndDeregister();
2021-09-06 15:06:51 +02:00
}
}).subscribeOn(uninterruptibleScheduler(Schedulers.boundedElastic())).publishOn(Schedulers.parallel());
2021-09-06 15:06:51 +02:00
}
2021-09-06 18:52:21 +02:00
public void close() {
if (!activeTasks.isTerminated()) {
activeTasks.arriveAndAwaitAdvance();
}
}
2021-09-06 15:06:51 +02:00
}