This is an automated email from the ASF dual-hosted git repository. rnewson pushed a commit to branch nouveau-purge in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit 6dc45453fe7e22193b456199ebb10f8b8595bfea Author: Robert Newson <rnew...@apache.org> AuthorDate: Thu May 18 16:28:30 2023 +0100 introduce purge sequence --- .../couchdb/nouveau/api/DocumentDeleteRequest.java | 12 +++++-- .../org/apache/couchdb/nouveau/api/IndexInfo.java | 12 ++++++- .../org/apache/couchdb/nouveau/core/Index.java | 37 ++++++++++++++++++---- .../couchdb/nouveau/lucene9/Lucene9Index.java | 10 +++--- .../couchdb/nouveau/resources/IndexResource.java | 9 +++--- .../couchdb/nouveau/lucene9/Lucene9IndexTest.java | 27 ++++++++++++++-- 6 files changed, 86 insertions(+), 21 deletions(-) diff --git a/nouveau/src/main/java/org/apache/couchdb/nouveau/api/DocumentDeleteRequest.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/api/DocumentDeleteRequest.java index 6f22effda..99f6f5d9f 100644 --- a/nouveau/src/main/java/org/apache/couchdb/nouveau/api/DocumentDeleteRequest.java +++ b/nouveau/src/main/java/org/apache/couchdb/nouveau/api/DocumentDeleteRequest.java @@ -24,15 +24,18 @@ public class DocumentDeleteRequest { @Positive private long seq; + private boolean purge; + public DocumentDeleteRequest() { // Jackson deserialization } - public DocumentDeleteRequest(long seq) { + public DocumentDeleteRequest(long seq, final boolean purge) { if (seq < 1) { throw new IllegalArgumentException("seq must be 1 or greater"); } this.seq = seq; + this.purge = purge; } @JsonProperty @@ -40,8 +43,13 @@ public class DocumentDeleteRequest { return seq; } + @JsonProperty + public boolean isPurge() { + return purge; + } + @Override public String toString() { - return "DocumentDeleteRequest [seq=" + seq + "]"; + return "DocumentDeleteRequest [seq=" + seq + ", purge=" + purge + "]"; } } diff --git a/nouveau/src/main/java/org/apache/couchdb/nouveau/api/IndexInfo.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/api/IndexInfo.java index e807c7c70..9958c7780 100644 --- a/nouveau/src/main/java/org/apache/couchdb/nouveau/api/IndexInfo.java +++ b/nouveau/src/main/java/org/apache/couchdb/nouveau/api/IndexInfo.java @@ -24,6 +24,9 @@ public final class IndexInfo { @PositiveOrZero private final long updateSeq; + @PositiveOrZero + private final long purgeSeq; + @PositiveOrZero private final int numDocs; @@ -32,9 +35,11 @@ public final class IndexInfo { public IndexInfo( @JsonProperty("update_seq") final long updateSeq, + @JsonProperty("purge_seq") final long purgeSeq, @JsonProperty("num_docs") final int numDocs, @JsonProperty("disk_size") final long diskSize) { this.updateSeq = updateSeq; + this.purgeSeq = purgeSeq; this.numDocs = numDocs; this.diskSize = diskSize; } @@ -51,8 +56,13 @@ public final class IndexInfo { return updateSeq; } + public long getPurgeSeq() { + return purgeSeq; + } + @Override public String toString() { - return "IndexInfo [updateSeq=" + updateSeq + ", numDocs=" + numDocs + ", diskSize=" + diskSize + "]"; + return "IndexInfo [updateSeq=" + updateSeq + ", purgeSeq=" + purgeSeq + ", numDocs=" + numDocs + ", diskSize=" + + diskSize + "]"; } } diff --git a/nouveau/src/main/java/org/apache/couchdb/nouveau/core/Index.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/core/Index.java index 5b31938e9..6cdd5ef31 100644 --- a/nouveau/src/main/java/org/apache/couchdb/nouveau/core/Index.java +++ b/nouveau/src/main/java/org/apache/couchdb/nouveau/core/Index.java @@ -36,13 +36,15 @@ import org.apache.couchdb.nouveau.api.SearchResults; public abstract class Index implements Closeable { private long updateSeq; + private long purgeSeq; private boolean deleteOnClose = false; private long lastCommit = now(); private volatile boolean closed; private final Semaphore permits = new Semaphore(Integer.MAX_VALUE); - protected Index(final long updateSeq) { + protected Index(final long updateSeq, final long purgeSeq) { this.updateSeq = updateSeq; + this.purgeSeq = purgeSeq; } public final boolean tryAcquire() { @@ -74,7 +76,7 @@ public abstract class Index implements Closeable { public final IndexInfo info() throws IOException { final int numDocs = doNumDocs(); final long diskSize = doDiskSize(); - return new IndexInfo(updateSeq, numDocs, diskSize); + return new IndexInfo(updateSeq, purgeSeq, numDocs, diskSize); } protected abstract int doNumDocs() throws IOException; @@ -90,9 +92,15 @@ public abstract class Index implements Closeable { protected abstract void doUpdate(final String docId, final DocumentUpdateRequest request) throws IOException; public final synchronized void delete(final String docId, final DocumentDeleteRequest request) throws IOException { - assertUpdateSeqIsLower(request.getSeq()); - doDelete(docId, request); - incrementUpdateSeq(request.getSeq()); + if (request.isPurge()) { + assertPurgeSeqIsLower(request.getSeq()); + doDelete(docId, request); + incrementPurgeSeq(request.getSeq()); + } else { + assertUpdateSeqIsLower(request.getSeq()); + doDelete(docId, request); + incrementUpdateSeq(request.getSeq()); + } } protected abstract void doDelete(final String docId, final DocumentDeleteRequest request) throws IOException; @@ -105,10 +113,12 @@ public abstract class Index implements Closeable { public final boolean commit() throws IOException { final long updateSeq; + final long purgeSeq; synchronized (this) { updateSeq = this.updateSeq; + purgeSeq = this.purgeSeq; } - final boolean result = doCommit(updateSeq); + final boolean result = doCommit(updateSeq, purgeSeq); if (result) { final long now = now(); synchronized (this) { @@ -118,7 +128,7 @@ public abstract class Index implements Closeable { return result; } - protected abstract boolean doCommit(final long updateSeq) throws IOException; + protected abstract boolean doCommit(final long updateSeq, final long purgeSeq) throws IOException; @Override public final void close() throws IOException { @@ -159,6 +169,19 @@ public abstract class Index implements Closeable { this.updateSeq = updateSeq; } + protected final void assertPurgeSeqIsLower(final long purgeSeq) throws UpdatesOutOfOrderException { + assert Thread.holdsLock(this); + if (!(purgeSeq > this.purgeSeq)) { + throw new UpdatesOutOfOrderException(this.purgeSeq, purgeSeq); + } + } + + protected final void incrementPurgeSeq(final long purgeSeq) throws IOException { + assert Thread.holdsLock(this); + assertPurgeSeqIsLower(purgeSeq); + this.purgeSeq = purgeSeq; + } + public boolean needsCommit(final long duration, final TimeUnit unit) { final long commitNeededSince = now() - unit.toNanos(duration); synchronized (this) { diff --git a/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene9/Lucene9Index.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene9/Lucene9Index.java index 69e6b1264..ce7f7d7ec 100644 --- a/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene9/Lucene9Index.java +++ b/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene9/Lucene9Index.java @@ -25,7 +25,6 @@ import java.nio.charset.CharsetDecoder; import java.nio.file.NoSuchFileException; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -104,8 +103,9 @@ public class Lucene9Index extends Index { final Analyzer analyzer, final IndexWriter writer, final long updateSeq, + final long purgeSeq, final SearcherManager searcherManager) { - super(updateSeq); + super(updateSeq, purgeSeq); this.analyzer = Objects.requireNonNull(analyzer); this.writer = Objects.requireNonNull(writer); this.searcherManager = Objects.requireNonNull(searcherManager); @@ -144,12 +144,12 @@ public class Lucene9Index extends Index { } @Override - public boolean doCommit(final long updateSeq) throws IOException { + public boolean doCommit(final long updateSeq, final long purgeSeq) throws IOException { if (!writer.hasUncommittedChanges()) { return false; } - writer.setLiveCommitData( - Collections.singletonMap("update_seq", Long.toString(updateSeq)).entrySet()); + writer.setLiveCommitData(Map.of("update_seq", Long.toString(updateSeq), "purge_seq", Long.toString(purgeSeq)) + .entrySet()); writer.commit(); return true; } diff --git a/nouveau/src/main/java/org/apache/couchdb/nouveau/resources/IndexResource.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/resources/IndexResource.java index 0776df52f..e12ab55fc 100644 --- a/nouveau/src/main/java/org/apache/couchdb/nouveau/resources/IndexResource.java +++ b/nouveau/src/main/java/org/apache/couchdb/nouveau/resources/IndexResource.java @@ -126,19 +126,20 @@ public final class IndexResource { final IndexWriterConfig config = new IndexWriterConfig(analyzer); config.setUseCompoundFile(false); final IndexWriter writer = new IndexWriter(dir, config); - final long updateSeq = getUpdateSeq(writer); + final long updateSeq = getSeq(writer, "update_seq"); + final long purgeSeq = getSeq(writer, "purge_seq"); final SearcherManager searcherManager = new SearcherManager(writer, searcherFactory); - return new Lucene9Index(analyzer, writer, updateSeq, searcherManager); + return new Lucene9Index(analyzer, writer, updateSeq, purgeSeq, searcherManager); }; } - private static long getUpdateSeq(final IndexWriter writer) throws IOException { + private static long getSeq(final IndexWriter writer, final String key) throws IOException { final Iterable<Map.Entry<String, String>> commitData = writer.getLiveCommitData(); if (commitData == null) { return 0L; } for (Map.Entry<String, String> entry : commitData) { - if (entry.getKey().equals("update_seq")) { + if (entry.getKey().equals(key)) { return Long.parseLong(entry.getValue()); } } diff --git a/nouveau/src/test/java/org/apache/couchdb/nouveau/lucene9/Lucene9IndexTest.java b/nouveau/src/test/java/org/apache/couchdb/nouveau/lucene9/Lucene9IndexTest.java index 79151a263..5d989804b 100644 --- a/nouveau/src/test/java/org/apache/couchdb/nouveau/lucene9/Lucene9IndexTest.java +++ b/nouveau/src/test/java/org/apache/couchdb/nouveau/lucene9/Lucene9IndexTest.java @@ -199,11 +199,34 @@ public class Lucene9IndexTest { IndexInfo info = index.info(); assertThat(info.getNumDocs()).isEqualTo(1); - index.delete("foo", new DocumentDeleteRequest(3)); + index.delete("foo", new DocumentDeleteRequest(3, false)); index.commit(); info = index.info(); assertThat(info.getNumDocs()).isEqualTo(0); + assertThat(info.getUpdateSeq()).isEqualTo(3); + } finally { + cleanup(index); + } + } + + @Test + public void testPurge(@TempDir Path path) throws IOException { + Index index = setup(path); + try { + final Collection<Field> fields = List.of(new DoubleField("bar", 12.0, false)); + index.update("foo", new DocumentUpdateRequest(2, null, fields)); + index.commit(); + + IndexInfo info = index.info(); + assertThat(info.getNumDocs()).isEqualTo(1); + + index.delete("foo", new DocumentDeleteRequest(3, true)); + index.commit(); + + info = index.info(); + assertThat(info.getNumDocs()).isEqualTo(0); + assertThat(info.getPurgeSeq()).isEqualTo(3); } finally { cleanup(index); } @@ -217,7 +240,7 @@ public class Lucene9IndexTest { config.setUseCompoundFile(false); final IndexWriter writer = new IndexWriter(dir, config); final SearcherManager searcherManager = new SearcherManager(writer, null); - return new Lucene9Index(analyzer, writer, 0L, searcherManager); + return new Lucene9Index(analyzer, writer, 0L, 0L, searcherManager); }; } }