You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by va...@apache.org on 2016/04/17 18:27:26 UTC
lucene-solr:master: SchemaManager waits correctly for replicas to be
notified of a new change
Repository: lucene-solr
Updated Branches:
refs/heads/master 72cb73c6b -> 44c9cd2fe
SchemaManager waits correctly for replicas to be notified of a new change
Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/44c9cd2f
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/44c9cd2f
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/44c9cd2f
Branch: refs/heads/master
Commit: 44c9cd2fe8403e7b17e2706c241cb7268773e788
Parents: 72cb73c
Author: Varun Thacker <va...@gmail.com>
Authored: Sun Apr 17 20:48:09 2016 +0530
Committer: Varun Thacker <va...@gmail.com>
Committed: Sun Apr 17 21:56:58 2016 +0530
----------------------------------------------------------------------
solr/CHANGES.txt | 5 +-
.../apache/solr/cloud/ZkSolrResourceLoader.java | 41 ++++++--
.../org/apache/solr/schema/SchemaManager.java | 99 +++++++++---------
.../cloud-managed/conf/managed-schema | 31 ++++++
.../cloud-managed/conf/solrconfig.xml | 51 ++++++++++
.../configset-1/conf/schema-minimal.xml | 25 -----
.../configset-1/conf/solrconfig-minimal.xml | 56 ----------
.../solr/schema/TestManagedSchemaAPI.java | 101 +++++++++++++++++++
8 files changed, 267 insertions(+), 142 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/44c9cd2f/solr/CHANGES.txt
----------------------------------------------------------------------
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 2b6c868..8ae840a 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -120,7 +120,10 @@ Bug Fixes
(Nicolas Gavalda, Jorge Luis Betancourt Gonzalez via Mark Miller)
* SOLR-8946: bin/post failed to detect stdin usage on Ubuntu; maybe other unixes. (David Smiley)
-
+
+* SOLR-8662: SchemaManager waits correctly for replicas to be notified of a new change.
+ (sarowe, Noble Paul, Varun Thacker)
+
Optimizations
----------------------
* SOLR-8722: Don't force a full ZkStateReader refresh on every Overseer operation.
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/44c9cd2f/solr/core/src/java/org/apache/solr/cloud/ZkSolrResourceLoader.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/cloud/ZkSolrResourceLoader.java b/solr/core/src/java/org/apache/solr/cloud/ZkSolrResourceLoader.java
index 4d12db9..2b60e53 100644
--- a/solr/core/src/java/org/apache/solr/cloud/ZkSolrResourceLoader.java
+++ b/solr/core/src/java/org/apache/solr/cloud/ZkSolrResourceLoader.java
@@ -80,17 +80,42 @@ public class ZkSolrResourceLoader extends SolrResourceLoader {
*/
@Override
public InputStream openResource(String resource) throws IOException {
- InputStream is = null;
+ InputStream is;
String file = configSetZkPath + "/" + resource;
- try {
- if (zkController.pathExists(file)) {
- Stat stat = new Stat();
- byte[] bytes = zkController.getZkClient().getData(file, null, stat, true);
- return new ZkByteArrayInputStream(bytes, stat);
+ int maxTries = 10;
+ Exception exception = null;
+ while (maxTries -- > 0) {
+ try {
+ if (zkController.pathExists(file)) {
+ Stat stat = new Stat();
+ byte[] bytes = zkController.getZkClient().getData(file, null, stat, true);
+ return new ZkByteArrayInputStream(bytes, stat);
+ } else {
+ //Path does not exists. We only retry for session expired exceptions.
+ break;
+ }
+ } catch (KeeperException.SessionExpiredException e) {
+ exception = e;
+ // Retry in case of session expiry
+ try {
+ Thread.sleep(1000);
+ log.debug("Sleeping for 1s before retrying fetching resource=" + resource);
+ } catch (InterruptedException ie) {
+ Thread.currentThread().interrupt();
+ throw new IOException("Could not load resource=" + resource, ie);
+ }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new IOException("Error opening " + file, e);
+ } catch (KeeperException e) {
+ throw new IOException("Error opening " + file, e);
}
- } catch (Exception e) {
- throw new IOException("Error opening " + file, e);
}
+
+ if (exception != null) {
+ throw new IOException("We re-tried 10 times but was still unable to fetch resource=" + resource + " from ZK", exception);
+ }
+
try {
// delegate to the class loader (looking into $INSTANCE_DIR/lib jars)
is = classLoader.getResourceAsStream(resource.replace(File.separatorChar, '/'));
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/44c9cd2f/solr/core/src/java/org/apache/solr/schema/SchemaManager.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/schema/SchemaManager.java b/solr/core/src/java/org/apache/solr/schema/SchemaManager.java
index e70b84f..3b492a7 100644
--- a/solr/core/src/java/org/apache/solr/schema/SchemaManager.java
+++ b/solr/core/src/java/org/apache/solr/schema/SchemaManager.java
@@ -25,6 +25,7 @@ import org.apache.solr.core.SolrResourceLoader;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.rest.BaseSolrResource;
import org.apache.solr.util.CommandOperation;
+import org.apache.solr.util.TimeOut;
import org.apache.zookeeper.KeeperException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -86,20 +87,27 @@ public class SchemaManager {
if (!errs.isEmpty()) return errs;
IndexSchema schema = req.getCore().getLatestSchema();
- if (!(schema instanceof ManagedIndexSchema)) {
+ if (schema instanceof ManagedIndexSchema && schema.isMutable()) {
+ synchronized (schema.getSchemaUpdateLock()) {
+ return doOperations(ops);
+ }
+ } else {
return singletonList(singletonMap(CommandOperation.ERR_MSGS, "schema is not editable"));
}
- synchronized (schema.getSchemaUpdateLock()) {
- return doOperations(ops);
- }
}
private List doOperations(List<CommandOperation> operations) throws InterruptedException, IOException, KeeperException {
- int timeout = req.getParams().getInt(BaseSolrResource.UPDATE_TIMEOUT_SECS, -1);
- long startTime = System.nanoTime();
- long endTime = timeout > 0 ? System.nanoTime() + (timeout * 1000 * 1000) : Long.MAX_VALUE;
+ //The default timeout is 10 minutes when no BaseSolrResource.UPDATE_TIMEOUT_SECS is specified
+ int timeout = req.getParams().getInt(BaseSolrResource.UPDATE_TIMEOUT_SECS, 600);
+
+ //If BaseSolrResource.UPDATE_TIMEOUT_SECS=0 or -1 then end time then we'll try for 10 mins ( default timeout )
+ if (timeout < 1) {
+ timeout = 600;
+ }
+ TimeOut timeOut = new TimeOut(timeout, TimeUnit.SECONDS);
SolrCore core = req.getCore();
- while (System.nanoTime() < endTime) {
+ String errorMsg = "Unable to persist managed schema. ";
+ while (!timeOut.hasTimedOut()) {
managedIndexSchema = getFreshManagedSchema();
for (CommandOperation op : operations) {
OpType opType = OpType.get(op.name);
@@ -118,25 +126,18 @@ public class SchemaManager {
try {
managedIndexSchema.persist(sw);
} catch (IOException e) {
- log.info("race condition ");
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "unable to serialize schema");
//unlikely
}
try {
- ZkController.persistConfigResourceToZooKeeper(zkLoader,
- managedIndexSchema.getSchemaZkVersion(),
- managedIndexSchema.getResourceName(),
- sw.toString().getBytes(StandardCharsets.UTF_8),
- true);
- waitForOtherReplicasToUpdate(timeout, startTime);
+ ZkController.persistConfigResourceToZooKeeper(zkLoader, managedIndexSchema.getSchemaZkVersion(),
+ managedIndexSchema.getResourceName(), sw.toString().getBytes(StandardCharsets.UTF_8), true);
+ waitForOtherReplicasToUpdate(timeOut);
+ core.setLatestSchema(managedIndexSchema);
return Collections.emptyList();
} catch (ZkController.ResourceModifiedInZkException e) {
- log.info("Race condition schema modified by another node");
- } catch (Exception e) {
- String s = "Exception persisting schema";
- log.warn(s, e);
- return singletonList(s + e.getMessage());
+ log.info("Schema was modified by another node. Retrying..");
}
} else {
try {
@@ -144,36 +145,30 @@ public class SchemaManager {
managedIndexSchema.persistManagedSchema(false);
core.setLatestSchema(managedIndexSchema);
return Collections.emptyList();
- } catch (ManagedIndexSchema.SchemaChangedInZkException e) {
- String s = "Failed to update schema because schema is modified";
- log.warn(s, e);
- } catch (Exception e) {
- String s = "Exception persisting schema";
- log.warn(s, e);
- return singletonList(s + e.getMessage());
+ } catch (SolrException e) {
+ log.warn(errorMsg);
+ return singletonList(errorMsg + e.getMessage());
}
}
}
- return singletonList("Unable to persist schema");
+ log.warn(errorMsg + "Timed out.");
+ return singletonList(errorMsg + "Timed out.");
}
- private void waitForOtherReplicasToUpdate(int timeout, long startTime) {
- if (timeout > 0 && managedIndexSchema.getResourceLoader() instanceof ZkSolrResourceLoader) {
- CoreDescriptor cd = req.getCore().getCoreDescriptor();
- String collection = cd.getCollectionName();
- if (collection != null) {
- ZkSolrResourceLoader zkLoader = (ZkSolrResourceLoader) managedIndexSchema.getResourceLoader();
- long timeLeftSecs = timeout - TimeUnit.SECONDS.convert(System.nanoTime() - startTime, TimeUnit.NANOSECONDS);
- if (timeLeftSecs <= 0) {
- throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
- "Not enough time left to update replicas. However, the schema is updated already.");
- }
- ManagedIndexSchema.waitForSchemaZkVersionAgreement(collection,
- cd.getCloudDescriptor().getCoreNodeName(),
- (managedIndexSchema).getSchemaZkVersion(),
- zkLoader.getZkController(),
- (int) timeLeftSecs);
+ private void waitForOtherReplicasToUpdate(TimeOut timeOut) {
+ CoreDescriptor cd = req.getCore().getCoreDescriptor();
+ String collection = cd.getCollectionName();
+ if (collection != null) {
+ ZkSolrResourceLoader zkLoader = (ZkSolrResourceLoader) managedIndexSchema.getResourceLoader();
+ if (timeOut.hasTimedOut()) {
+ throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
+ "Not enough time left to update replicas. However, the schema is updated already.");
}
+ ManagedIndexSchema.waitForSchemaZkVersionAgreement(collection,
+ cd.getCloudDescriptor().getCoreNodeName(),
+ (managedIndexSchema).getSchemaZkVersion(),
+ zkLoader.getZkController(),
+ (int) timeOut.timeLeft(TimeUnit.SECONDS));
}
}
@@ -198,7 +193,7 @@ public class SchemaManager {
@Override public boolean perform(CommandOperation op, SchemaManager mgr) {
String src = op.getStr(SOURCE);
List<String> dests = op.getStrs(DESTINATION);
-
+
int maxChars = CopyField.UNLIMITED; // If maxChars is not specified, there is no limit on copied chars
String maxCharsStr = op.getStr(MAX_CHARS, null);
if (null != maxCharsStr) {
@@ -241,7 +236,7 @@ public class SchemaManager {
}
try {
SchemaField field = SchemaField.create(name, ft, op.getValuesExcluding(NAME, TYPE));
- mgr.managedIndexSchema
+ mgr.managedIndexSchema
= mgr.managedIndexSchema.addFields(singletonList(field), Collections.emptyMap(), false);
return true;
} catch (Exception e) {
@@ -262,8 +257,8 @@ public class SchemaManager {
return false;
}
try {
- SchemaField field = SchemaField.create(name, ft, op.getValuesExcluding(NAME, TYPE));
- mgr.managedIndexSchema
+ SchemaField field = SchemaField.create(name, ft, op.getValuesExcluding(NAME, TYPE));
+ mgr.managedIndexSchema
= mgr.managedIndexSchema.addDynamicFields(singletonList(field), Collections.emptyMap(), false);
return true;
} catch (Exception e) {
@@ -297,7 +292,7 @@ public class SchemaManager {
if (op.hasError())
return false;
if ( ! op.getValuesExcluding(SOURCE, DESTINATION).isEmpty()) {
- op.addError("Only the '" + SOURCE + "' and '" + DESTINATION
+ op.addError("Only the '" + SOURCE + "' and '" + DESTINATION
+ "' params are allowed with the 'delete-copy-field' operation");
return false;
}
@@ -318,14 +313,14 @@ public class SchemaManager {
if ( ! op.getValuesExcluding(NAME).isEmpty()) {
op.addError("Only the '" + NAME + "' param is allowed with the 'delete-field' operation");
return false;
- }
+ }
try {
mgr.managedIndexSchema = mgr.managedIndexSchema.deleteFields(singleton(name));
return true;
} catch (Exception e) {
op.addError(getErrorStr(e));
return false;
- }
+ }
}
},
DELETE_DYNAMIC_FIELD("delete-dynamic-field") {
@@ -436,7 +431,7 @@ public class SchemaManager {
int version = ((ZkSolrResourceLoader.ZkByteArrayInputStream) in).getStat().getVersion();
log.info("managed schema loaded . version : {} ", version);
return new ManagedIndexSchema
- (req.getCore().getSolrConfig(), req.getSchema().getResourceName(), new InputSource(in),
+ (req.getCore().getSolrConfig(), req.getSchema().getResourceName(), new InputSource(in),
true, req.getSchema().getResourceName(), version, req.getSchema().getSchemaUpdateLock());
} else {
return (ManagedIndexSchema) req.getCore().getLatestSchema();
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/44c9cd2f/solr/core/src/test-files/solr/configsets/cloud-managed/conf/managed-schema
----------------------------------------------------------------------
diff --git a/solr/core/src/test-files/solr/configsets/cloud-managed/conf/managed-schema b/solr/core/src/test-files/solr/configsets/cloud-managed/conf/managed-schema
new file mode 100644
index 0000000..fd7be83
--- /dev/null
+++ b/solr/core/src/test-files/solr/configsets/cloud-managed/conf/managed-schema
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+ 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.
+-->
+<schema name="minimal" version="1.1">
+ <types>
+ <fieldType name="string" class="solr.StrField"/>
+ <fieldType name="int" class="solr.TrieIntField" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
+ <fieldType name="long" class="solr.TrieLongField" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
+ </types>
+ <fields>
+ <!-- for versioning -->
+ <field name="_version_" type="long" indexed="true" stored="true"/>
+ <field name="_root_" type="int" indexed="true" stored="true" multiValued="false" required="false"/>
+ <field name="id" type="string" indexed="true" stored="true"/>
+ </fields>
+ <uniqueKey>id</uniqueKey>
+</schema>
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/44c9cd2f/solr/core/src/test-files/solr/configsets/cloud-managed/conf/solrconfig.xml
----------------------------------------------------------------------
diff --git a/solr/core/src/test-files/solr/configsets/cloud-managed/conf/solrconfig.xml b/solr/core/src/test-files/solr/configsets/cloud-managed/conf/solrconfig.xml
new file mode 100644
index 0000000..aabfa2f
--- /dev/null
+++ b/solr/core/src/test-files/solr/configsets/cloud-managed/conf/solrconfig.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" ?>
+
+<!--
+ 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.
+-->
+
+<!-- Minimal solrconfig.xml with /select, /admin and /update only -->
+
+<config>
+
+ <dataDir>${solr.data.dir:}</dataDir>
+
+ <directoryFactory name="DirectoryFactory"
+ class="${solr.directoryFactory:solr.NRTCachingDirectoryFactory}"/>
+
+ <schemaFactory class="ManagedIndexSchemaFactory">
+ <bool name="mutable">${managed.schema.mutable}</bool>
+ <str name="managedSchemaResourceName">managed-schema</str>
+ </schemaFactory>
+
+ <luceneMatchVersion>${tests.luceneMatchVersion:LATEST}</luceneMatchVersion>
+
+ <updateHandler class="solr.DirectUpdateHandler2">
+ <commitWithin>
+ <softCommit>${solr.commitwithin.softcommit:true}</softCommit>
+ </commitWithin>
+ <updateLog></updateLog>
+ </updateHandler>
+
+ <requestHandler name="/select" class="solr.SearchHandler">
+ <lst name="defaults">
+ <str name="echoParams">explicit</str>
+ <str name="indent">true</str>
+ <str name="df">text</str>
+ </lst>
+
+ </requestHandler>
+</config>
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/44c9cd2f/solr/core/src/test-files/solr/configsets/configset-1/conf/schema-minimal.xml
----------------------------------------------------------------------
diff --git a/solr/core/src/test-files/solr/configsets/configset-1/conf/schema-minimal.xml b/solr/core/src/test-files/solr/configsets/configset-1/conf/schema-minimal.xml
deleted file mode 100644
index 9e2f947..0000000
--- a/solr/core/src/test-files/solr/configsets/configset-1/conf/schema-minimal.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
- 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.
--->
-<schema name="minimal" version="1.1">
- <types>
- <fieldType name="string" class="solr.StrField"/>
- </types>
- <fields>
- <dynamicField name="*" type="string" indexed="true" stored="true" />
- </fields>
-</schema>
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/44c9cd2f/solr/core/src/test-files/solr/configsets/configset-1/conf/solrconfig-minimal.xml
----------------------------------------------------------------------
diff --git a/solr/core/src/test-files/solr/configsets/configset-1/conf/solrconfig-minimal.xml b/solr/core/src/test-files/solr/configsets/configset-1/conf/solrconfig-minimal.xml
deleted file mode 100644
index a6fe5ba..0000000
--- a/solr/core/src/test-files/solr/configsets/configset-1/conf/solrconfig-minimal.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-<?xml version="1.0" ?>
-
-<!--
- 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.
--->
-
-<!-- This is a "kitchen sink" config file that tests can use.
- When writting a new test, feel free to add *new* items (plugins,
- config options, etc...) as long as they don't break any existing
- tests. if you need to test something esoteric please add a new
- "solrconfig-your-esoteric-purpose.xml" config file.
-
- Note in particular that this test is used by MinimalSchemaTest so
- Anything added to this file needs to work correctly even if there
- is now uniqueKey or defaultSearch Field.
- -->
-
-<config>
-
- <dataDir>${solr.data.dir:}</dataDir>
-
- <directoryFactory name="DirectoryFactory"
- class="${solr.directoryFactory:solr.NRTCachingDirectoryFactory}"/>
- <schemaFactory class="ClassicIndexSchemaFactory"/>
-
- <luceneMatchVersion>${tests.luceneMatchVersion:LATEST}</luceneMatchVersion>
-
- <updateHandler class="solr.DirectUpdateHandler2">
- <commitWithin>
- <softCommit>${solr.commitwithin.softcommit:true}</softCommit>
- </commitWithin>
-
- </updateHandler>
- <requestHandler name="/select" class="solr.SearchHandler">
- <lst name="defaults">
- <str name="echoParams">explicit</str>
- <str name="indent">true</str>
- <str name="df">text</str>
- </lst>
-
- </requestHandler>
-</config>
-
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/44c9cd2f/solr/core/src/test/org/apache/solr/schema/TestManagedSchemaAPI.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/schema/TestManagedSchemaAPI.java b/solr/core/src/test/org/apache/solr/schema/TestManagedSchemaAPI.java
new file mode 100644
index 0000000..3bd4dea
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/schema/TestManagedSchemaAPI.java
@@ -0,0 +1,101 @@
+package org.apache.solr.schema;
+
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.solr.client.solrj.SolrServerException;
+import org.apache.solr.client.solrj.impl.CloudSolrClient;
+import org.apache.solr.client.solrj.request.CollectionAdminRequest;
+import org.apache.solr.client.solrj.request.UpdateRequest;
+import org.apache.solr.client.solrj.request.schema.SchemaRequest;
+import org.apache.solr.client.solrj.response.CollectionAdminResponse;
+import org.apache.solr.client.solrj.response.schema.SchemaResponse;
+import org.apache.solr.cloud.SolrCloudTestCase;
+import org.apache.solr.common.SolrInputDocument;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/*
+ * 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.
+ */
+
+public class TestManagedSchemaAPI extends SolrCloudTestCase {
+ private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+ @BeforeClass
+ public static void createCluster() throws Exception {
+ System.setProperty("managed.schema.mutable", "true");
+ configureCluster(2)
+ .addConfig("conf1", TEST_PATH().resolve("configsets").resolve("cloud-managed").resolve("conf"))
+ .configure();
+ }
+
+ @Test
+ public void test() throws Exception {
+ String collection = "testschemaapi";
+ cluster.createCollection(collection, 1, 2, "conf1", null);
+ testReloadAndAddSimple(collection);
+ testAddFieldAndDocument(collection);
+ }
+
+ private void testReloadAndAddSimple(String collection) throws IOException, SolrServerException {
+ CloudSolrClient cloudClient = cluster.getSolrClient();
+
+ String fieldName = "myNewField";
+ addStringField(fieldName, collection, cloudClient);
+
+ CollectionAdminRequest.Reload reloadRequest = CollectionAdminRequest.reloadCollection(collection);
+ CollectionAdminResponse response = reloadRequest.process(cloudClient);
+ assertEquals(0, response.getStatus());
+ assertTrue(response.isSuccess());
+
+ SolrInputDocument doc = new SolrInputDocument();
+ doc.addField("id", "1");
+ doc.addField(fieldName, "val");
+ UpdateRequest ureq = new UpdateRequest().add(doc);
+ cloudClient.request(ureq, collection);
+ }
+
+ private void testAddFieldAndDocument(String collection) throws IOException, SolrServerException {
+ CloudSolrClient cloudClient = cluster.getSolrClient();
+
+ String fieldName = "myNewField1";
+ addStringField(fieldName, collection, cloudClient);
+
+ SolrInputDocument doc = new SolrInputDocument();
+ doc.addField("id", "2");
+ doc.addField(fieldName, "val1");
+ UpdateRequest ureq = new UpdateRequest().add(doc);
+ cloudClient.request(ureq, collection);;
+ }
+
+ private void addStringField(String fieldName, String collection, CloudSolrClient cloudClient) throws IOException, SolrServerException {
+ Map<String, Object> fieldAttributes = new LinkedHashMap<>();
+ fieldAttributes.put("name", fieldName);
+ fieldAttributes.put("type", "string");
+ SchemaRequest.AddField addFieldUpdateSchemaRequest = new SchemaRequest.AddField(fieldAttributes);
+ SchemaResponse.UpdateResponse addFieldResponse = addFieldUpdateSchemaRequest.process(cloudClient, collection);
+ assertEquals(0, addFieldResponse.getStatus());
+ assertNull(addFieldResponse.getResponse().get("errors"));
+
+ log.info("added new field="+fieldName);
+ }
+
+}