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

dsmiley pushed a commit to branch branch_10_0
in repository https://gitbox.apache.org/repos/asf/solr.git


The following commit(s) were added to refs/heads/branch_10_0 by this push:
     new ae8b468b104 SOLR-17725: Add MergePolicy to block older segments from 
participating in merges (#3883)
ae8b468b104 is described below

commit ae8b468b1043c6e50a387756d9ceab8d1fe974c7
Author: Rahul Goswami <[email protected]>
AuthorDate: Tue Dec 2 20:53:29 2025 -0500

    SOLR-17725: Add MergePolicy to block older segments from participating in 
merges (#3883)
    
    Helps with Solr upgrades in conjunction with re-indexing.
    New: LatestVersionFilterMergePolicy
    
    ---------
    
    Co-authored-by: Rahul Goswami <[email protected]>
---
 ...-for-compatibility-with-future-Solr-version.yml |   8 ++
 .../solr/index/LatestVersionFilterMergePolicy.java | 111 +++++++++++++++++++++
 .../index/LatestVersionMergePolicyFactory.java     |  43 ++++++++
 3 files changed, 162 insertions(+)

diff --git 
a/changelog/unreleased/SOLR-17725-Merge-policy-to-upgrade-index-for-compatibility-with-future-Solr-version.yml
 
b/changelog/unreleased/SOLR-17725-Merge-policy-to-upgrade-index-for-compatibility-with-future-Solr-version.yml
new file mode 100644
index 00000000000..502be026e8e
--- /dev/null
+++ 
b/changelog/unreleased/SOLR-17725-Merge-policy-to-upgrade-index-for-compatibility-with-future-Solr-version.yml
@@ -0,0 +1,8 @@
+# See https://github.com/apache/solr/blob/main/dev-docs/changelog.adoc
+title: Merge policy to upgrade index for compatibility with future Solr version
+type: added
+authors:
+  - name: Rahul Goswami
+links:
+  - name: SOLR-17725
+    url: https://issues.apache.org/jira/browse/SOLR-17725
diff --git 
a/solr/core/src/java/org/apache/solr/index/LatestVersionFilterMergePolicy.java 
b/solr/core/src/java/org/apache/solr/index/LatestVersionFilterMergePolicy.java
new file mode 100644
index 00000000000..c5a088220b2
--- /dev/null
+++ 
b/solr/core/src/java/org/apache/solr/index/LatestVersionFilterMergePolicy.java
@@ -0,0 +1,111 @@
+/*
+ * 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.solr.index;
+
+import java.io.IOException;
+import java.util.Map;
+import org.apache.lucene.index.FilterMergePolicy;
+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.util.Version;
+
+/**
+ * Prevents any older version segment (i.e. older than current lucene major 
version), either
+ * original or one derived as a result of merging with an older version 
segment, from being
+ * considered for merges. That way a snapshot of older segments remains 
consistent. This assists in
+ * upgrading to a future Lucene major version if existing documents are 
reindexed in the current
+ * version with this merge policy in place.
+ */
+public class LatestVersionFilterMergePolicy extends FilterMergePolicy {
+
+  public LatestVersionFilterMergePolicy(MergePolicy in) {
+    super(in);
+  }
+
+  @Override
+  public MergeSpecification findMerges(
+      MergeTrigger mergeTrigger, SegmentInfos infos, MergeContext 
mergeContext) throws IOException {
+    return in.findMerges(mergeTrigger, getFilteredInfos(infos), mergeContext);
+  }
+
+  @Override
+  public MergeSpecification findForcedMerges(
+      SegmentInfos infos,
+      int maxSegmentCount,
+      Map<SegmentCommitInfo, Boolean> segmentsToMerge,
+      MergeContext mergeContext)
+      throws IOException {
+    return in.findForcedMerges(
+        getFilteredInfos(infos), maxSegmentCount, segmentsToMerge, 
mergeContext);
+  }
+
+  @Override
+  public MergeSpecification findForcedDeletesMerges(SegmentInfos infos, 
MergeContext mergeContext)
+      throws IOException {
+    return in.findForcedDeletesMerges(getFilteredInfos(infos), mergeContext);
+  }
+
+  @Override
+  public MergeSpecification findFullFlushMerges(
+      MergeTrigger mergeTrigger, SegmentInfos infos, MergeContext 
mergeContext) throws IOException {
+    return in.findFullFlushMerges(mergeTrigger, getFilteredInfos(infos), 
mergeContext);
+  }
+
+  private SegmentInfos getFilteredInfos(SegmentInfos infos) {
+    SegmentInfos infosClone = null;
+
+    for (SegmentCommitInfo info : infos) {
+      if (!allowSegmentForMerge(info)) {
+        // There are older version segments present.
+        // We should not remove from the original SegmentInfos. Hence we clone.
+        infosClone = infos.clone();
+        infosClone.clear();
+        break;
+      }
+    }
+
+    if (infosClone == null) {
+      // All segments are latest major version and allowed to participate in 
merge
+      return infos;
+    } else {
+      // Either mixed versions or all older version segments.
+      // If we are here, most runs should fall in the former case.
+      // The latter case should only happen once right after an upgrade, so we 
are ok with incurring
+      // this redundant iteration for that one time to keep the logic simple
+      for (SegmentCommitInfo info : infos) {
+        if (allowSegmentForMerge(info)) {
+          infosClone.add(info);
+        }
+      }
+    }
+
+    return infosClone;
+  }
+
+  /**
+   * Determines if a SegmentCommitInfo should be part of the candidate set of 
segments that will be
+   * considered for merges. By default, we only allow LATEST version segments 
to participate in
+   * merges.
+   */
+  protected boolean allowSegmentForMerge(SegmentCommitInfo info) {
+    return info.info.getMinVersion() != null
+        && info.info.getMinVersion().major == Version.LATEST.major;
+  }
+}
diff --git 
a/solr/core/src/java/org/apache/solr/index/LatestVersionMergePolicyFactory.java 
b/solr/core/src/java/org/apache/solr/index/LatestVersionMergePolicyFactory.java
new file mode 100644
index 00000000000..047830d3199
--- /dev/null
+++ 
b/solr/core/src/java/org/apache/solr/index/LatestVersionMergePolicyFactory.java
@@ -0,0 +1,43 @@
+/*
+ * 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.solr.index;
+
+import org.apache.lucene.index.MergePolicy;
+import org.apache.lucene.index.TieredMergePolicy;
+import org.apache.solr.core.SolrResourceLoader;
+import org.apache.solr.schema.IndexSchema;
+
+/**
+ * A {@link MergePolicyFactory} for {@link LatestVersionFilterMergePolicy} 
objects. The returned
+ * LatestVersionFilterMergePolicy instance blocks older version segments (&lt; 
current version of
+ * Lucene) from participating in merges and delegates the merging to a 
TieredMergePolicy instance by
+ * default. This can be used to reindex the data and ensure all segments are 
the latest version
+ * segments by the end of the reindexing. This can help prepare the index for 
upgrade to a later
+ * version of Solr/Lucene even if it was initially created on a now 
unsupported version
+ */
+public class LatestVersionMergePolicyFactory extends SimpleMergePolicyFactory {
+
+  public LatestVersionMergePolicyFactory(
+      SolrResourceLoader resourceLoader, MergePolicyFactoryArgs args, 
IndexSchema schema) {
+    super(resourceLoader, args, schema);
+  }
+
+  @Override
+  protected MergePolicy getMergePolicyInstance() {
+    return new LatestVersionFilterMergePolicy(new TieredMergePolicy());
+  }
+}

Reply via email to