Author: camille Date: Wed Feb 19 01:29:43 2014 New Revision: 1569588 URL: http://svn.apache.org/r1569588 Log: ZOOKEEPER-1755. Concurrent operations of four letter 'dump' ephemeral command and killSession causing NPE (Rakesh R via camille) Added: zookeeper/branches/branch-3.4/src/java/test/org/apache/zookeeper/server/DataTreeTest.java (with props) Removed: zookeeper/branches/branch-3.4/src/java/test/org/apache/zookeeper/test/DataTreeTest.java Modified: zookeeper/branches/branch-3.4/CHANGES.txt zookeeper/branches/branch-3.4/src/java/main/org/apache/zookeeper/server/DataTree.java Modified: zookeeper/branches/branch-3.4/CHANGES.txt URL: http://svn.apache.org/viewvc/zookeeper/branches/branch-3.4/CHANGES.txt?rev=1569588&r1=1569587&r2=1569588&view=diff ============================================================================== --- zookeeper/branches/branch-3.4/CHANGES.txt (original) +++ zookeeper/branches/branch-3.4/CHANGES.txt Wed Feb 19 01:29:43 2014 @@ -245,6 +245,9 @@ BUGFIXES: unregister failed jmxbeans (Rakesh R via michim) ZOOKEEPER-1844. TruncateTest fails on windows (Rakesh R via fpj) + + ZOOKEEPER-1755. Concurrent operations of four letter 'dump' ephemeral + command and killSession causing NPE (Rakesh R via camille) IMPROVEMENTS: Modified: zookeeper/branches/branch-3.4/src/java/main/org/apache/zookeeper/server/DataTree.java URL: http://svn.apache.org/viewvc/zookeeper/branches/branch-3.4/src/java/main/org/apache/zookeeper/server/DataTree.java?rev=1569588&r1=1569587&r2=1569588&view=diff ============================================================================== --- zookeeper/branches/branch-3.4/src/java/main/org/apache/zookeeper/server/DataTree.java (original) +++ zookeeper/branches/branch-3.4/src/java/main/org/apache/zookeeper/server/DataTree.java Wed Feb 19 01:29:43 2014 @@ -1253,9 +1253,11 @@ public class DataTree { pwriter.print("0x" + Long.toHexString(k)); pwriter.println(":"); HashSet tmp = ephemerals.get(k); - synchronized (tmp) { - for (String path : tmp) { - pwriter.println("\t" + path); + if (tmp != null) { + synchronized (tmp) { + for (String path : tmp) { + pwriter.println("\t" + path); + } } } } Added: zookeeper/branches/branch-3.4/src/java/test/org/apache/zookeeper/server/DataTreeTest.java URL: http://svn.apache.org/viewvc/zookeeper/branches/branch-3.4/src/java/test/org/apache/zookeeper/server/DataTreeTest.java?rev=1569588&view=auto ============================================================================== --- zookeeper/branches/branch-3.4/src/java/test/org/apache/zookeeper/server/DataTreeTest.java (added) +++ zookeeper/branches/branch-3.4/src/java/test/org/apache/zookeeper/server/DataTreeTest.java Wed Feb 19 01:29:43 2014 @@ -0,0 +1,179 @@ +/** + * 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.zookeeper.server; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.apache.zookeeper.KeeperException.NoNodeException; +import org.apache.zookeeper.KeeperException.NodeExistsException; +import org.apache.zookeeper.WatchedEvent; +import org.apache.zookeeper.Watcher; +import org.apache.zookeeper.ZKTestCase; +import org.apache.zookeeper.data.Stat; +import org.apache.zookeeper.server.DataTree; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.apache.zookeeper.server.DataNode; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.PrintWriter; +import java.io.StringWriter; + +import org.apache.zookeeper.Quotas; +import org.apache.jute.BinaryInputArchive; +import org.apache.jute.BinaryOutputArchive; +import org.apache.zookeeper.common.PathTrie; +import java.lang.reflect.*; +import java.util.concurrent.atomic.AtomicBoolean; + +public class DataTreeTest extends ZKTestCase { + protected static final Logger LOG = LoggerFactory.getLogger(DataTreeTest.class); + + private DataTree dt; + + @Before + public void setUp() throws Exception { + dt=new DataTree(); + } + + @After + public void tearDown() throws Exception { + dt=null; + } + + /** + * For ZOOKEEPER-1755 - Test race condition when taking dumpEphemerals and + * removing the session related ephemerals from DataTree structure + */ + @Test(timeout = 60000) + public void testDumpEphemerals() throws Exception { + int count = 1000; + long session = 1000; + long zxid = 2000; + final DataTree dataTree = new DataTree(); + LOG.info("Create {} zkclient sessions and its ephemeral nodes", count); + createEphemeralNode(session, dataTree, count); + final AtomicBoolean exceptionDuringDumpEphemerals = new AtomicBoolean( + false); + final AtomicBoolean running = new AtomicBoolean(true); + Thread thread = new Thread() { + public void run() { + PrintWriter pwriter = new PrintWriter(new StringWriter()); + try { + while (running.get()) { + dataTree.dumpEphemerals(pwriter); + } + } catch (Exception e) { + LOG.error("Received exception while dumpEphemerals!", e); + exceptionDuringDumpEphemerals.set(true); + } + }; + }; + thread.start(); + LOG.debug("Killing {} zkclient sessions and its ephemeral nodes", count); + killZkClientSession(session, zxid, dataTree, count); + running.set(false); + thread.join(); + Assert.assertFalse("Should have got exception while dumpEphemerals!", + exceptionDuringDumpEphemerals.get()); + } + + private void killZkClientSession(long session, long zxid, + final DataTree dataTree, int count) { + for (int i = 0; i < count; i++) { + dataTree.killSession(session + i, zxid); + } + } + + private void createEphemeralNode(long session, final DataTree dataTree, + int count) throws NoNodeException, NodeExistsException { + for (int i = 0; i < count; i++) { + dataTree.createNode("/test" + i, new byte[0], null, session + i, + dataTree.getNode("/").stat.getCversion() + 1, 1, 1); + } + } + + @Test(timeout = 60000) + public void testRootWatchTriggered() throws Exception { + class MyWatcher implements Watcher{ + boolean fired=false; + public void process(WatchedEvent event) { + if(event.getPath().equals("/")) + fired=true; + } + } + MyWatcher watcher=new MyWatcher(); + // set a watch on the root node + dt.getChildren("/", new Stat(), watcher); + // add a new node, should trigger a watch + dt.createNode("/xyz", new byte[0], null, 0, dt.getNode("/").stat.getCversion()+1, 1, 1); + Assert.assertFalse("Root node watch not triggered",!watcher.fired); + } + + /** + * For ZOOKEEPER-1046 test if cversion is getting incremented correctly. + */ + @Test(timeout = 60000) + public void testIncrementCversion() throws Exception { + dt.createNode("/test", new byte[0], null, 0, dt.getNode("/").stat.getCversion()+1, 1, 1); + DataNode zk = dt.getNode("/test"); + int prevCversion = zk.stat.getCversion(); + long prevPzxid = zk.stat.getPzxid(); + dt.setCversionPzxid("/test/", prevCversion + 1, prevPzxid + 1); + int newCversion = zk.stat.getCversion(); + long newPzxid = zk.stat.getPzxid(); + Assert.assertTrue(" verification failed. Expected: <" + + (prevCversion + 1) + ", " + (prevPzxid + 1) + ">, found: <" + + newCversion + ", " + newPzxid + ">", + (newCversion == prevCversion + 1 && newPzxid == prevPzxid + 1)); + } + + @Test(timeout = 60000) + public void testPathTrieClearOnDeserialize() throws Exception { + + //Create a DataTree with quota nodes so PathTrie get updated + DataTree dserTree = new DataTree(); + + dserTree.createNode("/bug", new byte[20], null, -1, 1, 1, 1); + dserTree.createNode(Quotas.quotaZookeeper+"/bug", null, null, -1, 1, 1, 1); + dserTree.createNode(Quotas.quotaPath("/bug"), new byte[20], null, -1, 1, 1, 1); + dserTree.createNode(Quotas.statPath("/bug"), new byte[20], null, -1, 1, 1, 1); + + //deserialize a DataTree; this should clear the old /bug nodes and pathTrie + DataTree tree = new DataTree(); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + BinaryOutputArchive oa = BinaryOutputArchive.getArchive(baos); + tree.serialize(oa, "test"); + baos.flush(); + + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + BinaryInputArchive ia = BinaryInputArchive.getArchive(bais); + dserTree.deserialize(ia, "test"); + + Field pfield = DataTree.class.getDeclaredField("pTrie"); + pfield.setAccessible(true); + PathTrie pTrie = (PathTrie)pfield.get(dserTree); + + //Check that the node path is removed from pTrie + Assert.assertEquals("/bug is still in pTrie", "", pTrie.findMaxPrefix("/bug")); + } +} Propchange: zookeeper/branches/branch-3.4/src/java/test/org/apache/zookeeper/server/DataTreeTest.java ------------------------------------------------------------------------------ svn:mime-type = text/plain