You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by jp...@apache.org on 2022/03/02 08:19:28 UTC

[lucene] branch main updated: LUCENE-10237 : Add MergeOnCommitTieredMergePolicy to sandbox (#446)

This is an automated email from the ASF dual-hosted git repository.

jpountz pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/lucene.git


The following commit(s) were added to refs/heads/main by this push:
     new 14726de  LUCENE-10237 : Add MergeOnCommitTieredMergePolicy to sandbox (#446)
14726de is described below

commit 14726dec5168ccaf14065e14e7d7f3ee22f186f0
Author: Anand <an...@gmail.com>
AuthorDate: Wed Mar 2 13:49:25 2022 +0530

    LUCENE-10237 : Add MergeOnCommitTieredMergePolicy to sandbox (#446)
---
 lucene/CHANGES.txt                                 |   2 +
 lucene/sandbox/src/java/module-info.java           |   1 +
 .../sandbox/index/MergeOnFlushMergePolicy.java     |  85 +++++++++++++++
 .../apache/lucene/sandbox/index/package-info.java} |  16 +--
 .../sandbox/index/TestMergeOnFlushMergePolicy.java | 115 +++++++++++++++++++++
 5 files changed, 205 insertions(+), 14 deletions(-)

diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index cc41cbb..f25a8b4 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -41,6 +41,8 @@ Bug Fixes
 
 Other
 ---------------------
+* LUCENE-10237: Add MergeOnFlushMergePolicy to sandbox.
+  (Michael Froh, Anand Kotriwal)
 
 * LUCENE-10283: The minimum required Java version was bumped from 11 to 17.
   (Adrien Grand, Uwe Schindler, Dawid Weiss, Robert Muir)
diff --git a/lucene/sandbox/src/java/module-info.java b/lucene/sandbox/src/java/module-info.java
index 6d19544..c51a256 100644
--- a/lucene/sandbox/src/java/module-info.java
+++ b/lucene/sandbox/src/java/module-info.java
@@ -25,6 +25,7 @@ module org.apache.lucene.sandbox {
   exports org.apache.lucene.sandbox.document;
   exports org.apache.lucene.sandbox.queries;
   exports org.apache.lucene.sandbox.search;
+  exports org.apache.lucene.sandbox.index;
 
   provides org.apache.lucene.codecs.PostingsFormat with
       org.apache.lucene.sandbox.codecs.idversion.IDVersionPostingsFormat;
diff --git a/lucene/sandbox/src/java/org/apache/lucene/sandbox/index/MergeOnFlushMergePolicy.java b/lucene/sandbox/src/java/org/apache/lucene/sandbox/index/MergeOnFlushMergePolicy.java
new file mode 100644
index 0000000..53e4d99
--- /dev/null
+++ b/lucene/sandbox/src/java/org/apache/lucene/sandbox/index/MergeOnFlushMergePolicy.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.lucene.sandbox.index;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.lucene.index.*;
+
+/**
+ * A simple extension to wrap {@link MergePolicy} to merge all tiny segments (or at least segments
+ * smaller than specified in {@link #setSmallSegmentThresholdMB(double)} into one segment on commit.
+ */
+public class MergeOnFlushMergePolicy extends FilterMergePolicy {
+  private long smallSegmentThresholdBytes = Units.mbToBytes(100.0);
+
+  /**
+   * Creates a MergeOnFlushMergePolicy merge policy instance wrapping another.
+   *
+   * @param mergePolicy the wrapped {@link MergePolicy}
+   */
+  public MergeOnFlushMergePolicy(MergePolicy mergePolicy) {
+    super(mergePolicy);
+  }
+
+  public double getSmallSegmentThresholdMB() {
+    return Units.bytesToMB(smallSegmentThresholdBytes);
+  }
+
+  /**
+   * @param smallSegmentThresholdMB all segments smaller than this will be merged into a single
+   *     segment before commit completes.
+   */
+  public void setSmallSegmentThresholdMB(double smallSegmentThresholdMB) {
+    this.smallSegmentThresholdBytes = Units.mbToBytes(smallSegmentThresholdMB);
+  }
+
+  @Override
+  public MergeSpecification findFullFlushMerges(
+      MergeTrigger mergeTrigger, SegmentInfos segmentInfos, MergeContext mergeContext)
+      throws IOException {
+    List<SegmentCommitInfo> smallSegments = new ArrayList<>();
+    for (SegmentCommitInfo sci : segmentInfos) {
+      if (sci.sizeInBytes() < smallSegmentThresholdBytes) {
+        if (mergeContext.getMergingSegments().contains(sci) == false) {
+          smallSegments.add(sci);
+        }
+      }
+    }
+    if (smallSegments.size() > 1) {
+      MergeSpecification mergeSpecification = new MergeSpecification();
+      mergeSpecification.add(new OneMerge(smallSegments));
+      return mergeSpecification;
+    }
+    return null;
+  }
+  /** Utility class to handle conversion between megabytes and bytes */
+  static class Units {
+
+    private Units() {}
+
+    public static double bytesToMB(long bytes) {
+      return bytes / 1024. / 1024.;
+    }
+
+    public static long mbToBytes(double megabytes) {
+      return (long) (megabytes * 1024 * 1024);
+    }
+  }
+}
diff --git a/lucene/sandbox/src/java/module-info.java b/lucene/sandbox/src/java/org/apache/lucene/sandbox/index/package-info.java
similarity index 60%
copy from lucene/sandbox/src/java/module-info.java
copy to lucene/sandbox/src/java/org/apache/lucene/sandbox/index/package-info.java
index 6d19544..186ae0b 100644
--- a/lucene/sandbox/src/java/module-info.java
+++ b/lucene/sandbox/src/java/org/apache/lucene/sandbox/index/package-info.java
@@ -15,17 +15,5 @@
  * limitations under the License.
  */
 
-/** Various third party contributions and new ideas */
-module org.apache.lucene.sandbox {
-  requires org.apache.lucene.core;
-  requires org.apache.lucene.queries;
-
-  exports org.apache.lucene.payloads;
-  exports org.apache.lucene.sandbox.codecs.idversion;
-  exports org.apache.lucene.sandbox.document;
-  exports org.apache.lucene.sandbox.queries;
-  exports org.apache.lucene.sandbox.search;
-
-  provides org.apache.lucene.codecs.PostingsFormat with
-      org.apache.lucene.sandbox.codecs.idversion.IDVersionPostingsFormat;
-}
+/** Experimental index-related classes */
+package org.apache.lucene.sandbox.index;
diff --git a/lucene/sandbox/src/test/org/apache/lucene/sandbox/index/TestMergeOnFlushMergePolicy.java b/lucene/sandbox/src/test/org/apache/lucene/sandbox/index/TestMergeOnFlushMergePolicy.java
new file mode 100644
index 0000000..12ba8d5
--- /dev/null
+++ b/lucene/sandbox/src/test/org/apache/lucene/sandbox/index/TestMergeOnFlushMergePolicy.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.lucene.sandbox.index;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Random;
+import java.util.Set;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.MergePolicy;
+import org.apache.lucene.index.MergeTrigger;
+import org.apache.lucene.index.SegmentCommitInfo;
+import org.apache.lucene.index.SegmentInfos;
+import org.apache.lucene.index.TieredMergePolicy;
+import org.apache.lucene.tests.index.BaseMergePolicyTestCase;
+import org.apache.lucene.tests.util.TestUtil;
+import org.apache.lucene.util.Version;
+
+/** Test for {@link MergeOnFlushMergePolicy}. */
+public class TestMergeOnFlushMergePolicy extends BaseMergePolicyTestCase {
+
+  @Override
+  protected MergePolicy mergePolicy() {
+    Random r = random();
+    MergePolicy mergePolicy = newMergePolicy();
+    MergeOnFlushMergePolicy mergeOnFlushPolicy = new MergeOnFlushMergePolicy(mergePolicy);
+    mergeOnFlushPolicy.setMaxCFSSegmentSizeMB(mergePolicy.getMaxCFSSegmentSizeMB());
+    mergeOnFlushPolicy.setNoCFSRatio(mergePolicy.getNoCFSRatio());
+    mergeOnFlushPolicy.setSmallSegmentThresholdMB(TestUtil.nextInt(r, 1, 100));
+    if (mergePolicy instanceof TieredMergePolicy) {
+      ((TieredMergePolicy) mergePolicy)
+          .setMaxMergedSegmentMB(TestUtil.nextInt(random(), 1024, 10 * 1024));
+    }
+    return mergeOnFlushPolicy;
+  }
+
+  @Override
+  protected void assertSegmentInfos(MergePolicy policy, SegmentInfos infos) {}
+
+  @Override
+  protected void assertMerge(MergePolicy policy, MergePolicy.MergeSpecification merge) {}
+
+  public void testFindFullFlushMerges() throws IOException {
+    MergeOnFlushMergePolicy mergePolicy = (MergeOnFlushMergePolicy) mergePolicy();
+    double smallSegmentThresholdMB = mergePolicy.getSmallSegmentThresholdMB();
+    Random r = random();
+
+    for (int j = 0; j < 10_000; j++) {
+      SegmentInfos segmentInfos = new SegmentInfos(Version.LATEST.major);
+      int numSegs = random().nextInt(50);
+      Set<SegmentCommitInfo> mergingSegments = new HashSet<>();
+      Set<SegmentCommitInfo> smallSegments = new HashSet<>();
+      for (int i = 0; i < numSegs; i++) {
+        SegmentCommitInfo sci =
+            makeSegmentCommitInfo(
+                "_" + i,
+                TestUtil.nextInt(r, 10, 100),
+                r.nextInt(10),
+                r.nextDouble() * 2.0 * smallSegmentThresholdMB,
+                IndexWriter.SOURCE_FLUSH);
+        if (sci.sizeInBytes() < MergeOnFlushMergePolicy.Units.mbToBytes(smallSegmentThresholdMB)) {
+          smallSegments.add(sci);
+        }
+        if (r.nextBoolean()) {
+          mergingSegments.add(sci);
+        }
+        segmentInfos.add(sci);
+      }
+      MockMergeContext context = new MockMergeContext(SegmentCommitInfo::getDelCount);
+      context.setMergingSegments(mergingSegments);
+      MergePolicy.MergeSpecification mergeSpecification;
+
+      mergeSpecification =
+          mergePolicy.findFullFlushMerges(MergeTrigger.COMMIT, segmentInfos, context);
+
+      if (mergeSpecification == null) {
+        // If we didn't compute a merge, then we have at most one small segment not already
+        // participating in a merge.
+        boolean foundNonMergingSmallSegment = false;
+        for (SegmentCommitInfo smallSegment : smallSegments) {
+          if (!mergingSegments.contains(smallSegment)) {
+            assertFalse(
+                "If no merges, then at most one small segment is not already merging",
+                foundNonMergingSmallSegment);
+            foundNonMergingSmallSegment = true;
+          }
+        }
+      } else {
+        for (MergePolicy.OneMerge oneMerge : mergeSpecification.merges) {
+          for (SegmentCommitInfo sci : oneMerge.segments) {
+            assertTrue("Merges only contain small segments", smallSegments.contains(sci));
+            assertFalse(
+                "findFullFlushMerges must not return already merging segments",
+                mergingSegments.contains(sci));
+          }
+        }
+      }
+    }
+  }
+}