Merge branch 'master' into jira/solr-12730
diff --git a/lucene/core/src/java/org/apache/lucene/index/IndexFileDeleter.java b/lucene/core/src/java/org/apache/lucene/index/IndexFileDeleter.java
index cc9d2e0..cd5cf7f 100644
--- a/lucene/core/src/java/org/apache/lucene/index/IndexFileDeleter.java
+++ b/lucene/core/src/java/org/apache/lucene/index/IndexFileDeleter.java
@@ -20,6 +20,8 @@
import java.io.Closeable;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
import java.nio.file.NoSuchFileException;
import java.util.ArrayList;
import java.util.Collection;
@@ -108,7 +110,7 @@
/** Change to true to see details of reference counts when
* infoStream is enabled */
- public static boolean VERBOSE_REF_COUNTS = false;
+ public static boolean VERBOSE_REF_COUNTS = true;
private final IndexWriter writer;
@@ -564,7 +566,16 @@
RefCount rc = getRefCount(fileName);
if (infoStream.isEnabled("IFD")) {
if (VERBOSE_REF_COUNTS) {
- infoStream.message("IFD", " IncRef \"" + fileName + "\": pre-incr count is " + rc.count);
+ // todo nocommit remove the extra logging
+ String extra = null;
+ if ("_0.fdt".equals(fileName)) {
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter writer = new PrintWriter(stringWriter);
+ new Exception().printStackTrace(writer);
+ extra = stringWriter.toString();
+ }
+
+ infoStream.message("IFD", " IncRef \"" + fileName + "\": pre-incr count is " + rc.count + (extra != null ? " and stack is " + extra : ""));
}
}
rc.IncRef();
@@ -602,7 +613,15 @@
RefCount rc = getRefCount(fileName);
if (infoStream.isEnabled("IFD")) {
if (VERBOSE_REF_COUNTS) {
- infoStream.message("IFD", " DecRef \"" + fileName + "\": pre-decr count is " + rc.count);
+ String extra = null;
+ // todo nocommit remove the extra logging
+ if ("_0.fdt".equals(fileName)) {
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter writer = new PrintWriter(stringWriter);
+ new Exception().printStackTrace(writer);
+ extra = stringWriter.toString();
+ }
+ infoStream.message("IFD", " DecRef \"" + fileName + "\": pre-decr count is " + rc.count + (extra != null ? " and stack is " + extra : ""));
}
}
if (rc.DecRef() == 0) {
diff --git a/lucene/core/src/java/org/apache/lucene/store/FSDirectory.java b/lucene/core/src/java/org/apache/lucene/store/FSDirectory.java
index fb03f85..db4e1f9 100644
--- a/lucene/core/src/java/org/apache/lucene/store/FSDirectory.java
+++ b/lucene/core/src/java/org/apache/lucene/store/FSDirectory.java
@@ -339,6 +339,7 @@
}
privateDeleteFile(name, false);
maybeDeletePendingFiles();
+ System.out.println("Deleted file: " + name);
}
/** Try to delete any pending files that we had previously tried to delete but failed
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/SplitShardCmd.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/SplitShardCmd.java
index aa4909d..b9370ac 100644
--- a/solr/core/src/java/org/apache/solr/cloud/api/collections/SplitShardCmd.java
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/SplitShardCmd.java
@@ -686,6 +686,13 @@
boolean firstReplicaNrt) {
String splitKey = message.getStr("split.key");
String rangesStr = message.getStr(CoreAdminParams.RANGES);
+ String fuzzStr = message.getStr(CommonAdminParams.SPLIT_FUZZ, "0");
+ float fuzz = 0.0f;
+ try {
+ fuzz = Float.parseFloat(fuzzStr);
+ } catch (Exception e) {
+ throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "?Invalid numeric value of 'fuzz': " + fuzzStr);
+ }
DocRouter.Range range = parentSlice.getRange();
if (range == null) {
@@ -752,7 +759,7 @@
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
"A shard can only be split into "+MIN_NUM_SUB_SHARDS+" to " + MAX_NUM_SUB_SHARDS
+ " subshards in one split request. Provided "+NUM_SUB_SHARDS+"=" + numSubShards);
- subRanges.addAll(router.partitionRange(numSubShards, range));
+ subRanges.addAll(router.partitionRange(numSubShards, range, fuzz));
}
for (int i = 0; i < subRanges.size(); i++) {
diff --git a/solr/core/src/java/org/apache/solr/cloud/autoscaling/IndexSizeTrigger.java b/solr/core/src/java/org/apache/solr/cloud/autoscaling/IndexSizeTrigger.java
index 31322c0..27daec2 100644
--- a/solr/core/src/java/org/apache/solr/cloud/autoscaling/IndexSizeTrigger.java
+++ b/solr/core/src/java/org/apache/solr/cloud/autoscaling/IndexSizeTrigger.java
@@ -65,6 +65,7 @@
public static final String BELOW_OP_PROP = "belowOp";
public static final String COLLECTIONS_PROP = "collections";
public static final String MAX_OPS_PROP = "maxOps";
+ public static final String SPLIT_FUZZ_PROP = CommonAdminParams.SPLIT_FUZZ;
public static final String SPLIT_METHOD_PROP = CommonAdminParams.SPLIT_METHOD;
public static final String BYTES_SIZE_PROP = "__bytes__";
@@ -80,6 +81,7 @@
private long aboveBytes, aboveDocs, belowBytes, belowDocs;
private int maxOps;
private SolrIndexSplitter.SplitMethod splitMethod;
+ private float splitFuzz;
private CollectionParams.CollectionAction aboveOp, belowOp;
private final Set<String> collections = new HashSet<>();
private final Map<String, Long> lastAboveEventMap = new ConcurrentHashMap<>();
@@ -89,7 +91,7 @@
super(TriggerEventType.INDEXSIZE, name);
TriggerUtils.validProperties(validProperties,
ABOVE_BYTES_PROP, ABOVE_DOCS_PROP, BELOW_BYTES_PROP, BELOW_DOCS_PROP,
- COLLECTIONS_PROP, MAX_OPS_PROP, SPLIT_METHOD_PROP);
+ COLLECTIONS_PROP, MAX_OPS_PROP, SPLIT_METHOD_PROP, SPLIT_FUZZ_PROP);
}
@Override
@@ -175,6 +177,12 @@
throw new TriggerValidationException(getName(), SPLIT_METHOD_PROP, "Unknown value '" + CommonAdminParams.SPLIT_METHOD +
": " + methodStr);
}
+ String fuzzStr = String.valueOf(properties.getOrDefault(SPLIT_FUZZ_PROP, 0.0f));
+ try {
+ splitFuzz = Float.parseFloat(fuzzStr);
+ } catch (Exception e) {
+ throw new TriggerValidationException(getName(), SPLIT_FUZZ_PROP, "invalid value: '" + fuzzStr + "': " + e.getMessage());
+ }
}
@Override
@@ -278,6 +286,7 @@
String registry = SolrCoreMetricManager.createRegistryName(true, coll, sh, replicaName, null);
String tag = "metrics:" + registry + ":INDEX.sizeInBytes";
metricTags.put(tag, info);
+ metricTags.put("metrics:" + registry + ":INDEX.sizeDetails", info);
tag = "metrics:" + registry + ":SEARCHER.searcher.numDocs";
metricTags.put(tag, info);
});
@@ -399,6 +408,9 @@
op.addHint(Suggester.Hint.COLL_SHARD, new Pair<>(coll, r.getShard()));
Map<String, Object> params = new HashMap<>();
params.put(CommonAdminParams.SPLIT_METHOD, splitMethod.toLower());
+ if (splitFuzz > 0) {
+ params.put(CommonAdminParams.SPLIT_FUZZ, splitFuzz);
+ }
op.addHint(Suggester.Hint.PARAMS, params);
ops.add(op);
Long time = lastAboveEventMap.get(r.getCore());
@@ -451,6 +463,14 @@
if (ops.isEmpty()) {
return;
}
+ try {
+ ClusterState cs = cloudManager.getClusterStateProvider().getClusterState();
+ cs.forEachCollection(coll -> {
+ log.debug("##== Collection: {}", coll);
+ });
+ } catch (IOException e) {
+ throw new RuntimeException("oops: ", e);
+ }
if (processor.process(new IndexSizeEvent(getName(), eventTime.get(), ops, aboveSize, belowSize))) {
// update last event times
aboveSize.forEach((coll, replicas) -> {
diff --git a/solr/core/src/java/org/apache/solr/core/DirectoryFactory.java b/solr/core/src/java/org/apache/solr/core/DirectoryFactory.java
index fab3300..a05a36c 100644
--- a/solr/core/src/java/org/apache/solr/core/DirectoryFactory.java
+++ b/solr/core/src/java/org/apache/solr/core/DirectoryFactory.java
@@ -280,6 +280,8 @@
break;
}
}
+
+ System.out.println(Arrays.toString(files) + " size=" + size + " on path: " + directory.toString());
return size;
}
diff --git a/solr/core/src/java/org/apache/solr/core/IndexDeletionPolicyWrapper.java b/solr/core/src/java/org/apache/solr/core/IndexDeletionPolicyWrapper.java
index 40e65b7..ee7ed36 100644
--- a/solr/core/src/java/org/apache/solr/core/IndexDeletionPolicyWrapper.java
+++ b/solr/core/src/java/org/apache/solr/core/IndexDeletionPolicyWrapper.java
@@ -130,6 +130,7 @@
if (reserveCount == null) reserveCount = new AtomicInteger();
reserveCount.incrementAndGet();
savedCommits.put(indexCommitGen, reserveCount);
+ log.debug("Saving commit point for generation {}", indexCommitGen);
}
/** Release a previously saved commit point */
@@ -138,6 +139,7 @@
if (reserveCount == null) return;// this should not happen
if (reserveCount.decrementAndGet() <= 0) {
savedCommits.remove(indexCommitGen);
+ log.debug("Releasing commit point for generation {}", indexCommitGen);
}
}
@@ -189,11 +191,20 @@
@Override
public void delete() {
Long gen = delegate.getGeneration();
+ log.debug("Checking whether we can delete commit point with generation: {}", gen);
Long reserve = reserves.get(gen);
- if (reserve != null && System.nanoTime() < reserve) return;
- if (savedCommits.containsKey(gen)) return;
+ long currentTime = System.nanoTime();
+ if (reserve != null && currentTime < reserve) {
+ log.debug("Commit point with generation: {} not deleted because its reserve {} is less than current time {}", gen, reserve, currentTime);
+ return;
+ }
+ if (savedCommits.containsKey(gen)) {
+ log.debug("Commit point with generation: {} not deleted because it is saved");
+ return;
+ }
if (snapshotMgr.isSnapshotted(gen)) return;
delegate.delete();
+ log.debug("Commit point with generation: {} deleted", gen);
}
@Override
diff --git a/solr/core/src/java/org/apache/solr/core/SolrCore.java b/solr/core/src/java/org/apache/solr/core/SolrCore.java
index 6e13039..1af7771 100644
--- a/solr/core/src/java/org/apache/solr/core/SolrCore.java
+++ b/solr/core/src/java/org/apache/solr/core/SolrCore.java
@@ -433,6 +433,7 @@
dir = directoryFactory.get(getIndexDir(), DirContext.DEFAULT, solrConfig.indexConfig.lockType);
try {
size = DirectoryFactory.sizeOfDirectory(dir);
+ System.out.println("Found size=" + size + " for indexDir=" + getIndexDir());
} finally {
directoryFactory.release(dir);
}
@@ -443,6 +444,31 @@
return size;
}
+ String getIndexSizeDetails() {
+ Directory dir;
+ StringBuilder sb = new StringBuilder();
+ try {
+ if (directoryFactory.exists(getIndexDir())) {
+ dir = directoryFactory.get(getIndexDir(), DirContext.DEFAULT, solrConfig.indexConfig.lockType);
+ try {
+ String[] files = dir.listAll();
+ Arrays.sort(files);
+ for (String file : files) {
+ sb.append('\n');
+ sb.append(file);
+ sb.append('\t');
+ sb.append(String.valueOf(dir.fileLength(file)));
+ }
+ } finally {
+ directoryFactory.release(dir);
+ }
+ }
+ } catch (IOException e) {
+ SolrException.log(log, "IO error while trying to get the details of the Directory", e);
+ }
+ return sb.toString();
+ }
+
@Override
public String getName() {
return name;
@@ -1161,6 +1187,7 @@
manager.registerGauge(this, registry, () -> resourceLoader.getInstancePath().toString(), getMetricTag(), true, "instanceDir", Category.CORE.toString());
manager.registerGauge(this, registry, () -> isClosed() ? "(closed)" : getIndexDir(), getMetricTag(), true, "indexDir", Category.CORE.toString());
manager.registerGauge(this, registry, () -> isClosed() ? 0 : getIndexSize(), getMetricTag(), true, "sizeInBytes", Category.INDEX.toString());
+ manager.registerGauge(this, registry, () -> isClosed() ? "" : getIndexSizeDetails(), getMetricTag(), true, "sizeDetails", Category.INDEX.toString());
manager.registerGauge(this, registry, () -> isClosed() ? "(closed)" : NumberUtils.readableSize(getIndexSize()), getMetricTag(), true, "size", Category.INDEX.toString());
if (coreContainer != null) {
manager.registerGauge(this, registry, () -> coreContainer.getNamesForCore(this), getMetricTag(), true, "aliases", Category.CORE.toString());
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/CollectionsHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/CollectionsHandler.java
index dfb3c6b..462da77 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/CollectionsHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/CollectionsHandler.java
@@ -144,6 +144,7 @@
import static org.apache.solr.common.params.CommonAdminParams.ASYNC;
import static org.apache.solr.common.params.CommonAdminParams.IN_PLACE_MOVE;
import static org.apache.solr.common.params.CommonAdminParams.NUM_SUB_SHARDS;
+import static org.apache.solr.common.params.CommonAdminParams.SPLIT_FUZZ;
import static org.apache.solr.common.params.CommonAdminParams.SPLIT_METHOD;
import static org.apache.solr.common.params.CommonAdminParams.WAIT_FOR_FINAL_STATE;
import static org.apache.solr.common.params.CommonParams.NAME;
@@ -640,6 +641,7 @@
String rangesStr = req.getParams().get(CoreAdminParams.RANGES);
String splitKey = req.getParams().get("split.key");
String numSubShards = req.getParams().get(NUM_SUB_SHARDS);
+ String fuzz = req.getParams().get(SPLIT_FUZZ);
if (splitKey == null && shard == null) {
throw new SolrException(ErrorCode.BAD_REQUEST, "At least one of shard, or split.key should be specified.");
@@ -656,6 +658,10 @@
throw new SolrException(ErrorCode.BAD_REQUEST,
"numSubShards can not be specified with split.key or ranges parameters");
}
+ if (fuzz != null && (splitKey != null || rangesStr != null)) {
+ throw new SolrException(ErrorCode.BAD_REQUEST,
+ "fuzz can not be specified with split.key or ranges parameters");
+ }
Map<String, Object> map = copy(req.getParams(), null,
COLLECTION_PROP,
@@ -665,7 +671,8 @@
WAIT_FOR_FINAL_STATE,
TIMING,
SPLIT_METHOD,
- NUM_SUB_SHARDS);
+ NUM_SUB_SHARDS,
+ SPLIT_FUZZ);
return copyPropertiesWithPrefix(req.getParams(), map, COLL_PROP_PREFIX);
}),
DELETESHARD_OP(DELETESHARD, (req, rsp, h) -> {
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/MetricsHandler.java b/solr/core/src/java/org/apache/solr/handler/admin/MetricsHandler.java
index 752e021..9b9f948 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/MetricsHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/MetricsHandler.java
@@ -17,6 +17,7 @@
package org.apache.solr.handler.admin;
+import java.lang.invoke.MethodHandles;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
@@ -41,6 +42,7 @@
import org.apache.solr.common.util.NamedList;
import org.apache.solr.common.util.SimpleOrderedMap;
import org.apache.solr.common.util.StrUtils;
+import org.apache.solr.common.util.Utils;
import org.apache.solr.core.CoreContainer;
import org.apache.solr.handler.RequestHandlerBase;
import org.apache.solr.metrics.SolrMetricManager;
@@ -49,11 +51,15 @@
import org.apache.solr.security.AuthorizationContext;
import org.apache.solr.security.PermissionNameProvider;
import org.apache.solr.util.stats.MetricUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* Request handler to return metrics
*/
public class MetricsHandler extends RequestHandlerBase implements PermissionNameProvider {
+ private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
final SolrMetricManager metricManager;
public static final String COMPACT_PARAM = "compact";
@@ -99,6 +105,8 @@
}
handleRequest(req.getParams(), (k, v) -> rsp.add(k, v));
+ log.debug("##== Req: {}", req);
+ log.debug("##== Rsp: {}", Utils.toJSONString(rsp.getValues()));
}
public void handleRequest(SolrParams params, BiConsumer<String, Object> consumer) throws Exception {
diff --git a/solr/core/src/java/org/apache/solr/update/SolrIndexConfig.java b/solr/core/src/java/org/apache/solr/update/SolrIndexConfig.java
index 48b2417..0df2b16 100644
--- a/solr/core/src/java/org/apache/solr/update/SolrIndexConfig.java
+++ b/solr/core/src/java/org/apache/solr/update/SolrIndexConfig.java
@@ -18,6 +18,7 @@
import java.io.IOException;
import java.lang.invoke.MethodHandles;
+import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -25,6 +26,7 @@
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.DelegatingAnalyzerWrapper;
import org.apache.lucene.index.ConcurrentMergeScheduler;
+import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriter.IndexReaderWarmer;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.MergePolicy;
@@ -48,6 +50,7 @@
import org.apache.solr.util.SolrPluginUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.slf4j.MDC;
import static org.apache.solr.core.Config.assertWarnOrFail;
@@ -283,7 +286,30 @@
private MergeScheduler buildMergeScheduler(IndexSchema schema) {
String msClassName = mergeSchedulerInfo == null ? SolrIndexConfig.DEFAULT_MERGE_SCHEDULER_CLASSNAME : mergeSchedulerInfo.className;
- MergeScheduler scheduler = schema.getResourceLoader().newInstance(msClassName, MergeScheduler.class);
+ // todo nocommit -- remove this scheduler instance with proper MDC logging support inside merge scheduler threads
+ MergeScheduler scheduler = new ConcurrentMergeScheduler() {
+ @Override
+ protected synchronized MergeThread getMergeThread(IndexWriter writer, MergePolicy.OneMerge merge) throws IOException {
+ MergeThread mergeThread = super.getMergeThread(writer, merge);
+ final Map<String, String> submitterContext = MDC.getCopyOfContextMap();
+ StringBuilder contextString = new StringBuilder();
+ if (submitterContext != null) {
+ Collection<String> values = submitterContext.values();
+
+ for (String value : values) {
+ contextString.append(value + " ");
+ }
+ if (contextString.length() > 1) {
+ contextString.setLength(contextString.length() - 1);
+ }
+ }
+
+ String ctxStr = contextString.toString().replace("/", "//");
+ final String submitterContextStr = ctxStr.length() <= 512 ? ctxStr : ctxStr.substring(0, 512);
+ mergeThread.setName(mergeThread.getName() + "-processing-" + submitterContextStr);
+ return mergeThread;
+ }
+ };
if (mergeSchedulerInfo != null) {
// LUCENE-5080: these two setters are removed, so we have to invoke setMaxMergesAndThreads
@@ -311,5 +337,4 @@
return scheduler;
}
-
}
diff --git a/solr/core/src/java/org/apache/solr/update/SolrIndexSplitter.java b/solr/core/src/java/org/apache/solr/update/SolrIndexSplitter.java
index 334a29d..6ed0b1f 100644
--- a/solr/core/src/java/org/apache/solr/update/SolrIndexSplitter.java
+++ b/solr/core/src/java/org/apache/solr/update/SolrIndexSplitter.java
@@ -174,7 +174,7 @@
log.error("Original error closing IndexWriter:", e);
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Error reopening IndexWriter after failed close", e1);
}
- throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Error closing current IndexWriter, aborting offline split...", e);
+ throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Error closing current IndexWriter, aborting 'link' split...", e);
}
}
boolean success = false;
@@ -195,7 +195,7 @@
t = timings.sub("parentApplyBufferedUpdates");
ulog.applyBufferedUpdates();
t.stop();
- log.info("Splitting in 'offline' mode " + (success? "finished" : "FAILED") +
+ log.info("Splitting in 'link' mode " + (success? "finished" : "FAILED") +
": re-opened parent IndexWriter.");
}
}
diff --git a/solr/core/src/java/org/apache/solr/update/processor/DistributedUpdateProcessor.java b/solr/core/src/java/org/apache/solr/update/processor/DistributedUpdateProcessor.java
index 004f4f7..8bdc8bc 100644
--- a/solr/core/src/java/org/apache/solr/update/processor/DistributedUpdateProcessor.java
+++ b/solr/core/src/java/org/apache/solr/update/processor/DistributedUpdateProcessor.java
@@ -235,7 +235,8 @@
// this should always be used - see filterParams
DistributedUpdateProcessorFactory.addParamToDistributedRequestWhitelist
- (this.req, UpdateParams.UPDATE_CHAIN, TEST_DISTRIB_SKIP_SERVERS, CommonParams.VERSION_FIELD);
+ (this.req, UpdateParams.UPDATE_CHAIN, TEST_DISTRIB_SKIP_SERVERS, CommonParams.VERSION_FIELD,
+ UpdateParams.EXPUNGE_DELETES, UpdateParams.OPTIMIZE, UpdateParams.MAX_OPTIMIZE_SEGMENTS);
CoreContainer cc = req.getCore().getCoreContainer();
@@ -1390,7 +1391,7 @@
}
if (dropCmd) {
- // TODO: do we need to add anything to the response?
+ // TODO: do we need to add anytprocehing to the response?
return;
}
diff --git a/solr/core/src/test-files/solr/configsets/cloud-minimal/conf/solrconfig.xml b/solr/core/src/test-files/solr/configsets/cloud-minimal/conf/solrconfig.xml
index f671842..d98ba99 100644
--- a/solr/core/src/test-files/solr/configsets/cloud-minimal/conf/solrconfig.xml
+++ b/solr/core/src/test-files/solr/configsets/cloud-minimal/conf/solrconfig.xml
@@ -46,6 +46,7 @@
</requestHandler>
<indexConfig>
<mergeScheduler class="${solr.mscheduler:org.apache.lucene.index.ConcurrentMergeScheduler}"/>
+ <infoStream>true</infoStream>
</indexConfig>
</config>
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/IndexSizeTriggerTest.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/IndexSizeTriggerTest.java
index ec2315d..314ddbd 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/IndexSizeTriggerTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/IndexSizeTriggerTest.java
@@ -68,7 +68,7 @@
/**
*
*/
-@LogLevel("org.apache.solr.cloud.autoscaling=DEBUG")
+//@LogLevel("org.apache.solr.cloud.autoscaling=DEBUG;org.apache.solr.handler.admin.MetricsHandler=DEBUG;org.apache.solr.core.SolrDeletionPolicy=DEBUG;org.apache.solr.core.IndexDeletionPolicyWrapper=DEBUG;org.apache.solr.client.solrj.cloud.autoscaling=DEBUG;org.apache.solr.cloud.api.collections=DEBUG;org.apache.solr.cloud.Overseer=DEBUG;org.apache.solr.cloud.overseer=DEBUGorg.apache.solr.client.solrj.cloud.autoscaling=DEBUG;org.apache.solr.cloud.api.collections=DEBUG;org.apache.solr.cloud.Overseer=DEBUG;org.apache.solr.cloud.overseer=DEBUG")
public class IndexSizeTriggerTest extends SolrCloudTestCase {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@@ -91,10 +91,11 @@
@BeforeClass
public static void setupCluster() throws Exception {
+ System.setProperty("solr.directoryFactory", "solr.StandardDirectoryFactory");
configureCluster(2)
.addConfig("conf", configset("cloud-minimal"))
.configure();
- if (random().nextBoolean()) {
+ if (random().nextBoolean() || true) {
cloudManager = cluster.getJettySolrRunner(0).getCoreContainer().getZkController().getSolrCloudManager();
solrClient = cluster.getSolrClient();
loader = cluster.getJettySolrRunner(0).getCoreContainer().getResourceLoader();
@@ -505,9 +506,10 @@
}
assertTrue("maxSize should be non-zero", maxSize > 0);
- int aboveBytes = maxSize * 2 / 3;
+ int aboveBytes = maxSize * 9 / 10;
- long waitForSeconds = 3 + random().nextInt(5);
+ // need to wait for recovery after splitting
+ long waitForSeconds = 10 + random().nextInt(5);
// the trigger is initially disabled so that we have time to add listeners
// and have them capture all events once the trigger is enabled
@@ -566,6 +568,7 @@
"'name' : 'index_size_trigger4'" +
"}" +
"}";
+ log.info("-- resuming trigger");
req = createAutoScalingRequest(SolrRequest.METHOD.POST, resumeTriggerCommand);
response = solrClient.request(req);
assertEquals(response.get("result").toString(), "success");
@@ -574,6 +577,17 @@
boolean await = finished.await(90000 / SPEED, TimeUnit.MILLISECONDS);
assertTrue("did not finish processing in time", await);
+ log.info("-- suspending trigger");
+ // suspend the trigger to avoid generating more events
+ String suspendTriggerCommand = "{" +
+ "'suspend-trigger' : {" +
+ "'name' : 'index_size_trigger4'" +
+ "}" +
+ "}";
+ req = createAutoScalingRequest(SolrRequest.METHOD.POST, suspendTriggerCommand);
+ response = solrClient.request(req);
+ assertEquals(response.get("result").toString(), "success");
+
assertEquals(1, listenerEvents.size());
List<CapturedEvent> events = listenerEvents.get("capturing4");
assertNotNull("'capturing4' events not found", events);
@@ -617,16 +631,8 @@
listenerEvents.clear();
finished = new CountDownLatch(1);
- // suspend the trigger first so that we can safely delete all docs
- String suspendTriggerCommand = "{" +
- "'suspend-trigger' : {" +
- "'name' : 'index_size_trigger4'" +
- "}" +
- "}";
- req = createAutoScalingRequest(SolrRequest.METHOD.POST, suspendTriggerCommand);
- response = solrClient.request(req);
- assertEquals(response.get("result").toString(), "success");
+ log.info("-- deleting documents");
for (int j = 0; j < 10; j++) {
UpdateRequest ureq = new UpdateRequest();
ureq.setParam("collection", collectionName);
@@ -635,6 +641,7 @@
}
solrClient.request(ureq);
}
+ cloudManager.getTimeSource().sleep(5000);
// make sure the actual index size is reduced by deletions, otherwise we may still violate aboveBytes
UpdateRequest ur = new UpdateRequest();
ur.setParam(UpdateParams.COMMIT, "true");
@@ -643,9 +650,28 @@
ur.setParam(UpdateParams.MAX_OPTIMIZE_SEGMENTS, "1");
ur.setParam(UpdateParams.WAIT_SEARCHER, "true");
ur.setParam(UpdateParams.OPEN_SEARCHER, "true");
+ log.info("-- requesting optimize / expungeDeletes / commit");
solrClient.request(ur, collectionName);
- // resume trigger
+ // wait for the segments to merge to reduce the index size
+ cloudManager.getTimeSource().sleep(50000);
+
+ // add some docs so that every shard gets an update
+ // we can reduce the number of docs here but this also works
+ for (int j = 0; j < 1; j++) {
+ UpdateRequest ureq = new UpdateRequest();
+ ureq.setParam("collection", collectionName);
+ for (int i = 0; i < 98; i++) {
+ ureq.add("id", "id-" + (i * 100) + "-" + j);
+ }
+ solrClient.request(ureq);
+ }
+
+ log.info("-- requesting commit");
+ solrClient.commit(collectionName, true, true);
+
+ // resume the trigger
+ log.info("-- resuming trigger");
req = createAutoScalingRequest(SolrRequest.METHOD.POST, resumeTriggerCommand);
response = solrClient.request(req);
assertEquals(response.get("result").toString(), "success");
@@ -654,6 +680,12 @@
await = finished.await(90000 / SPEED, TimeUnit.MILLISECONDS);
assertTrue("did not finish processing in time", await);
+ log.info("-- suspending trigger");
+ req = createAutoScalingRequest(SolrRequest.METHOD.POST, suspendTriggerCommand);
+ response = solrClient.request(req);
+ assertEquals(response.get("result").toString(), "success");
+// System.exit(-1);
+
assertEquals(1, listenerEvents.size());
events = listenerEvents.get("capturing4");
assertNotNull("'capturing4' events not found", events);
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/SplitShardSuggester.java b/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/SplitShardSuggester.java
index 559ff8a..8aafef8 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/SplitShardSuggester.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/SplitShardSuggester.java
@@ -47,7 +47,11 @@
}
Pair<String, String> collShard = shards.iterator().next();
Map<String, Object> params = (Map<String, Object>)hints.getOrDefault(Hint.PARAMS, Collections.emptyMap());
+ Float splitFuzz = (Float)params.get(CommonAdminParams.SPLIT_FUZZ);
CollectionAdminRequest.SplitShard req = CollectionAdminRequest.splitShard(collShard.first()).setShardName(collShard.second());
+ if (splitFuzz != null) {
+ req.setSplitFuzz(splitFuzz);
+ }
String splitMethod = (String)params.get(CommonAdminParams.SPLIT_METHOD);
if (splitMethod != null) {
req.setSplitMethod(splitMethod);
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/request/CollectionAdminRequest.java b/solr/solrj/src/java/org/apache/solr/client/solrj/request/CollectionAdminRequest.java
index 4f26984..3769c2e 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/request/CollectionAdminRequest.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/request/CollectionAdminRequest.java
@@ -1154,6 +1154,7 @@
protected String shard;
protected String splitMethod;
protected Integer numSubShards;
+ protected Float splitFuzz;
private Properties properties;
@@ -1183,6 +1184,15 @@
return splitMethod;
}
+ public SplitShard setSplitFuzz(float splitFuzz) {
+ this.splitFuzz = splitFuzz;
+ return this;
+ }
+
+ public Float getSplitFuzz() {
+ return splitFuzz;
+ }
+
public SplitShard setSplitKey(String splitKey) {
this.splitKey = splitKey;
return this;
diff --git a/solr/solrj/src/java/org/apache/solr/common/cloud/DocRouter.java b/solr/solrj/src/java/org/apache/solr/common/cloud/DocRouter.java
index 846c25e..c1471b5 100644
--- a/solr/solrj/src/java/org/apache/solr/common/cloud/DocRouter.java
+++ b/solr/solrj/src/java/org/apache/solr/common/cloud/DocRouter.java
@@ -30,6 +30,7 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Random;
import static org.apache.solr.common.cloud.DocCollection.DOC_ROUTER;
@@ -153,24 +154,59 @@
}
/**
- * Returns the range for each partition
+ * Split the range into partitions.
+ * @param partitions number of partitions
+ * @param range range to split
*/
public List<Range> partitionRange(int partitions, Range range) {
+ return partitionRange(partitions, range, 0.0f);
+ }
+
+ /**
+ * Split the range into partitions with inexact sizes.
+ * @param partitions number of partitions
+ * @param range range to split
+ * @param fuzz value between 0 (inclusive) and 0.5 indicating inexact split, i.e. percentage
+ * of variation in resulting ranges - odd ranges will be larger and even ranges will be smaller
+ * by up to that percentage.
+ */
+ public List<Range> partitionRange(int partitions, Range range, float fuzz) {
int min = range.min;
int max = range.max;
assert max >= min;
+ if (fuzz > 0.5f) {
+ throw new IllegalArgumentException("'fuzz' parameter must be <= 0.5f but was " + fuzz);
+ }
if (partitions == 0) return Collections.EMPTY_LIST;
long rangeSize = (long)max - (long)min;
long rangeStep = Math.max(1, rangeSize / partitions);
+ long fuzzStep = Math.round(rangeStep * fuzz / 2.0f);
+ // use a predictable pseudo-random
+ Random r = null;
+ if (fuzzStep > 2) {
+ r = new Random(0);
+ // limit randomness to half the range
+ fuzzStep = fuzzStep / 2;
+ }
List<Range> ranges = new ArrayList<>(partitions);
long start = min;
long end = start;
+ boolean odd = true;
while (end < max) {
end = start + rangeStep;
+ if (fuzzStep > 0) {
+ long currentFuzz = r != null ? fuzzStep + r.nextLong() % fuzzStep : fuzzStep;
+ if (odd) {
+ end = end + currentFuzz;
+ } else {
+ end = end - currentFuzz;
+ }
+ odd = !odd;
+ }
// make last range always end exactly on MAX_VALUE
if (ranges.size() == partitions - 1) {
end = max;
diff --git a/solr/solrj/src/java/org/apache/solr/common/params/CommonAdminParams.java b/solr/solrj/src/java/org/apache/solr/common/params/CommonAdminParams.java
index c080342..13d304c 100644
--- a/solr/solrj/src/java/org/apache/solr/common/params/CommonAdminParams.java
+++ b/solr/solrj/src/java/org/apache/solr/common/params/CommonAdminParams.java
@@ -27,8 +27,10 @@
String IN_PLACE_MOVE = "inPlaceMove";
/** Method to use for shard splitting. */
String SPLIT_METHOD = "splitMethod";
- /** **/
+ /** Number of sub-shards to create. **/
String NUM_SUB_SHARDS = "numSubShards";
/** Timeout for replicas to become active. */
String TIMEOUT = "timeout";
+ /** Inexact shard splitting factor. */
+ String SPLIT_FUZZ = "splitFuzz";
}