2021-09-06 15:06:51 +02:00
|
|
|
package it.cavallium.dbengine.database.disk;
|
|
|
|
|
2021-12-16 16:14:44 +01:00
|
|
|
import static it.cavallium.dbengine.client.UninterruptibleScheduler.uninterruptibleScheduler;
|
|
|
|
|
2021-09-06 15:06:51 +02:00
|
|
|
import it.cavallium.dbengine.database.LLSnapshot;
|
2022-06-30 13:54:55 +02:00
|
|
|
import it.cavallium.dbengine.utils.SimpleResource;
|
2021-09-06 15:06:51 +02:00
|
|
|
import java.io.IOException;
|
2022-06-30 13:54:55 +02:00
|
|
|
import java.io.UncheckedIOException;
|
2021-09-06 15:06:51 +02:00
|
|
|
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;
|
|
|
|
|
2022-06-30 13:54:55 +02:00
|
|
|
public class SnapshotsManager extends SimpleResource {
|
2021-09-06 15:06:51 +02:00
|
|
|
|
|
|
|
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() {
|
2022-06-28 13:52:21 +02:00
|
|
|
return Mono
|
|
|
|
.fromCallable(this::takeLuceneSnapshot)
|
|
|
|
.subscribeOn(uninterruptibleScheduler(Schedulers.boundedElastic()))
|
|
|
|
.publishOn(Schedulers.parallel());
|
2021-09-06 15:06:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Use internally. This method commits before taking the snapshot if there are no commits in a new database,
|
|
|
|
* avoiding the exception.
|
|
|
|
*/
|
2022-06-28 13:52:21 +02:00
|
|
|
private LLSnapshot takeLuceneSnapshot() throws IOException {
|
|
|
|
activeTasks.register();
|
|
|
|
try {
|
|
|
|
if (snapshotter.getSnapshots().isEmpty()) {
|
|
|
|
indexWriter.commit();
|
|
|
|
}
|
|
|
|
var snapshotSeqNo = lastSnapshotSeqNo.incrementAndGet();
|
|
|
|
IndexCommit snapshot = snapshotter.snapshot();
|
|
|
|
var prevSnapshot = this.snapshots.put(snapshotSeqNo, new LuceneIndexSnapshot(snapshot));
|
|
|
|
|
|
|
|
// Unexpectedly found a snapshot
|
|
|
|
if (prevSnapshot != null) {
|
|
|
|
try {
|
|
|
|
prevSnapshot.close();
|
2022-06-30 13:54:55 +02:00
|
|
|
} catch (UncheckedIOException e) {
|
2022-06-28 13:52:21 +02:00
|
|
|
throw new IllegalStateException("Can't close snapshot", e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return new LLSnapshot(snapshotSeqNo);
|
|
|
|
} finally {
|
|
|
|
activeTasks.arriveAndDeregister();
|
|
|
|
}
|
2021-09-06 15:06:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public Mono<Void> releaseSnapshot(LLSnapshot snapshot) {
|
2021-12-16 16:14:44 +01:00
|
|
|
return Mono.<Void>fromCallable(() -> {
|
2021-09-06 18:52:21 +02:00
|
|
|
activeTasks.register();
|
2022-06-28 13:52:21 +02:00
|
|
|
try (var indexSnapshot = this.snapshots.remove(snapshot.getSequenceNumber())) {
|
2021-09-06 15:06:51 +02:00
|
|
|
if (indexSnapshot == null) {
|
|
|
|
throw new IOException("LLSnapshot " + snapshot.getSequenceNumber() + " not found!");
|
|
|
|
}
|
|
|
|
|
|
|
|
var luceneIndexSnapshot = indexSnapshot.getSnapshot();
|
|
|
|
snapshotter.release(luceneIndexSnapshot);
|
2021-12-16 16:14:44 +01:00
|
|
|
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
|
|
|
}
|
2021-12-16 16:14:44 +01:00
|
|
|
}).subscribeOn(uninterruptibleScheduler(Schedulers.boundedElastic())).publishOn(Schedulers.parallel());
|
2021-09-06 15:06:51 +02:00
|
|
|
}
|
2021-09-06 18:52:21 +02:00
|
|
|
|
2022-06-21 14:35:07 +02:00
|
|
|
/**
|
|
|
|
* Returns the total number of snapshots currently held.
|
|
|
|
*/
|
|
|
|
public int getSnapshotsCount() {
|
2022-06-28 13:52:21 +02:00
|
|
|
return Math.max(snapshots.size(), snapshotter.getSnapshotCount());
|
2022-06-21 14:35:07 +02:00
|
|
|
}
|
|
|
|
|
2022-06-30 13:54:55 +02:00
|
|
|
@Override
|
|
|
|
protected void onClose() {
|
2021-09-06 18:52:21 +02:00
|
|
|
if (!activeTasks.isTerminated()) {
|
|
|
|
activeTasks.arriveAndAwaitAdvance();
|
|
|
|
}
|
|
|
|
}
|
2021-09-06 15:06:51 +02:00
|
|
|
}
|