blob: 5f4eb95d1bd55fc83f7a7e2cf0f6e179330f06b6 [file] [log] [blame]
/*
* 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.core;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.index.LogByteSizeMergePolicy;
import org.apache.lucene.index.LogDocMergePolicy;
import org.apache.lucene.index.LogMergePolicy;
import org.apache.lucene.index.NoMergePolicy;
import org.apache.lucene.index.SegmentReader;
import org.apache.lucene.index.TieredMergePolicy;
import org.apache.solr.SolrTestCaseJ4;
import org.apache.solr.index.LogByteSizeMergePolicyFactory;
import org.apache.solr.index.LogDocMergePolicyFactory;
import org.apache.solr.index.MergePolicyFactory;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.search.SolrIndexSearcher;
import org.apache.solr.update.CommitUpdateCommand;
import org.apache.solr.update.SolrIndexConfigTest;
import org.apache.solr.update.UpdateHandler;
import org.apache.solr.util.RefCounted;
import org.junit.After;
/** @see SolrIndexConfigTest */
public class TestMergePolicyConfig extends SolrTestCaseJ4 {
private static AtomicInteger docIdCounter = new AtomicInteger(42);
@After
public void after() throws Exception {
deleteCore();
}
public void testSetNoCFSMergePolicyConfig() throws Exception {
final boolean useCompoundFile = random().nextBoolean();
System.setProperty("testSetNoCFSMergePolicyConfig.useCompoundFile", String.valueOf(useCompoundFile));
try {
initCore("solrconfig-mergepolicyfactory-nocfs.xml","schema-minimal.xml");
IndexWriterConfig iwc = solrConfig.indexConfig.toIndexWriterConfig(h.getCore());
assertEquals(useCompoundFile, iwc.getUseCompoundFile());
TieredMergePolicy tieredMP = assertAndCast(TieredMergePolicy.class,
iwc.getMergePolicy());
assertEquals(0.5D, tieredMP.getNoCFSRatio(), 0.0D);
} finally {
System.getProperties().remove("testSetNoCFSMergePolicyConfig.useCompoundFile");
}
}
public void testDefaultMergePolicyConfig() throws Exception {
initCore("solrconfig-mergepolicy-defaults.xml","schema-minimal.xml");
IndexWriterConfig iwc = solrConfig.indexConfig.toIndexWriterConfig(h.getCore());
assertEquals(false, iwc.getUseCompoundFile());
TieredMergePolicy tieredMP = assertAndCast(TieredMergePolicy.class,
iwc.getMergePolicy());
assertEquals(TieredMergePolicy.DEFAULT_NO_CFS_RATIO, tieredMP.getNoCFSRatio(), 0.0D);
assertCommitSomeNewDocs();
assertCompoundSegments(h.getCore(), false);
}
public void testLegacyMergePolicyConfig() throws Exception {
final boolean expectCFS = Boolean.parseBoolean(System.getProperty("useCompoundFile"));
initCore("solrconfig-mergepolicy-legacy.xml","schema-minimal.xml");
IndexWriterConfig iwc = solrConfig.indexConfig.toIndexWriterConfig(h.getCore());
assertEquals(expectCFS, iwc.getUseCompoundFile());
TieredMergePolicy tieredMP = assertAndCast(TieredMergePolicy.class, iwc.getMergePolicy());
assertEquals(10, tieredMP.getMaxMergeAtOnce());
assertEquals(10.0D, tieredMP.getSegmentsPerTier(), 0.0D);
assertCommitSomeNewDocs();
assertCompoundSegments(h.getCore(), expectCFS);
}
public void testTieredMergePolicyConfig() throws Exception {
final boolean expectCFS
= Boolean.parseBoolean(System.getProperty("useCompoundFile"));
initCore("solrconfig-tieredmergepolicyfactory.xml","schema-minimal.xml");
IndexWriterConfig iwc = solrConfig.indexConfig.toIndexWriterConfig(h.getCore());
assertEquals(expectCFS, iwc.getUseCompoundFile());
TieredMergePolicy tieredMP = assertAndCast(TieredMergePolicy.class,
iwc.getMergePolicy());
// set by legacy <mergeFactor> setting
assertEquals(7, tieredMP.getMaxMergeAtOnce());
// mp-specific setters
assertEquals(19, tieredMP.getMaxMergeAtOnceExplicit());
assertEquals(0.1D, tieredMP.getNoCFSRatio(), 0.0D);
// make sure we overrode segmentsPerTier
// (split from maxMergeAtOnce out of mergeFactor)
assertEquals(9D, tieredMP.getSegmentsPerTier(), 0.001);
assertCommitSomeNewDocs();
// even though we have a single segment (which is 100% of the size of
// the index which is higher then our 0.6D threshold) the
// compound ratio doesn't matter because the segment was never merged
assertCompoundSegments(h.getCore(), expectCFS);
assertCommitSomeNewDocs();
assertNumSegments(h.getCore(), 2);
assertCompoundSegments(h.getCore(), expectCFS);
assertU(optimize("maxSegments", "1"));
assertNumSegments(h.getCore(), 1);
// we've now forced a merge, and the MP ratio should be in play
assertCompoundSegments(h.getCore(), false);
}
public void testNoMergePolicyFactoryConfig() throws Exception {
initCore("solrconfig-nomergepolicyfactory.xml","schema-minimal.xml");
IndexWriterConfig iwc = solrConfig.indexConfig.toIndexWriterConfig(h.getCore());
NoMergePolicy mergePolicy = assertAndCast(NoMergePolicy.class,
iwc.getMergePolicy());
assertCommitSomeNewDocs();
assertCommitSomeNewDocs();
assertNumSegments(h.getCore(), 2);
assertU(optimize());
assertNumSegments(h.getCore(), 2);
deleteCore();
initCore("solrconfig-nomergepolicyfactory.xml","schema-minimal.xml");
iwc = solrConfig.indexConfig.toIndexWriterConfig(h.getCore());
assertEquals(mergePolicy, iwc.getMergePolicy());
UpdateHandler updater = h.getCore().getUpdateHandler();
SolrQueryRequest req = req();
CommitUpdateCommand cmtCmd = new CommitUpdateCommand(req, true);
cmtCmd.maxOptimizeSegments = -1;
expectThrows(IllegalArgumentException.class, () -> {
updater.commit(cmtCmd);
});
}
public void testLogMergePolicyFactoryConfig() throws Exception {
final boolean byteSizeMP = random().nextBoolean();
final Class<? extends LogMergePolicy> mpClass = byteSizeMP
? LogByteSizeMergePolicy.class : LogDocMergePolicy.class;
final Class<? extends MergePolicyFactory> mpfClass = byteSizeMP
? LogByteSizeMergePolicyFactory.class : LogDocMergePolicyFactory.class;
System.setProperty("solr.test.log.merge.policy.factory", mpfClass.getName());
implTestLogMergePolicyConfig("solrconfig-logmergepolicyfactory.xml", mpClass);
}
private void implTestLogMergePolicyConfig(String solrConfigFileName,
Class<? extends LogMergePolicy> mpClass) throws Exception {
initCore(solrConfigFileName, "schema-minimal.xml");
IndexWriterConfig iwc = solrConfig.indexConfig.toIndexWriterConfig(h.getCore());
// verify some props set to -1 get lucene internal defaults
assertEquals(-1, solrConfig.indexConfig.maxBufferedDocs);
assertEquals(IndexWriterConfig.DISABLE_AUTO_FLUSH,
iwc.getMaxBufferedDocs());
assertEquals(-1, solrConfig.indexConfig.ramBufferSizeMB, 0.0D);
assertEquals(IndexWriterConfig.DEFAULT_RAM_BUFFER_SIZE_MB,
iwc.getRAMBufferSizeMB(), 0.0D);
LogMergePolicy logMP = assertAndCast(mpClass, iwc.getMergePolicy());
assertEquals(11, logMP.getMergeFactor());
assertEquals(456, logMP.getMaxMergeDocs());
}
/**
* Given a Type and an object asserts that the object is non-null and an
* instance of the specified Type. The object is then cast to that type and
* returned.
*/
public static <T> T assertAndCast(Class<? extends T> clazz, Object o) {
assertNotNull(clazz);
assertNotNull(o);
assertTrue(clazz.isInstance(o));
return clazz.cast(o);
}
public static void assertCommitSomeNewDocs() {
for (int i = 0; i < 5; i++) {
int val = docIdCounter.getAndIncrement();
assertU(adoc("id", "" + val,
"a_s", val + "_" + val + "_" + val + "_" + val,
"b_s", val + "_" + val + "_" + val + "_" + val,
"c_s", val + "_" + val + "_" + val + "_" + val,
"d_s", val + "_" + val + "_" + val + "_" + val,
"e_s", val + "_" + val + "_" + val + "_" + val,
"f_s", val + "_" + val + "_" + val + "_" + val));
}
assertU(commit());
}
/**
* Given an SolrCore, asserts that the number of leave segments in
* the index reader matches the expected value.
*/
public static void assertNumSegments(SolrCore core, int expected) {
RefCounted<SolrIndexSearcher> searcherRef = core.getRegisteredSearcher();
try {
assertEquals(expected, searcherRef.get().getIndexReader().leaves().size());
} finally {
searcherRef.decref();
}
}
/**
* Given an SolrCore, asserts that each segment in the (searchable) index
* has a compound file status that matches the expected input.
*/
public static void assertCompoundSegments(SolrCore core, boolean compound) {
RefCounted<SolrIndexSearcher> searcherRef = core.getRegisteredSearcher();
try {
assertCompoundSegments(searcherRef.get().getRawReader(), compound);
} finally {
searcherRef.decref();
}
}
/**
* Given an IndexReader, asserts that there is at least one AtomcReader leaf,
* and that all LeafReader leaves are SegmentReader's that have a compound
* file status that matches the expected input.
*/
private static void assertCompoundSegments(IndexReader reader,
boolean compound) {
assertNotNull("Null leaves", reader.leaves());
assertTrue("no leaves", 0 < reader.leaves().size());
for (LeafReaderContext atomic : reader.leaves()) {
assertTrue("not a segment reader: " + atomic.reader().toString(),
atomic.reader() instanceof SegmentReader);
assertEquals("Compound status incorrect for: " +
atomic.reader().toString(),
compound,
((SegmentReader)atomic.reader()).getSegmentInfo().info.getUseCompoundFile());
}
}
}