package org.apache.solr.cloud;
/*
* 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.
*/
import java.io.File;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import junit.framework.Assert;
import org.apache.solr.common.cloud.SolrZkClient;
import org.apache.solr.util.AbstractSolrTestCase;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.junit.AfterClass;
import org.junit.BeforeClass;
public class ZkSolrClientTest extends AbstractSolrTestCase {
private static final boolean DEBUG = false;
@BeforeClass
public static void beforeClass() throws Exception {
initCore("solrconfig.xml", "schema.xml");
}
public void testConnect() throws Exception {
String zkDir = dataDir.getAbsolutePath() + File.separator
+ "zookeeper/server1/data";
ZkTestServer server = null;
server = new ZkTestServer(zkDir);
server.run();
AbstractZkTestCase.tryCleanSolrZkNode(server.getZkHost());
SolrZkClient zkClient = new SolrZkClient(server.getZkAddress(), AbstractZkTestCase.TIMEOUT);
zkClient.close();
server.shutdown();
}
public void testMakeRootNode() throws Exception {
String zkDir = dataDir.getAbsolutePath() + File.separator
+ "zookeeper/server1/data";
ZkTestServer server = null;
server = new ZkTestServer(zkDir);
server.run();
AbstractZkTestCase.tryCleanSolrZkNode(server.getZkHost());
AbstractZkTestCase.makeSolrZkNode(server.getZkHost());
SolrZkClient zkClient = new SolrZkClient(server.getZkHost(),
AbstractZkTestCase.TIMEOUT);
assertTrue(zkClient.exists("/solr", true));
zkClient.close();
server.shutdown();
}
public void testClean() throws Exception {
String zkDir = dataDir.getAbsolutePath() + File.separator
+ "zookeeper/server1/data";
ZkTestServer server = null;
server = new ZkTestServer(zkDir);
server.run();
AbstractZkTestCase.tryCleanSolrZkNode(server.getZkHost());
SolrZkClient zkClient = new SolrZkClient(server.getZkHost(),
AbstractZkTestCase.TIMEOUT);
zkClient.makePath("/test/path/here", true);
zkClient.makePath("/zz/path/here", true);
zkClient.clean("/");
assertFalse(zkClient.exists("/test", true));
assertFalse(zkClient.exists("/zz", true));
zkClient.close();
server.shutdown();
}
public void testReconnect() throws Exception {
String zkDir = dataDir.getAbsolutePath() + File.separator
+ "zookeeper/server1/data";
ZkTestServer server = null;
SolrZkClient zkClient = null;
try {
server = new ZkTestServer(zkDir);
server.run();
AbstractZkTestCase.tryCleanSolrZkNode(server.getZkHost());
AbstractZkTestCase.makeSolrZkNode(server.getZkHost());
zkClient = new SolrZkClient(server.getZkAddress(), AbstractZkTestCase.TIMEOUT);
String shardsPath = "/collections/collection1/shards";
zkClient.makePath(shardsPath, false, true);
zkClient.makePath("collections/collection1", false, true);
int zkServerPort = server.getPort();
// this tests disconnect state
server.shutdown();
Thread.sleep(80);
try {
zkClient.makePath("collections/collection2", false);
Assert.fail("Server should be down here");
} catch (KeeperException.ConnectionLossException e) {
}
// bring server back up
server = new ZkTestServer(zkDir, zkServerPort);
server.run();
// TODO: can we do better?
// wait for reconnect
Thread.sleep(600);
try {
zkClient.makePath("collections/collection3", true);
} catch (KeeperException.ConnectionLossException e) {
Thread.sleep(5000); // try again in a bit
zkClient.makePath("collections/collection3", true);
}
if (DEBUG) {
zkClient.printLayoutToStdOut();
}
assertNotNull(zkClient.exists("/collections/collection3", null, true));
assertNotNull(zkClient.exists("/collections/collection1", null, true));
// simulate session expiration
// one option
long sessionId = zkClient.getSolrZooKeeper().getSessionId();
server.expire(sessionId);
// another option
//zkClient.getSolrZooKeeper().getConnection().disconnect();
// this tests expired state
Thread.sleep(1000); // pause for reconnect
for (int i = 0; i < 8; i++) {
try {
zkClient.makePath("collections/collection4", true);
break;
} catch (KeeperException.SessionExpiredException e) {
} catch (KeeperException.ConnectionLossException e) {
}
Thread.sleep(1000 * i);
}
if (DEBUG) {
zkClient.printLayoutToStdOut();
}
assertNotNull("Node does not exist, but it should", zkClient.exists("/collections/collection4", null, true));
} finally {
if (zkClient != null) {
zkClient.close();
}
if (server != null) {
server.shutdown();
}
}
}
public void testWatchChildren() throws Exception {
String zkDir = dataDir.getAbsolutePath() + File.separator
+ "zookeeper/server1/data";
final AtomicInteger cnt = new AtomicInteger();
ZkTestServer server = new ZkTestServer(zkDir);
server.run();
AbstractZkTestCase.tryCleanSolrZkNode(server.getZkHost());
Thread.sleep(400);
AbstractZkTestCase.makeSolrZkNode(server.getZkHost());
final SolrZkClient zkClient = new SolrZkClient(server.getZkAddress(), AbstractZkTestCase.TIMEOUT);
try {
final CountDownLatch latch = new CountDownLatch(1);
zkClient.makePath("/collections", true);
zkClient.getChildren("/collections", new Watcher() {
@Override
public void process(WatchedEvent event) {
if (DEBUG) {
System.out.println("children changed");
}
cnt.incrementAndGet();
// remake watch
try {
zkClient.getChildren("/collections", this, true);
latch.countDown();
} catch (KeeperException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}, true);
zkClient.makePath("/collections/collection99/shards", true);
latch.await(); //wait until watch has been re-created
zkClient.makePath("collections/collection99/config=collection1", true);
zkClient.makePath("collections/collection99/config=collection3", true);
zkClient.makePath("/collections/collection97/shards", true);
if (DEBUG) {
zkClient.printLayoutToStdOut();
}
// pause for the watches to fire
Thread.sleep(700);
if (cnt.intValue() < 2) {
Thread.sleep(4000); // wait a bit more
}
if (cnt.intValue() < 2) {
Thread.sleep(4000); // wait a bit more
}
assertEquals(2, cnt.intValue());
} finally {
if (zkClient != null) {
zkClient.close();
}
if (server != null) {
server.shutdown();
}
}
}
@Override
public void tearDown() throws Exception {
super.tearDown();
}
@AfterClass
public static void afterClass() throws InterruptedException {
// wait just a bit for any zk client threads to outlast timeout
Thread.sleep(2000);
}
}