Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id B69F0200B94 for ; Sun, 2 Oct 2016 20:45:27 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id B5310160AD8; Sun, 2 Oct 2016 18:45:27 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id A7334160AC4 for ; Sun, 2 Oct 2016 20:45:26 +0200 (CEST) Received: (qmail 65222 invoked by uid 500); 2 Oct 2016 18:45:25 -0000 Mailing-List: contact commits-help@lucene.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@lucene.apache.org Delivered-To: mailing list commits@lucene.apache.org Received: (qmail 65213 invoked by uid 99); 2 Oct 2016 18:45:25 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 02 Oct 2016 18:45:25 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id AF5A3E008F; Sun, 2 Oct 2016 18:45:25 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: mkhl@apache.org To: commits@lucene.apache.org Message-Id: <9a8846b0e69944178d66bdd3f64d9a22@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: lucene-solr:branch_6x: SOLR-9554: adding a test for concurrent schema upgrade in cloud. Date: Sun, 2 Oct 2016 18:45:25 +0000 (UTC) archived-at: Sun, 02 Oct 2016 18:45:27 -0000 Repository: lucene-solr Updated Branches: refs/heads/branch_6x a2e24d1fc -> cc1841cbb SOLR-9554: adding a test for concurrent schema upgrade in cloud. Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/cc1841cb Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/cc1841cb Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/cc1841cb Branch: refs/heads/branch_6x Commit: cc1841cbb73f1c5c54208549cb20cf8244fa5104 Parents: a2e24d1 Author: Mikhail Khludnev Authored: Wed Sep 28 12:33:15 2016 +0300 Committer: Mikhail Khludnev Committed: Sun Oct 2 21:33:01 2016 +0300 ---------------------------------------------------------------------- .../cloud-managed-upgrade/conf/schema.xml | 27 ++++ .../cloud-managed-upgrade/conf/solrconfig.xml | 50 ++++++ .../schema/TestManagedSchemaThreadSafety.java | 162 +++++++++++++++++++ .../java/org/apache/solr/SolrTestCaseJ4.java | 4 + .../apache/solr/cloud/SolrCloudTestCase.java | 4 - 5 files changed, 243 insertions(+), 4 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/cc1841cb/solr/core/src/test-files/solr/configsets/cloud-managed-upgrade/conf/schema.xml ---------------------------------------------------------------------- diff --git a/solr/core/src/test-files/solr/configsets/cloud-managed-upgrade/conf/schema.xml b/solr/core/src/test-files/solr/configsets/cloud-managed-upgrade/conf/schema.xml new file mode 100644 index 0000000..b9f09f9 --- /dev/null +++ b/solr/core/src/test-files/solr/configsets/cloud-managed-upgrade/conf/schema.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + id + http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/cc1841cb/solr/core/src/test-files/solr/configsets/cloud-managed-upgrade/conf/solrconfig.xml ---------------------------------------------------------------------- diff --git a/solr/core/src/test-files/solr/configsets/cloud-managed-upgrade/conf/solrconfig.xml b/solr/core/src/test-files/solr/configsets/cloud-managed-upgrade/conf/solrconfig.xml new file mode 100644 index 0000000..fc4db3b --- /dev/null +++ b/solr/core/src/test-files/solr/configsets/cloud-managed-upgrade/conf/solrconfig.xml @@ -0,0 +1,50 @@ + + + + + + + + + ${solr.data.dir:} + + + + + managed-schema + + + ${tests.luceneMatchVersion:LATEST} + + + + ${solr.commitwithin.softcommit:true} + + + + + + + explicit + true + text + + + + http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/cc1841cb/solr/core/src/test/org/apache/solr/schema/TestManagedSchemaThreadSafety.java ---------------------------------------------------------------------- diff --git a/solr/core/src/test/org/apache/solr/schema/TestManagedSchemaThreadSafety.java b/solr/core/src/test/org/apache/solr/schema/TestManagedSchemaThreadSafety.java new file mode 100644 index 0000000..043632e --- /dev/null +++ b/solr/core/src/test/org/apache/solr/schema/TestManagedSchemaThreadSafety.java @@ -0,0 +1,162 @@ +/* + * 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.schema; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicReference; + +import org.apache.solr.SolrTestCaseJ4; +import org.apache.solr.cloud.ZkController; +import org.apache.solr.cloud.ZkSolrResourceLoader; +import org.apache.solr.cloud.ZkTestServer; +import org.apache.solr.common.cloud.SolrZkClient; +import org.apache.solr.common.util.ExecutorUtil; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.core.SolrConfig; +import org.apache.solr.core.SolrResourceLoader; +import org.apache.solr.util.LogLevel; +import org.apache.zookeeper.KeeperException; +import org.apache.zookeeper.KeeperException.NoNodeException; +import org.apache.zookeeper.Watcher; +import org.apache.zookeeper.data.Stat; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +public class TestManagedSchemaThreadSafety extends SolrTestCaseJ4 { + + private static final class SuspendingZkClient extends SolrZkClient { + AtomicReference slowpoke = new AtomicReference<>(); + + private SuspendingZkClient(String zkServerAddress, int zkClientTimeout) { + super(zkServerAddress, zkClientTimeout); + } + + boolean isSlowpoke(){ + Thread youKnow; + if ((youKnow = slowpoke.get())!=null) { + return youKnow == Thread.currentThread(); + } else { + return slowpoke.compareAndSet(null, Thread.currentThread()); + } + } + + @Override + public byte[] getData(String path, Watcher watcher, Stat stat, boolean retryOnConnLoss) + throws KeeperException, InterruptedException { + byte[] data; + try { + data = super.getData(path, watcher, stat, retryOnConnLoss); + } catch (NoNodeException e) { + if (isSlowpoke()) { + //System.out.println("suspending "+Thread.currentThread()+" on " + path); + Thread.sleep(500); + } + throw e; + } + return data; + } + } + + private static ZkTestServer zkServer; + private static Path loaderPath; + + @BeforeClass + public static void startZkServer() throws Exception { + zkServer = new ZkTestServer(createTempDir().toString()); + zkServer.run(); + loaderPath = createTempDir(); + } + + @AfterClass + public static void stopZkServer() throws Exception { + zkServer.shutdown(); + } + + @Test + @LogLevel("org.apache.solr.common.cloud.SolrZkClient=debug") + public void testThreadSafety() throws Exception { + + final String configsetName = "managed-config";// + + try (SolrZkClient client = new SuspendingZkClient(zkServer.getZkHost(), 30)) { + // we can pick any to load configs, I suppose, but here we check + client.upConfig(configset("cloud-managed-upgrade"), configsetName); + } + + ExecutorService executor = ExecutorUtil.newMDCAwareCachedThreadPool("threadpool"); + + try (SolrZkClient raceJudge = new SuspendingZkClient(zkServer.getZkHost(), 30)) { + + ZkController zkController = createZkController(raceJudge); + + List> futures = new ArrayList<>(); + for (int i = 0; i < 2; i++) { + futures.add(executor.submit(indexSchemaLoader(configsetName, zkController))); + } + + for (Future future : futures) { + future.get(); + } + } + finally { + ExecutorUtil.shutdownAndAwaitTermination(executor); + } + } + + private ZkController createZkController(SolrZkClient client) throws KeeperException, InterruptedException { + ZkController zkController = mock(ZkController.class, + Mockito.withSettings().defaultAnswer(Mockito.CALLS_REAL_METHODS)); + + when(zkController.getZkClient()).thenReturn(client); + Mockito.doAnswer(new Answer() { + @Override + public Boolean answer(InvocationOnMock invocation) throws Throwable { + return client.exists((String) invocation.getArguments()[0], true); + } + }).when(zkController).pathExists(Mockito.anyString()); + return zkController; + } + + private Runnable indexSchemaLoader(String configsetName, final ZkController zkController) { + return () -> { + try { + SolrResourceLoader loader = new ZkSolrResourceLoader(loaderPath, configsetName, zkController); + SolrConfig solrConfig = SolrConfig.readFromResourceLoader(loader, "solrconfig.xml"); + + ManagedIndexSchemaFactory factory = new ManagedIndexSchemaFactory(); + factory.init(new NamedList()); + factory.create("schema.xml", solrConfig); + } + catch (Exception e) { + throw new RuntimeException(e); + } + }; + } + +} http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/cc1841cb/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java ---------------------------------------------------------------------- diff --git a/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java b/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java index 3659446..88ddc54 100644 --- a/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java +++ b/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java @@ -1804,6 +1804,10 @@ public abstract class SolrTestCaseJ4 extends LuceneTestCase { public static Path TEST_PATH() { return getFile("solr/collection1").getParentFile().toPath(); } + public static Path configset(String name) { + return TEST_PATH().resolve("configsets").resolve(name).resolve("conf"); + } + public static Throwable getRootCause(Throwable t) { Throwable result = t; for (Throwable cause = t; null != cause; cause = cause.getCause()) { http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/cc1841cb/solr/test-framework/src/java/org/apache/solr/cloud/SolrCloudTestCase.java ---------------------------------------------------------------------- diff --git a/solr/test-framework/src/java/org/apache/solr/cloud/SolrCloudTestCase.java b/solr/test-framework/src/java/org/apache/solr/cloud/SolrCloudTestCase.java index 777e80a..02a4895 100644 --- a/solr/test-framework/src/java/org/apache/solr/cloud/SolrCloudTestCase.java +++ b/solr/test-framework/src/java/org/apache/solr/cloud/SolrCloudTestCase.java @@ -69,10 +69,6 @@ public class SolrCloudTestCase extends SolrTestCaseJ4 { public static final int DEFAULT_TIMEOUT = 30; - public static Path configset(String name) { - return TEST_PATH().resolve("configsets").resolve(name).resolve("conf"); - } - private static class Config { final String name; final Path path;