Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SOLR-16959: Make CoresLocator class configurable #1891

Merged
merged 9 commits into from
Sep 27, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions solr/core/src/java/org/apache/solr/core/CoreContainer.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
import static org.apache.solr.common.params.CommonParams.METRICS_PATH;
import static org.apache.solr.common.params.CommonParams.ZK_PATH;
import static org.apache.solr.common.params.CommonParams.ZK_STATUS_PATH;
import static org.apache.solr.core.CorePropertiesLocator.PROPERTIES_FILENAME;
import static org.apache.solr.security.AuthenticationPlugin.AUTHENTICATION_PLUGIN_PROP;

import com.github.benmanes.caffeine.cache.Interner;
Expand Down Expand Up @@ -377,21 +376,24 @@ public CoreContainer(Path solrHome, Properties properties) {
* @see #load()
*/
public CoreContainer(NodeConfig config) {
this(config, new CorePropertiesLocator(config.getCoreRootDirectory()));
this(config, CoresLocator.instantiate(config));
}

public CoreContainer(NodeConfig config, boolean asyncSolrCoreLoad) {
this(config, new CorePropertiesLocator(config.getCoreRootDirectory()), asyncSolrCoreLoad);
this(config, CoresLocator.instantiate(config), asyncSolrCoreLoad);
}

/**
* Create a new CoreContainer using the given configuration and locator. The container's cores are
* not loaded.
* Create a new CoreContainer using the given configuration and locator.
*
* <p>The container's cores are not loaded. This constructor should be used only in tests, as it
* overrides {@link CoresLocator}'s instantiation process.
*
* @param config a ConfigSolr representation of this container's configuration
* @param locator a CoresLocator
* @see #load()
*/
@VisibleForTesting
public CoreContainer(NodeConfig config, CoresLocator locator) {
this(config, locator, false);
}
Expand Down Expand Up @@ -1945,9 +1947,7 @@ private CoreDescriptor reloadCoreDescriptor(CoreDescriptor oldDesc) {
return null;
}

CorePropertiesLocator cpl = new CorePropertiesLocator(null);
CoreDescriptor ret =
cpl.buildCoreDescriptor(oldDesc.getInstanceDir().resolve(PROPERTIES_FILENAME), this);
CoreDescriptor ret = getCoresLocator().reload(oldDesc, this);

// Ok, this little jewel is all because we still create core descriptors on the fly from lists
// of properties in tests particularly. Theoretically, there should be _no_ way to create a
Expand Down
11 changes: 11 additions & 0 deletions solr/core/src/java/org/apache/solr/core/CorePropertiesLocator.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/
package org.apache.solr.core;

import com.google.common.annotations.VisibleForTesting;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
Expand Down Expand Up @@ -51,6 +52,11 @@ public class CorePropertiesLocator implements CoresLocator {

private final Path rootDirectory;

public CorePropertiesLocator(NodeConfig nodeConfig) {
this(nodeConfig.getCoreRootDirectory());
}

@VisibleForTesting
public CorePropertiesLocator(Path coreDiscoveryRoot) {
this.rootDirectory = coreDiscoveryRoot;
log.debug("Config-defined core root directory: {}", this.rootDirectory);
Expand Down Expand Up @@ -193,6 +199,11 @@ public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOExce
return cds;
}

@Override
public CoreDescriptor reload(CoreDescriptor cd, CoreContainer cc) {
return buildCoreDescriptor(cd.getInstanceDir().resolve(PROPERTIES_FILENAME), cc);
}

protected CoreDescriptor buildCoreDescriptor(Path propertiesFile, CoreContainer cc) {
if (Files.notExists(propertiesFile)) {
// This can happen in tests, see CoreContainer#reloadCoreDescriptor
Expand Down
44 changes: 38 additions & 6 deletions solr/core/src/java/org/apache/solr/core/CoresLocator.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/
package org.apache.solr.core;

import java.lang.reflect.Constructor;
import java.util.List;

/** Manage the discovery and persistence of core definitions across Solr restarts */
Expand All @@ -27,7 +28,7 @@ public interface CoresLocator {
* @param cc the CoreContainer
* @param coreDescriptors CoreDescriptors to persist
*/
public void create(CoreContainer cc, CoreDescriptor... coreDescriptors);
void create(CoreContainer cc, CoreDescriptor... coreDescriptors);

/**
* Ensure that the core definitions from the passed in CoreDescriptors will persist across
Expand All @@ -36,7 +37,7 @@ public interface CoresLocator {
* @param cc the CoreContainer
* @param coreDescriptors CoreDescriptors to persist
*/
public void persist(CoreContainer cc, CoreDescriptor... coreDescriptors);
void persist(CoreContainer cc, CoreDescriptor... coreDescriptors);

/**
* Ensure that the core definitions from the passed in CoreDescriptors are not available for
Expand All @@ -45,7 +46,7 @@ public interface CoresLocator {
* @param cc the CoreContainer
* @param coreDescriptors CoreDescriptors of the cores to remove
*/
public void delete(CoreContainer cc, CoreDescriptor... coreDescriptors);
void delete(CoreContainer cc, CoreDescriptor... coreDescriptors);

/**
* Persist the new name of a renamed core
Expand All @@ -54,7 +55,7 @@ public interface CoresLocator {
* @param oldCD the CoreDescriptor of the core before renaming
* @param newCD the CoreDescriptor of the core after renaming
*/
public void rename(CoreContainer cc, CoreDescriptor oldCD, CoreDescriptor newCD);
void rename(CoreContainer cc, CoreDescriptor oldCD, CoreDescriptor newCD);

/**
* Swap two core definitions
Expand All @@ -63,13 +64,44 @@ public interface CoresLocator {
* @param cd1 the core descriptor of the first core, after swapping
* @param cd2 the core descriptor of the second core, after swapping
*/
public void swap(CoreContainer cc, CoreDescriptor cd1, CoreDescriptor cd2);
void swap(CoreContainer cc, CoreDescriptor cd1, CoreDescriptor cd2);

/**
* Load all the CoreDescriptors from persistence store
*
* @param cc the CoreContainer
* @return a list of all CoreDescriptors found
*/
public List<CoreDescriptor> discover(CoreContainer cc);
List<CoreDescriptor> discover(CoreContainer cc);

/**
* Reload a core descriptor.
*
* @param cd the old core descriptor
* @param cc the CoreContainer
* @return a new core descriptor
*/
CoreDescriptor reload(CoreDescriptor cd, CoreContainer cc);

/**
* Returns a new instance of {@link CoresLocator}, depending on provided config.
*
* @param nodeConfig Solr configuration.
*/
static CoresLocator instantiate(NodeConfig nodeConfig) {
final String coresLocatorClass = nodeConfig.getCoresLocatorClass();
if (coresLocatorClass != null) {
try {
Class<? extends CoresLocator> clazz =
nodeConfig.getSolrResourceLoader().findClass(coresLocatorClass, CoresLocator.class);
Constructor<? extends CoresLocator> constructor = clazz.getConstructor(NodeConfig.class);
return constructor.newInstance(nodeConfig);
} catch (Exception e) {
throw new RuntimeException(
"create CoresLocator instance failed, coresLocatorClass=" + coresLocatorClass, e);
}
} else {
return new CorePropertiesLocator(nodeConfig);
}
}
}
16 changes: 15 additions & 1 deletion solr/core/src/java/org/apache/solr/core/NodeConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ public class NodeConfig {
private final UpdateShardHandlerConfig updateShardHandlerConfig;

private final String configSetServiceClass;
private final String coresLocatorClass;

private final String coreAdminHandlerClass;

Expand Down Expand Up @@ -123,6 +124,7 @@ public class NodeConfig {
private NodeConfig(
String nodeName,
Path coreRootDirectory,
String coresLocatorClass,
Path solrDataHome,
Integer booleanQueryMaxClauseCount,
Path configSetBaseDirectory,
Expand Down Expand Up @@ -159,6 +161,7 @@ private NodeConfig(
// all Path params here are absolute and normalized.
this.nodeName = nodeName;
this.coreRootDirectory = coreRootDirectory;
this.coresLocatorClass = coresLocatorClass;
this.solrDataHome = solrDataHome;
this.booleanQueryMaxClauseCount = booleanQueryMaxClauseCount;
this.configSetBaseDirectory = configSetBaseDirectory;
Expand Down Expand Up @@ -267,6 +270,10 @@ public Path getCoreRootDirectory() {
return coreRootDirectory;
}

public String getCoresLocatorClass() {
return this.coresLocatorClass;
}

/** Absolute. */
public Path getSolrDataHome() {
return solrDataHome;
Expand Down Expand Up @@ -584,6 +591,7 @@ public static class NodeConfigBuilder {
// all Path fields here are absolute and normalized.
private SolrResourceLoader loader;
private Path coreRootDirectory;
private String coresLocatorClass;
private Path solrDataHome;
private Integer booleanQueryMaxClauseCount;
private Path configSetBaseDirectory;
Expand Down Expand Up @@ -662,6 +670,11 @@ public NodeConfigBuilder setCoreRootDirectory(String coreRootDirectory) {
return this;
}

public NodeConfigBuilder setCoresLocatorClass(String coresLocatorClass) {
this.coresLocatorClass = coresLocatorClass;
return this;
}

public NodeConfigBuilder setSolrDataHome(String solrDataHomeString) {
// keep it null unless explicitly set to non-empty value
if (solrDataHomeString != null && !solrDataHomeString.isEmpty()) {
Expand Down Expand Up @@ -746,8 +759,8 @@ public NodeConfigBuilder setReplayUpdatesThreads(int replayUpdatesThreads) {
this.replayUpdatesThreads = replayUpdatesThreads;
return this;
}

// Remove in Solr 10.0

@Deprecated
public NodeConfigBuilder setTransientCacheSize(int transientCacheSize) {
this.transientCacheSize = transientCacheSize;
Expand Down Expand Up @@ -872,6 +885,7 @@ public NodeConfig build() {
return new NodeConfig(
nodeName,
coreRootDirectory,
coresLocatorClass,
solrDataHome,
booleanQueryMaxClauseCount,
configSetBaseDirectory,
Expand Down
3 changes: 3 additions & 0 deletions solr/core/src/java/org/apache/solr/core/SolrXmlConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,9 @@ private static NodeConfig fillSolrSection(NodeConfig.NodeConfigBuilder builder,
case "configSetService":
builder.setConfigSetServiceClass(it.txt());
break;
case "coresLocator":
builder.setCoresLocatorClass(it.txt());
break;
case "coreRootDirectory":
builder.setCoreRootDirectory(it.txt());
break;
Expand Down
71 changes: 71 additions & 0 deletions solr/core/src/test/org/apache/solr/core/CoresLocatorTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* 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 static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;

import org.apache.solr.util.NoCoresLocator;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;

/** Unit tests for {@link CoresLocator}. */
public class CoresLocatorTest {

@Rule public TemporaryFolder temporaryFolder = new TemporaryFolder();

@Test
public void testInstantiateDefaultCoresLocator() throws Exception {
String solrXml = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?><solr></solr>";
NodeConfig nodeConfig = SolrXmlConfig.fromString(temporaryFolder.newFolder().toPath(), solrXml);
CoresLocator coresLocator = CoresLocator.instantiate(nodeConfig);

assertTrue(coresLocator instanceof CorePropertiesLocator);
}

@Test
public void testInstantiateCustomCoresLocator() throws Exception {
String solrXml =
"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
+ "<solr>\n"
+ "<str name=\"coresLocator\">org.apache.solr.util.NoCoresLocator</str>\n"
+ "</solr>";
NodeConfig nodeConfig = SolrXmlConfig.fromString(temporaryFolder.newFolder().toPath(), solrXml);
CoresLocator coresLocator = CoresLocator.instantiate(nodeConfig);

assertTrue(coresLocator instanceof NoCoresLocator);
assertSame(nodeConfig, ((NoCoresLocator) coresLocator).getNodeConfig());
}

@Test
public void testInstantiateUnknownCoresLocator() throws Exception {
String solrXml =
"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
+ "<solr>\n"
+ "<str name=\"coresLocator\">foo.BarCoresLocator</str>\n"
+ "</solr>";
NodeConfig nodeConfig = SolrXmlConfig.fromString(temporaryFolder.newFolder().toPath(), solrXml);
Throwable t = assertThrows(RuntimeException.class, () -> CoresLocator.instantiate(nodeConfig));

assertEquals(
"create CoresLocator instance failed, coresLocatorClass=foo.BarCoresLocator",
t.getMessage());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -799,6 +799,11 @@ public void swap(CoreContainer cc, CoreDescriptor cd1, CoreDescriptor cd2) {}
public List<CoreDescriptor> discover(CoreContainer cc) {
return cores;
}

@Override
public CoreDescriptor reload(CoreDescriptor cd, CoreContainer cc) {
return cd;
}
}

@Test
Expand Down
2 changes: 1 addition & 1 deletion solr/core/src/test/org/apache/solr/core/TestLazyCores.java
Original file line number Diff line number Diff line change
Expand Up @@ -743,7 +743,7 @@ private CoreContainer initGoodAndBad(
NodeConfig config = SolrXmlConfig.fromString(solrHomeDirectory.toPath(), "<solr/>");

// OK this should succeed, but at the end we should have recorded a series of errors.
return createCoreContainer(config, new CorePropertiesLocator(config.getCoreRootDirectory()));
return createCoreContainer(config, new CorePropertiesLocator(config));
}

// We want to see that the core "heals itself" if an un-corrupted file is written to the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ The tables below list the child nodes of each XML element in `solr.xml`.
+
This attribute does not need to be set.
+
If used, this attribute should be set to the FQN (Fully qualified name) of a class that inherits from `ConfigSetService`, and you must provide a constructor with one parameter which the type is `org.apache.solr.core.CoreContainer`.
If used, this attribute should be set to the FQN (Fully qualified name) of a class that inherits from `ConfigSetService`, and you must provide a constructor with one parameter of type `org.apache.solr.core.CoreContainer`.
For example, `<str name="configSetService">com.myorg.CustomConfigSetService</str>`.
+
If this attribute isn't set, Solr uses the default `configSetService`, with zookeeper aware of `org.apache.solr.cloud.ZkConfigSetService`, without zookeeper aware of `org.apache.solr.core.FileSystemConfigSetService`.
Expand Down Expand Up @@ -188,6 +188,18 @@ The default value is equal to the number of processors.
+
The root of the core discovery tree, defaults to `$SOLR_HOME`.

`coresLocator`::
+
[%autowidth,frame=none]
|===
|Optional |Default: `org.apache.solr.core.CoresPropertiesLocator`
|===
+
This attribute does not need to be set.
+
If used, this attribute should be set to the FQN (Fully qualified name) of a class that implements `CoresLocator`, and you must provide a constructor with one parameter of type `org.apache.solr.core.NodeConfig`.
For example, `<str name="coresLocator">com.myorg.CustomCoresLocator</str>`.

`managementPath`::
+
[%autowidth,frame=none]
Expand Down
Loading