zookeeper-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ph...@apache.org
Subject zookeeper git commit: ZOOKEEPER-2901: TTL Nodes don't work with Server IDs > 127
Date Wed, 09 May 2018 22:12:41 GMT
Repository: zookeeper
Updated Branches:
  refs/heads/branch-3.5 04a8df770 -> 282dc8368


ZOOKEEPER-2901: TTL Nodes don't work with Server IDs > 127

There was a major oversight when TTL nodes were implemented. The session ID generator for each server is seeded with the configured Server ID in the high byte. TTL Nodes were using the highest bit to denote a TTL node when used in the ephemeral owner. This meant that Server IDs > 127 that created ephemeral nodes would have those nodes always considered TTL nodes (with the TTL being essentially a random number).

This PR fixes the issue by disabling TTL Nodes by default. They must now be enabled in zoo.cfg. When TTL Nodes are enabled, the max Server ID changes from 255 to 254. This allows the high byte of a session ID stored in the ephemeral owner to use 0xFF to denote a TTL node.

About this change:

- The doc has been updated to note that TTL nodes are disabled by default and must be enabled via config. Also, the docs explains that when TTL nodes are enabled the max Server ID becomes 254
- The TTL implementation has been updated to use 0xFF in the high byte of the ephemeralOwner to denote a TTL node. This decreases the max TTL by an insignificant amount
- PrepRequestProcessor now throws `KeeperException.UnimplementedException` when an attempt to create a TTL node is made but the server has not been configured to enable TTL Nodes.
- QuorumPeer throws a `RuntimeException` if TTL Nodes are enabled but the Server ID > 254
- Tests have been added to validate all of this

IMPORTANT NOTE: TTL Nodes created in 3.5.3 will revert to EPHEMERAL with this change. We need to discuss the impact of this and consider workarounds, etc.

Author: randgalt <jordan@jordanzimmerman.com>
Author: Abraham Fine <afine@apache.org>

Reviewers: phunt@apache.org

Closes #377 from Randgalt/ZOOKEEPER-2901

Change-Id: I6923a22ecf847710d4e7151daf8c044734873f6b
(cherry picked from commit ceaeccd6e310983d37e685a9d5fff3d7e75cf125)
Signed-off-by: Patrick Hunt <phunt@apache.org>


Project: http://git-wip-us.apache.org/repos/asf/zookeeper/repo
Commit: http://git-wip-us.apache.org/repos/asf/zookeeper/commit/282dc836
Tree: http://git-wip-us.apache.org/repos/asf/zookeeper/tree/282dc836
Diff: http://git-wip-us.apache.org/repos/asf/zookeeper/diff/282dc836

Branch: refs/heads/branch-3.5
Commit: 282dc836802f67ed4814a36bdf45423cff27b577
Parents: 04a8df7
Author: randgalt <jordan@jordanzimmerman.com>
Authored: Wed May 9 14:57:41 2018 -0700
Committer: Patrick Hunt <phunt@apache.org>
Committed: Wed May 9 15:11:41 2018 -0700

----------------------------------------------------------------------
 .../content/xdocs/zookeeperAdmin.xml            |  37 +++-
 .../content/xdocs/zookeeperProgrammers.xml      |   5 +
 .../main/org/apache/zookeeper/ZooKeeper.java    |   2 +-
 .../org/apache/zookeeper/cli/CreateCommand.java |   2 +-
 .../zookeeper/server/ContainerManager.java      |  10 +-
 .../org/apache/zookeeper/server/DataTree.java   |   2 +-
 .../apache/zookeeper/server/EphemeralType.java  | 186 +++++++++++++++++--
 .../zookeeper/server/OldEphemeralType.java      |  74 ++++++++
 .../zookeeper/server/PrepRequestProcessor.java  |   5 +-
 .../zookeeper/server/SessionTrackerImpl.java    |   5 +
 .../zookeeper/server/ZooKeeperServer.java       |  13 +-
 .../zookeeper/server/quorum/QuorumPeer.java     |   1 +
 src/java/test/config/findbugsExcludeFile.xml    |   8 +
 .../zookeeper/server/CreateContainerTest.java   |  23 ++-
 .../apache/zookeeper/server/CreateTTLTest.java  |  51 ++++-
 .../zookeeper/server/Emulate353TTLTest.java     |  95 ++++++++++
 .../zookeeper/server/EphemeralTypeTest.java     |  32 +++-
 .../apache/zookeeper/server/ServerIdTest.java   | 119 ++++++++++++
 .../org/apache/zookeeper/test/ClientBase.java   |  17 +-
 .../org/apache/zookeeper/test/TruncateTest.java |   4 +-
 20 files changed, 634 insertions(+), 57 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/zookeeper/blob/282dc836/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml
----------------------------------------------------------------------
diff --git a/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml b/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml
index 1ff428e..62cd54b 100644
--- a/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml
+++ b/src/docs/src/documentation/content/xdocs/zookeeperAdmin.xml
@@ -274,7 +274,9 @@ server.3=zoo3:2888:3888</programlisting>
           consists of a single line containing only the text of that machine's
           id. So <filename>myid</filename> of server 1 would contain the text
           "1" and nothing else. The id must be unique within the
-          ensemble and should have a value between 1 and 255.</para>
+          ensemble and should have a value between 1 and 255. <emphasis role="bold">IMPORTANT:</emphasis> if you
+          enable extended features such as TTL Nodes (see below) the id must be
+          between 1 and 254 due to internal limitations.</para>
         </listitem>
 
         <listitem>
@@ -940,6 +942,39 @@ server.3=zoo3:2888:3888</programlisting>
             </listitem>
           </varlistentry>
 
+          <varlistentry>
+            <term>zookeeper.extendedTypesEnabled</term>
+
+            <listitem>
+                <para>(Java system property only: <emphasis
+                    role="bold">zookeeper.extendedTypesEnabled</emphasis>)</para>
+
+              <para><emphasis role="bold">New in 3.5.4, 3.6.0:</emphasis> Define to "true" to enable
+              extended features such as the creation of <ulink url="zookeeperProgrammers.html#TTL+Nodes">TTL Nodes</ulink>.
+              They are disabled by default. IMPORTANT: when enabled server IDs must
+              be less than 255 due to internal limitations.
+              </para>
+            </listitem>
+          </varlistentry>
+
+          <varlistentry>
+            <term>zookeeper.emulate353TTLNodes</term>
+
+            <listitem>
+                <para>(Java system property only: <emphasis
+                    role="bold">zookeeper.emulate353TTLNodes</emphasis>)</para>
+
+              <para><emphasis role="bold">New in 3.5.4, 3.6.0:</emphasis> Due to
+                <ulink url="https://issues.apache.org/jira/browse/ZOOKEEPER-2901">ZOOKEEPER-2901</ulink> TTL nodes
+                created in version 3.5.3 are not supported in 3.5.4/3.6.0. However, a workaround is provided via the
+                zookeeper.emulate353TTLNodes system property. If you used TTL nodes in ZooKeeper 3.5.3 and need to maintain
+                compatibility set <emphasis role="bold">zookeeper.emulate353TTLNodes</emphasis> to "true" in addition to
+                <emphasis role="bold">zookeeper.extendedTypesEnabled</emphasis>. NOTE: due to the bug, server IDs
+                must be 127 or less. Additionally, the maximum support TTL value is 1099511627775 which is smaller
+                than what was allowed in 3.5.3 (1152921504606846975)</para>
+            </listitem>
+          </varlistentry>
+
         </variablelist>
       </section>
 

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/282dc836/src/docs/src/documentation/content/xdocs/zookeeperProgrammers.xml
----------------------------------------------------------------------
diff --git a/src/docs/src/documentation/content/xdocs/zookeeperProgrammers.xml b/src/docs/src/documentation/content/xdocs/zookeeperProgrammers.xml
index 9669546..35fd23e 100644
--- a/src/docs/src/documentation/content/xdocs/zookeeperProgrammers.xml
+++ b/src/docs/src/documentation/content/xdocs/zookeeperProgrammers.xml
@@ -270,6 +270,11 @@
           you can optionally set a TTL in milliseconds for the znode. If the znode
           is not modified within the TTL and has no children it will become a candidate
           to be deleted by the server at some point in the future.</para>
+
+        <para>Note: TTL Nodes must be enabled via System property as
+        they are disabled by default. See the <ulink url="zookeeperAdmin.html#sc_configuration">Administrator's
+        Guide</ulink> for details. If you attempt to create TTL Nodes without the proper System property set the server
+        will throw <emphasis>KeeperException.UnimplementedException</emphasis>.</para>
       </section>
     </section>
 

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/282dc836/src/java/main/org/apache/zookeeper/ZooKeeper.java
----------------------------------------------------------------------
diff --git a/src/java/main/org/apache/zookeeper/ZooKeeper.java b/src/java/main/org/apache/zookeeper/ZooKeeper.java
index ef1d2a1..6b3628b 100644
--- a/src/java/main/org/apache/zookeeper/ZooKeeper.java
+++ b/src/java/main/org/apache/zookeeper/ZooKeeper.java
@@ -1526,7 +1526,7 @@ public class ZooKeeper implements AutoCloseable {
      * or {@link CreateMode#PERSISTENT_SEQUENTIAL_WITH_TTL}. If the znode has not been modified
      * within the given TTL, it will be deleted once it has no children. The TTL unit is
      * milliseconds and must be greater than 0 and less than or equal to
-     * {@link EphemeralType#MAX_TTL}.
+     * {@link EphemeralType#maxValue()} for {@link EphemeralType#TTL}.
      */
     public String create(final String path, byte data[], List<ACL> acl,
             CreateMode createMode, Stat stat, long ttl)

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/282dc836/src/java/main/org/apache/zookeeper/cli/CreateCommand.java
----------------------------------------------------------------------
diff --git a/src/java/main/org/apache/zookeeper/cli/CreateCommand.java b/src/java/main/org/apache/zookeeper/cli/CreateCommand.java
index 3d3f3ff..04d4909 100644
--- a/src/java/main/org/apache/zookeeper/cli/CreateCommand.java
+++ b/src/java/main/org/apache/zookeeper/cli/CreateCommand.java
@@ -100,7 +100,7 @@ public class CreateCommand extends CliCommand {
         }
         if ( hasT ) {
             try {
-                EphemeralType.ttlToEphemeralOwner(ttl);
+                EphemeralType.TTL.toEphemeralOwner(ttl);
             } catch (IllegalArgumentException e) {
                 throw new MalformedCommandException(e.getMessage());
             }

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/282dc836/src/java/main/org/apache/zookeeper/server/ContainerManager.java
----------------------------------------------------------------------
diff --git a/src/java/main/org/apache/zookeeper/server/ContainerManager.java b/src/java/main/org/apache/zookeeper/server/ContainerManager.java
index e59d7bd..2b41ac6 100644
--- a/src/java/main/org/apache/zookeeper/server/ContainerManager.java
+++ b/src/java/main/org/apache/zookeeper/server/ContainerManager.java
@@ -158,10 +158,12 @@ public class ContainerManager {
             if (node != null) {
                 Set<String> children = node.getChildren();
                 if ((children == null) || (children.size() == 0)) {
-                    long elapsed = getElapsed(node);
-                    long ttl = EphemeralType.getTTL(node.stat.getEphemeralOwner());
-                    if ((ttl != 0) && (elapsed > ttl)) {
-                        candidates.add(ttlPath);
+                    if ( EphemeralType.get(node.stat.getEphemeralOwner()) == EphemeralType.TTL ) {
+                        long elapsed = getElapsed(node);
+                        long ttl = EphemeralType.TTL.getValue(node.stat.getEphemeralOwner());
+                        if ((ttl != 0) && (getElapsed(node) > ttl)) {
+                            candidates.add(ttlPath);
+                        }
                     }
                 }
             }

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/282dc836/src/java/main/org/apache/zookeeper/server/DataTree.java
----------------------------------------------------------------------
diff --git a/src/java/main/org/apache/zookeeper/server/DataTree.java b/src/java/main/org/apache/zookeeper/server/DataTree.java
index b6fb61d..f17be30 100644
--- a/src/java/main/org/apache/zookeeper/server/DataTree.java
+++ b/src/java/main/org/apache/zookeeper/server/DataTree.java
@@ -812,7 +812,7 @@ public class DataTree {
                             createTtlTxn.getPath(),
                             createTtlTxn.getData(),
                             createTtlTxn.getAcl(),
-                            EphemeralType.ttlToEphemeralOwner(createTtlTxn.getTtl()),
+                            EphemeralType.TTL.toEphemeralOwner(createTtlTxn.getTtl()),
                             createTtlTxn.getParentCVersion(),
                             header.getZxid(), header.getTime(), stat);
                     rc.stat = stat;

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/282dc836/src/java/main/org/apache/zookeeper/server/EphemeralType.java
----------------------------------------------------------------------
diff --git a/src/java/main/org/apache/zookeeper/server/EphemeralType.java b/src/java/main/org/apache/zookeeper/server/EphemeralType.java
index cb4de9d..7861d40 100644
--- a/src/java/main/org/apache/zookeeper/server/EphemeralType.java
+++ b/src/java/main/org/apache/zookeeper/server/EphemeralType.java
@@ -20,6 +20,47 @@ package org.apache.zookeeper.server;
 
 import org.apache.zookeeper.CreateMode;
 
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * <p>
+ * Abstraction that interprets the <code>ephemeralOwner</code> field of a ZNode. Originally,
+ * the ephemeralOwner noted that a ZNode is ephemeral and which session created the node.
+ * Through an optional system property (<code>zookeeper.extendedTypesEnabled</code>) "extended"
+ * features such as TTL Nodes can be enabled. Special bits of the ephemeralOwner are used to
+ * denote which feature is enabled and the remaining bits of the ephemeralOwner are feature
+ * specific.
+ * </p>
+ * <p>
+ * <p>
+ * When the system property <code>zookeeper.extendedTypesEnabled</code> is true, extended types
+ * are enabled. An extended ephemeralOwner is defined as an ephemeralOwner whose high 8 bits are
+ * set (<code>0xff00000000000000L</code>). The two bytes that follow the high 8 bits are
+ * used to denote which extended feature the ephemeralOwner represents. The remaining 5 bytes are
+ * used by the feature for whatever purpose is needed
+ * </p>
+ * <p>
+ * <p>
+ * Currently, the only extended feature is TTL Nodes. It is denoted by the extended feature value of 0.
+ * i.e. for TTL Nodes, the ephemeralOwner has the high byte set to 0xff and the next 2 bytes are 0 followed
+ * by 5 bytes that represent the TTL value in milliseconds. So, an ephemeralOwner with a TTL value of 1
+ * millisecond is: <code>0xff00000000000001</code>.
+ * </p>
+ * <p>
+ * <p>
+ * To add new extended features: a) Add a new name to the enum, b) define a constant EXTENDED_BIT_XXXX that's next
+ * in line (after TTLs, that would be <code>0x0001</code>), c) add a mapping to the extendedFeatureMap via the static
+ * initializer
+ * </p>
+ * <p>
+ * <p>
+ * NOTE: "Container" nodes technically are extended types but as it was implemented before this feature they are
+ * denoted specially. An ephemeral owner with only the high bit set (<code>0x8000000000000000L</code>) is by definition
+ * a container node (irrespective of whether or not extended types are enabled).
+ * </p>
+ */
 public enum EphemeralType {
     /**
      * Not ephemeral
@@ -36,41 +77,152 @@ public enum EphemeralType {
     /**
      * TTL node
      */
-    TTL;
+    TTL() {
+        @Override
+        public long maxValue() {
+            return EXTENDED_FEATURE_VALUE_MASK;  // 12725 days, about 34 years
+        }
+
+        @Override
+        public long toEphemeralOwner(long ttl) {
+            if ((ttl > TTL.maxValue()) || (ttl <= 0)) {
+                throw new IllegalArgumentException("ttl must be positive and cannot be larger than: " + TTL.maxValue());
+            }
+            //noinspection PointlessBitwiseExpression
+            return EXTENDED_MASK | EXTENDED_BIT_TTL | ttl;  // TTL_RESERVED_BIT is actually zero - but it serves to document that the proper extended bit needs to be set
+        }
+
+        @Override
+        public long getValue(long ephemeralOwner) {
+            return getExtendedFeatureValue(ephemeralOwner);
+        }
+    };
+
+    /**
+     * For types that support it, the maximum extended value
+     *
+     * @return 0 or max
+     */
+    public long maxValue() {
+        return 0;
+    }
+
+    /**
+     * For types that support it, convert a value to an extended ephemeral owner
+     *
+     * @return 0 or extended ephemeral owner
+     */
+    public long toEphemeralOwner(long value) {
+        return 0;
+    }
+
+    /**
+     * For types that support it, return the extended value from an extended ephemeral owner
+     *
+     * @return 0 or extended value
+     */
+    public long getValue(long ephemeralOwner) {
+        return 0;
+    }
 
     public static final long CONTAINER_EPHEMERAL_OWNER = Long.MIN_VALUE;
-    public static final long MAX_TTL = 0x0fffffffffffffffL;
-    public static final long TTL_MASK = 0x8000000000000000L;
+    public static final long MAX_EXTENDED_SERVER_ID = 0xfe;  // 254
+
+    private static final long EXTENDED_MASK = 0xff00000000000000L;
+    private static final long EXTENDED_BIT_TTL = 0x0000;
+    private static final long RESERVED_BITS_MASK = 0x00ffff0000000000L;
+    private static final long RESERVED_BITS_SHIFT = 40;
+
+    private static final Map<Long, EphemeralType> extendedFeatureMap;
 
+    static {
+        Map<Long, EphemeralType> map = new HashMap<>();
+        map.put(EXTENDED_BIT_TTL, TTL);
+        extendedFeatureMap = Collections.unmodifiableMap(map);
+    }
+
+    private static final long EXTENDED_FEATURE_VALUE_MASK = ~(EXTENDED_MASK | RESERVED_BITS_MASK);
+
+    // Visible for testing
+    static final String EXTENDED_TYPES_ENABLED_PROPERTY = "zookeeper.extendedTypesEnabled";
+    static final String TTL_3_5_3_EMULATION_PROPERTY = "zookeeper.emulate353TTLNodes";
+
+    /**
+     * Return true if extended ephemeral types are enabled
+     *
+     * @return true/false
+     */
+    public static boolean extendedEphemeralTypesEnabled() {
+        return Boolean.getBoolean(EXTENDED_TYPES_ENABLED_PROPERTY);
+    }
+
+    /**
+     * Convert a ZNode ephemeral owner to an ephemeral type. If extended types are not
+     * enabled, VOID or NORMAL is always returned
+     *
+     * @param ephemeralOwner the ZNode's ephemeral owner
+     * @return type
+     */
     public static EphemeralType get(long ephemeralOwner) {
+        if (extendedEphemeralTypesEnabled()) {
+            if (Boolean.getBoolean(TTL_3_5_3_EMULATION_PROPERTY)) {
+                if (OldEphemeralType.get(ephemeralOwner) == OldEphemeralType.TTL) {
+                    return TTL;
+                }
+            }
+
+            if ((ephemeralOwner & EXTENDED_MASK) == EXTENDED_MASK) {
+                long extendedFeatureBit = getExtendedFeatureBit(ephemeralOwner);
+                EphemeralType ephemeralType = extendedFeatureMap.get(extendedFeatureBit);
+                if (ephemeralType == null) {
+                    throw new IllegalArgumentException(String.format("Invalid ephemeralOwner. [%s]", Long.toHexString(ephemeralOwner)));
+                }
+                return ephemeralType;
+            }
+        }
         if (ephemeralOwner == CONTAINER_EPHEMERAL_OWNER) {
             return CONTAINER;
         }
-        if (ephemeralOwner < 0) {
-            return TTL;
-        }
         return (ephemeralOwner == 0) ? VOID : NORMAL;
     }
 
+    /**
+     * Make sure the given server ID is compatible with the current extended ephemeral setting
+     *
+     * @param serverId Server ID
+     * @throws RuntimeException extendedTypesEnabled is true but Server ID is too large
+     */
+    public static void validateServerId(long serverId) {
+        // TODO: in the future, serverId should be validated for all cases, not just the extendedEphemeralTypesEnabled case
+        // TODO: however, for now, it would be too disruptive
+
+        if (extendedEphemeralTypesEnabled()) {
+            if (serverId > EphemeralType.MAX_EXTENDED_SERVER_ID) {
+                throw new RuntimeException("extendedTypesEnabled is true but Server ID is too large. Cannot be larger than " + EphemeralType.MAX_EXTENDED_SERVER_ID);
+            }
+        }
+    }
+
+    /**
+     * Utility to validate a create mode and a ttl
+     *
+     * @param mode create mode
+     * @param ttl  ttl
+     * @throws IllegalArgumentException if the ttl is not valid for the mode
+     */
     public static void validateTTL(CreateMode mode, long ttl) {
         if (mode.isTTL()) {
-            ttlToEphemeralOwner(ttl);
+            TTL.toEphemeralOwner(ttl);
         } else if (ttl >= 0) {
             throw new IllegalArgumentException("ttl not valid for mode: " + mode);
         }
     }
 
-    public static long getTTL(long ephemeralOwner) {
-        if ((ephemeralOwner < 0) && (ephemeralOwner != CONTAINER_EPHEMERAL_OWNER)) {
-            return (ephemeralOwner & MAX_TTL);
-        }
-        return 0;
+    private static long getExtendedFeatureBit(long ephemeralOwner) {
+        return (ephemeralOwner & RESERVED_BITS_MASK) >> RESERVED_BITS_SHIFT;
     }
 
-    public static long ttlToEphemeralOwner(long ttl) {
-        if ((ttl > MAX_TTL) || (ttl <= 0)) {
-            throw new IllegalArgumentException("ttl must be positive and cannot be larger than: " + MAX_TTL);
-        }
-        return TTL_MASK | ttl;
+    private static long getExtendedFeatureValue(long ephemeralOwner) {
+        return ephemeralOwner & EXTENDED_FEATURE_VALUE_MASK;
     }
 }

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/282dc836/src/java/main/org/apache/zookeeper/server/OldEphemeralType.java
----------------------------------------------------------------------
diff --git a/src/java/main/org/apache/zookeeper/server/OldEphemeralType.java b/src/java/main/org/apache/zookeeper/server/OldEphemeralType.java
new file mode 100644
index 0000000..bffec8a
--- /dev/null
+++ b/src/java/main/org/apache/zookeeper/server/OldEphemeralType.java
@@ -0,0 +1,74 @@
+/**
+ * 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
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * 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;
+
+/**
+ * See https://issues.apache.org/jira/browse/ZOOKEEPER-2901
+ *
+ * version 3.5.3 introduced bugs associated with how TTL nodes were implemented. version 3.5.4
+ * fixes the problems but makes TTL nodes created in 3.5.3 invalid. OldEphemeralType is a copy
+ * of the old - bad - implementation that is provided as a workaround. {@link EphemeralType#TTL_3_5_3_EMULATION_PROPERTY}
+ * can be used to emulate support of the badly specified TTL nodes.
+ */
+public enum OldEphemeralType {
+    /**
+     * Not ephemeral
+     */
+    VOID,
+    /**
+     * Standard, pre-3.5.x EPHEMERAL
+     */
+    NORMAL,
+    /**
+     * Container node
+     */
+    CONTAINER,
+    /**
+     * TTL node
+     */
+    TTL;
+
+    public static final long CONTAINER_EPHEMERAL_OWNER = Long.MIN_VALUE;
+    public static final long MAX_TTL = 0x0fffffffffffffffL;
+    public static final long TTL_MASK = 0x8000000000000000L;
+
+    public static OldEphemeralType get(long ephemeralOwner) {
+        if (ephemeralOwner == CONTAINER_EPHEMERAL_OWNER) {
+            return CONTAINER;
+        }
+        if (ephemeralOwner < 0) {
+            return TTL;
+        }
+        return (ephemeralOwner == 0) ? VOID : NORMAL;
+    }
+
+    public static long getTTL(long ephemeralOwner) {
+        if ((ephemeralOwner < 0) && (ephemeralOwner != CONTAINER_EPHEMERAL_OWNER)) {
+            return (ephemeralOwner & MAX_TTL);
+        }
+        return 0;
+    }
+
+    public static long ttlToEphemeralOwner(long ttl) {
+        if ((ttl > MAX_TTL) || (ttl <= 0)) {
+            throw new IllegalArgumentException("ttl must be positive and cannot be larger than: " + MAX_TTL);
+        }
+        return TTL_MASK | ttl;
+    }
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/282dc836/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java
----------------------------------------------------------------------
diff --git a/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java b/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java
index aa4594c..a8cb048 100644
--- a/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java
+++ b/src/java/main/org/apache/zookeeper/server/PrepRequestProcessor.java
@@ -916,9 +916,12 @@ public class PrepRequestProcessor extends ZooKeeperCriticalThread implements
         }
         return retval;
     }
-    
+
     private void validateCreateRequest(String path, CreateMode createMode, Request request, long ttl)
             throws KeeperException {
+        if (createMode.isTTL() && !EphemeralType.extendedEphemeralTypesEnabled()) {
+            throw new KeeperException.UnimplementedException();
+        }
         try {
             EphemeralType.validateTTL(createMode, ttl);
         } catch (IllegalArgumentException e) {

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/282dc836/src/java/main/org/apache/zookeeper/server/SessionTrackerImpl.java
----------------------------------------------------------------------
diff --git a/src/java/main/org/apache/zookeeper/server/SessionTrackerImpl.java b/src/java/main/org/apache/zookeeper/server/SessionTrackerImpl.java
index b776332..6fc6eb5 100644
--- a/src/java/main/org/apache/zookeeper/server/SessionTrackerImpl.java
+++ b/src/java/main/org/apache/zookeeper/server/SessionTrackerImpl.java
@@ -84,6 +84,9 @@ public class SessionTrackerImpl extends ZooKeeperCriticalThread implements
         long nextSid;
         nextSid = (Time.currentElapsedTime() << 24) >>> 8;
         nextSid =  nextSid | (id <<56);
+        if (nextSid == EphemeralType.CONTAINER_EPHEMERAL_OWNER) {
+            ++nextSid;  // this is an unlikely edge case, but check it just in case
+        }
         return nextSid;
     }
 
@@ -101,6 +104,8 @@ public class SessionTrackerImpl extends ZooKeeperCriticalThread implements
         for (Entry<Long, Integer> e : sessionsWithTimeout.entrySet()) {
             addSession(e.getKey(), e.getValue());
         }
+
+        EphemeralType.validateServerId(serverId);
     }
 
     volatile boolean running = true;

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/282dc836/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java
----------------------------------------------------------------------
diff --git a/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java b/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java
index 76040df..3692dc4 100644
--- a/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java
+++ b/src/java/main/org/apache/zookeeper/server/ZooKeeperServer.java
@@ -124,6 +124,7 @@ public class ZooKeeperServer implements SessionExpirer, ServerStats.Provider {
     private final ServerStats serverStats;
     private final ZooKeeperServerListener listener;
     private ZooKeeperServerShutdownHandler zkShutdownHandler;
+    private volatile int createSessionTrackerServerId = 1;
 
     void removeCnxn(ServerCnxn cnxn) {
         zkDb.removeCnxn(cnxn);
@@ -470,9 +471,19 @@ public class ZooKeeperServer implements SessionExpirer, ServerStats.Provider {
         return listener;
     }
 
+    /**
+     * Change the server ID used by {@link #createSessionTracker()}. Must be called prior to
+     * {@link #startup()} being called
+     *
+     * @param newId ID to use
+     */
+    public void setCreateSessionTrackerServerId(int newId) {
+        createSessionTrackerServerId = newId;
+    }
+
     protected void createSessionTracker() {
         sessionTracker = new SessionTrackerImpl(this, zkDb.getSessionWithTimeOuts(),
-                tickTime, 1, getZooKeeperServerListener());
+                tickTime, createSessionTrackerServerId, getZooKeeperServerListener());
     }
 
     protected void startSessionTracker() {

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/282dc836/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java
----------------------------------------------------------------------
diff --git a/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java b/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java
index 0ad9235..da837b3 100644
--- a/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java
+++ b/src/java/main/org/apache/zookeeper/server/quorum/QuorumPeer.java
@@ -51,6 +51,7 @@ import org.apache.zookeeper.common.AtomicFileWritingIdiom.WriterStatement;
 import org.apache.zookeeper.common.Time;
 import org.apache.zookeeper.jmx.MBeanRegistry;
 import org.apache.zookeeper.jmx.ZKMBeanInfo;
+import org.apache.zookeeper.server.EphemeralType;
 import org.apache.zookeeper.server.ServerCnxnFactory;
 import org.apache.zookeeper.server.ZKDatabase;
 import org.apache.zookeeper.server.ZooKeeperServer;

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/282dc836/src/java/test/config/findbugsExcludeFile.xml
----------------------------------------------------------------------
diff --git a/src/java/test/config/findbugsExcludeFile.xml b/src/java/test/config/findbugsExcludeFile.xml
index b0be95e..71715e9 100644
--- a/src/java/test/config/findbugsExcludeFile.xml
+++ b/src/java/test/config/findbugsExcludeFile.xml
@@ -155,4 +155,12 @@
     <Class name="org.apache.zookeeper.ZooDefs$Ids"/>
       <Bug pattern="MS_MUTABLE_COLLECTION" />
   </Match>
+
+  <!-- Disable 'Return value of method without side effect is ignored' for EphemeralType. The default
+       version just returns 0 which triggers the warning. Findbugs can't seem to find the override in
+       the TTL value of the enum however -->
+  <Match>
+    <Class name="org.apache.zookeeper.server.EphemeralType"/>
+      <Bug pattern="RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT" />
+  </Match>
 </FindBugsFilter>

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/282dc836/src/java/test/org/apache/zookeeper/server/CreateContainerTest.java
----------------------------------------------------------------------
diff --git a/src/java/test/org/apache/zookeeper/server/CreateContainerTest.java b/src/java/test/org/apache/zookeeper/server/CreateContainerTest.java
index 2b1743b..4889684 100644
--- a/src/java/test/org/apache/zookeeper/server/CreateContainerTest.java
+++ b/src/java/test/org/apache/zookeeper/server/CreateContainerTest.java
@@ -24,7 +24,6 @@ import org.apache.zookeeper.test.ClientBase;
 import org.junit.Assert;
 import org.junit.Test;
 
-import java.io.IOException;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
@@ -47,14 +46,14 @@ public class CreateContainerTest extends ClientBase {
 
     @Test(timeout = 30000)
     public void testCreate()
-            throws IOException, KeeperException, InterruptedException {
+            throws KeeperException, InterruptedException {
         createNoStatVerifyResult("/foo");
         createNoStatVerifyResult("/foo/child");
     }
 
     @Test(timeout = 30000)
     public void testCreateWithStat()
-            throws IOException, KeeperException, InterruptedException {
+            throws KeeperException, InterruptedException {
         Stat stat = createWithStatVerifyResult("/foo");
         Stat childStat = createWithStatVerifyResult("/foo/child");
         // Don't expect to get the same stats for different creates.
@@ -64,7 +63,7 @@ public class CreateContainerTest extends ClientBase {
     @SuppressWarnings("ConstantConditions")
     @Test(timeout = 30000)
     public void testCreateWithNullStat()
-            throws IOException, KeeperException, InterruptedException {
+            throws KeeperException, InterruptedException {
         final String name = "/foo";
         Assert.assertNull(zk.exists(name, false));
 
@@ -78,7 +77,7 @@ public class CreateContainerTest extends ClientBase {
 
     @Test(timeout = 30000)
     public void testSimpleDeletion()
-            throws IOException, KeeperException, InterruptedException {
+            throws KeeperException, InterruptedException {
         zk.create("/foo", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.CONTAINER);
         zk.create("/foo/bar", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
         zk.delete("/foo/bar", -1);  // should cause "/foo" to get deleted when checkContainers() is called
@@ -94,7 +93,7 @@ public class CreateContainerTest extends ClientBase {
 
     @Test(timeout = 30000)
     public void testMultiWithContainerSimple()
-            throws IOException, KeeperException, InterruptedException {
+            throws KeeperException, InterruptedException {
         Op createContainer = Op.create("/foo", new byte[0],
                 ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.CONTAINER);
         zk.multi(Collections.singletonList(createContainer));
@@ -105,7 +104,7 @@ public class CreateContainerTest extends ClientBase {
 
     @Test(timeout = 30000)
     public void testMultiWithContainer()
-            throws IOException, KeeperException, InterruptedException {
+            throws KeeperException, InterruptedException {
         Op createContainer = Op.create("/foo", new byte[0],
                 ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.CONTAINER);
         Op createChild = Op.create("/foo/bar", new byte[0],
@@ -141,7 +140,7 @@ public class CreateContainerTest extends ClientBase {
 
     @Test(timeout = 30000)
     public void testSimpleDeletionAsync()
-            throws IOException, KeeperException, InterruptedException {
+            throws KeeperException, InterruptedException {
         final CountDownLatch latch = new CountDownLatch(1);
         AsyncCallback.Create2Callback cb = new AsyncCallback.Create2Callback() {
             @Override
@@ -166,7 +165,7 @@ public class CreateContainerTest extends ClientBase {
 
     @Test(timeout = 30000)
     public void testCascadingDeletion()
-            throws IOException, KeeperException, InterruptedException {
+            throws KeeperException, InterruptedException {
         zk.create("/foo", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.CONTAINER);
         zk.create("/foo/bar", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.CONTAINER);
         zk.create("/foo/bar/one", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
@@ -186,7 +185,7 @@ public class CreateContainerTest extends ClientBase {
 
     @Test(timeout = 30000)
     public void testFalseEmpty()
-            throws IOException, KeeperException, InterruptedException {
+            throws KeeperException, InterruptedException {
         zk.create("/foo", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.CONTAINER);
         zk.create("/foo/bar", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
 
@@ -205,11 +204,11 @@ public class CreateContainerTest extends ClientBase {
 
     @Test(timeout = 30000)
     public void testMaxPerMinute()
-            throws IOException, KeeperException, InterruptedException {
+            throws InterruptedException {
         final BlockingQueue<String> queue = new LinkedBlockingQueue<String>();
         RequestProcessor processor = new RequestProcessor() {
             @Override
-            public void processRequest(Request request) throws RequestProcessorException {
+            public void processRequest(Request request) {
                 queue.add(new String(request.request.array()));
             }
 

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/282dc836/src/java/test/org/apache/zookeeper/server/CreateTTLTest.java
----------------------------------------------------------------------
diff --git a/src/java/test/org/apache/zookeeper/server/CreateTTLTest.java b/src/java/test/org/apache/zookeeper/server/CreateTTLTest.java
index 70fa223..2440030 100644
--- a/src/java/test/org/apache/zookeeper/server/CreateTTLTest.java
+++ b/src/java/test/org/apache/zookeeper/server/CreateTTLTest.java
@@ -21,12 +21,11 @@ package org.apache.zookeeper.server;
 import org.apache.zookeeper.AsyncCallback;
 import org.apache.zookeeper.CreateMode;
 import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.KeeperException.Code;
 import org.apache.zookeeper.Op;
 import org.apache.zookeeper.OpResult;
 import org.apache.zookeeper.TestableZooKeeper;
 import org.apache.zookeeper.ZooDefs;
-import org.apache.zookeeper.ZooKeeper;
-import org.apache.zookeeper.KeeperException.Code;
 import org.apache.zookeeper.data.Stat;
 import org.apache.zookeeper.proto.CreateResponse;
 import org.apache.zookeeper.proto.CreateTTLRequest;
@@ -36,8 +35,8 @@ import org.apache.zookeeper.test.ClientBase;
 import org.junit.Assert;
 import org.junit.Test;
 
-import java.io.IOException;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicLong;
@@ -45,21 +44,25 @@ import java.util.concurrent.atomic.AtomicLong;
 public class CreateTTLTest extends ClientBase {
     private TestableZooKeeper zk;
 
+    private static final Collection<String> disabledTests = Collections.singleton("testDisabled");
+
     @Override
     public void setUp() throws Exception {
-        super.setUp();
+        System.setProperty(EphemeralType.EXTENDED_TYPES_ENABLED_PROPERTY, disabledTests.contains(getTestName()) ? "false" : "true");
+        super.setUpWithServerId(254);
         zk = createClient();
     }
 
     @Override
     public void tearDown() throws Exception {
+        System.clearProperty(EphemeralType.EXTENDED_TYPES_ENABLED_PROPERTY);
         super.tearDown();
         zk.close();
     }
 
     @Test
     public void testCreate()
-            throws IOException, KeeperException, InterruptedException {
+            throws KeeperException, InterruptedException {
         Stat stat = new Stat();
         zk.create("/foo", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_WITH_TTL, stat, 100);
         Assert.assertEquals(0, stat.getEphemeralOwner());
@@ -87,11 +90,35 @@ public class CreateTTLTest extends ClientBase {
                             r.getErr(), Code.BADARGUMENTS.intValue());
         Assert.assertNull("An invalid CreateTTLRequest should not result in znode creation",
                           zk.exists(path, false));
+
+        request = new CreateTTLRequest(path, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE,
+                CreateMode.PERSISTENT_WITH_TTL.toFlag(), EphemeralType.TTL.maxValue() + 1);
+        response = new CreateResponse();
+        r = zk.submitRequest(h, request, response, null);
+        Assert.assertEquals("An invalid CreateTTLRequest should throw BadArguments",
+                r.getErr(), Code.BADARGUMENTS.intValue());
+        Assert.assertNull("An invalid CreateTTLRequest should not result in znode creation",
+                zk.exists(path, false));
+    }
+
+    @Test
+    public void testMaxTTLs() throws InterruptedException, KeeperException {
+        RequestHeader h = new RequestHeader(1, ZooDefs.OpCode.createTTL);
+
+        String path = "/bad_ttl";
+        CreateTTLRequest request = new CreateTTLRequest(path, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE,
+                                                        CreateMode.PERSISTENT_WITH_TTL.toFlag(), EphemeralType.TTL.maxValue());
+        CreateResponse response = new CreateResponse();
+        ReplyHeader r = zk.submitRequest(h, request, response, null);
+        Assert.assertEquals("EphemeralType.getMaxTTL() should succeed",
+                            r.getErr(), Code.OK.intValue());
+        Assert.assertNotNull("Node should exist",
+                          zk.exists(path, false));
     }
 
     @Test
     public void testCreateSequential()
-            throws IOException, KeeperException, InterruptedException {
+            throws KeeperException, InterruptedException {
         Stat stat = new Stat();
         String path = zk.create("/foo", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL_WITH_TTL, stat, 100);
         Assert.assertEquals(0, stat.getEphemeralOwner());
@@ -108,7 +135,7 @@ public class CreateTTLTest extends ClientBase {
 
     @Test
     public void testCreateAsync()
-            throws IOException, KeeperException, InterruptedException {
+            throws KeeperException, InterruptedException {
         AsyncCallback.Create2Callback callback = new AsyncCallback.Create2Callback() {
             @Override
             public void processResult(int rc, String path, Object ctx, String name, Stat stat) {
@@ -129,7 +156,7 @@ public class CreateTTLTest extends ClientBase {
 
     @Test
     public void testModifying()
-            throws IOException, KeeperException, InterruptedException {
+            throws KeeperException, InterruptedException {
         Stat stat = new Stat();
         zk.create("/foo", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_WITH_TTL, stat, 100);
         Assert.assertEquals(0, stat.getEphemeralOwner());
@@ -153,7 +180,7 @@ public class CreateTTLTest extends ClientBase {
 
     @Test
     public void testMulti()
-            throws IOException, KeeperException, InterruptedException {
+            throws KeeperException, InterruptedException {
         Op createTtl = Op.create("/a", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_WITH_TTL, 100);
         Op createTtlSequential = Op.create("/b", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL_WITH_TTL, 200);
         Op createNonTtl = Op.create("/c", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
@@ -222,6 +249,12 @@ public class CreateTTLTest extends ClientBase {
         }
     }
 
+    @Test(expected = KeeperException.UnimplementedException.class)
+    public void testDisabled() throws KeeperException, InterruptedException {
+        // note, setUp() enables this test based on the test name
+        zk.create("/foo", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_WITH_TTL, new Stat(), 100);
+    }
+
     private ContainerManager newContainerManager(final AtomicLong fakeElapsed) {
         return new ContainerManager(serverFactory.getZooKeeperServer()
                 .getZKDatabase(), serverFactory.getZooKeeperServer().firstProcessor, 1, 100) {

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/282dc836/src/java/test/org/apache/zookeeper/server/Emulate353TTLTest.java
----------------------------------------------------------------------
diff --git a/src/java/test/org/apache/zookeeper/server/Emulate353TTLTest.java b/src/java/test/org/apache/zookeeper/server/Emulate353TTLTest.java
new file mode 100644
index 0000000..4d7906e
--- /dev/null
+++ b/src/java/test/org/apache/zookeeper/server/Emulate353TTLTest.java
@@ -0,0 +1,95 @@
+/**
+ * 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.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.TestableZooKeeper;
+import org.apache.zookeeper.ZooDefs;
+import org.apache.zookeeper.data.Stat;
+import org.apache.zookeeper.test.ClientBase;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+public class Emulate353TTLTest extends ClientBase {
+    private TestableZooKeeper zk;
+
+    @Override
+    public void setUp() throws Exception {
+        System.setProperty(EphemeralType.EXTENDED_TYPES_ENABLED_PROPERTY, "true");
+        System.setProperty(EphemeralType.TTL_3_5_3_EMULATION_PROPERTY, "true");
+        super.setUp();
+        zk = createClient();
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        System.clearProperty(EphemeralType.EXTENDED_TYPES_ENABLED_PROPERTY);
+        System.clearProperty(EphemeralType.TTL_3_5_3_EMULATION_PROPERTY);
+        super.tearDown();
+        zk.close();
+    }
+
+    @Test
+    public void testCreate()
+            throws KeeperException, InterruptedException {
+        Stat stat = new Stat();
+        zk.create("/foo", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_WITH_TTL, stat, 100);
+        Assert.assertEquals(0, stat.getEphemeralOwner());
+
+        final AtomicLong fakeElapsed = new AtomicLong(0);
+        ContainerManager containerManager = newContainerManager(fakeElapsed);
+        containerManager.checkContainers();
+        Assert.assertNotNull("Ttl node should not have been deleted yet", zk.exists("/foo", false));
+
+        fakeElapsed.set(1000);
+        containerManager.checkContainers();
+        Assert.assertNull("Ttl node should have been deleted", zk.exists("/foo", false));
+    }
+
+    @Test
+    public void test353TTL()
+            throws KeeperException, InterruptedException {
+        DataTree dataTree = serverFactory.zkServer.getZKDatabase().dataTree;
+        long ephemeralOwner = OldEphemeralType.ttlToEphemeralOwner(100);
+        dataTree.createNode("/foo", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, ephemeralOwner,
+                dataTree.getNode("/").stat.getCversion()+1, 1, 1);
+
+        final AtomicLong fakeElapsed = new AtomicLong(0);
+        ContainerManager containerManager = newContainerManager(fakeElapsed);
+        containerManager.checkContainers();
+        Assert.assertNotNull("Ttl node should not have been deleted yet", zk.exists("/foo", false));
+
+        fakeElapsed.set(1000);
+        containerManager.checkContainers();
+        Assert.assertNull("Ttl node should have been deleted", zk.exists("/foo", false));
+    }
+
+    private ContainerManager newContainerManager(final AtomicLong fakeElapsed) {
+        return new ContainerManager(serverFactory.getZooKeeperServer()
+                .getZKDatabase(), serverFactory.getZooKeeperServer().firstProcessor, 1, 100) {
+            @Override
+            protected long getElapsed(DataNode node) {
+                return fakeElapsed.get();
+            }
+        };
+    }
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/282dc836/src/java/test/org/apache/zookeeper/server/EphemeralTypeTest.java
----------------------------------------------------------------------
diff --git a/src/java/test/org/apache/zookeeper/server/EphemeralTypeTest.java b/src/java/test/org/apache/zookeeper/server/EphemeralTypeTest.java
index 2e6b3c7..40863f5 100644
--- a/src/java/test/org/apache/zookeeper/server/EphemeralTypeTest.java
+++ b/src/java/test/org/apache/zookeeper/server/EphemeralTypeTest.java
@@ -19,17 +19,29 @@
 package org.apache.zookeeper.server;
 
 import org.apache.zookeeper.CreateMode;
+import org.junit.After;
 import org.junit.Assert;
+import org.junit.Before;
 import org.junit.Test;
 
 public class EphemeralTypeTest {
+    @Before
+    public void setUp() {
+        System.setProperty(EphemeralType.EXTENDED_TYPES_ENABLED_PROPERTY, "true");
+    }
+
+    @After
+    public void tearDown() {
+        System.clearProperty(EphemeralType.EXTENDED_TYPES_ENABLED_PROPERTY);
+    }
+
     @Test
     public void testTtls() {
-        long ttls[] = {100, 1, EphemeralType.MAX_TTL};
+        long ttls[] = {100, 1, EphemeralType.TTL.maxValue()};
         for (long ttl : ttls) {
-            long ephemeralOwner = EphemeralType.ttlToEphemeralOwner(ttl);
+            long ephemeralOwner = EphemeralType.TTL.toEphemeralOwner(ttl);
             Assert.assertEquals(EphemeralType.TTL, EphemeralType.get(ephemeralOwner));
-            Assert.assertEquals(ttl, EphemeralType.getTTL(ephemeralOwner));
+            Assert.assertEquals(ttl, EphemeralType.TTL.getValue(ephemeralOwner));
         }
 
         EphemeralType.validateTTL(CreateMode.PERSISTENT_WITH_TTL, 100);
@@ -55,4 +67,18 @@ public class EphemeralTypeTest {
         Assert.assertEquals(EphemeralType.NORMAL, EphemeralType.get(1));
         Assert.assertEquals(EphemeralType.NORMAL, EphemeralType.get(Long.MAX_VALUE));
     }
+
+    @Test
+    public void testServerIds() {
+        for ( int i = 0; i < 255; ++i ) {
+            Assert.assertEquals(EphemeralType.NORMAL, EphemeralType.get(SessionTrackerImpl.initializeNextSession(i)));
+        }
+        try {
+            Assert.assertEquals(EphemeralType.TTL, EphemeralType.get(SessionTrackerImpl.initializeNextSession(255)));
+            Assert.fail("Should have thrown IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+        Assert.assertEquals(EphemeralType.NORMAL, EphemeralType.get(SessionTrackerImpl.initializeNextSession(EphemeralType.MAX_EXTENDED_SERVER_ID)));
+    }
 }

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/282dc836/src/java/test/org/apache/zookeeper/server/ServerIdTest.java
----------------------------------------------------------------------
diff --git a/src/java/test/org/apache/zookeeper/server/ServerIdTest.java b/src/java/test/org/apache/zookeeper/server/ServerIdTest.java
new file mode 100644
index 0000000..a04c37b
--- /dev/null
+++ b/src/java/test/org/apache/zookeeper/server/ServerIdTest.java
@@ -0,0 +1,119 @@
+/**
+ * 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.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.TestableZooKeeper;
+import org.apache.zookeeper.ZKParameterized;
+import org.apache.zookeeper.ZooDefs;
+import org.apache.zookeeper.data.Stat;
+import org.apache.zookeeper.test.ClientBase;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(Parameterized.class)
+@Parameterized.UseParametersRunnerFactory(ZKParameterized.RunnerFactory.class)
+public class ServerIdTest extends ClientBase {
+    private final TestType testType;
+
+    private static class TestType {
+        final boolean ttlsEnabled;
+        final int serverId;
+
+        TestType(boolean ttlsEnabled, int serverId) {
+            this.ttlsEnabled = ttlsEnabled;
+            this.serverId = serverId;
+        }
+    }
+
+    @Parameterized.Parameters
+    public static List<TestType> data() {
+        List<TestType> testTypes = new ArrayList<>();
+        for ( boolean ttlsEnabled : new boolean[]{true, false} ) {
+            for ( int serverId = 0; serverId <= 255; ++serverId ) {
+                testTypes.add(new TestType(ttlsEnabled, serverId));
+            }
+        }
+        return testTypes;
+    }
+
+    @After
+    @Override
+    public void tearDown() throws Exception {
+        super.tearDown();
+        System.clearProperty("zookeeper.extendedTypesEnabled");
+    }
+
+    public ServerIdTest(TestType testType) {
+        this.testType = testType;
+    }
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        System.setProperty("zookeeper.extendedTypesEnabled", Boolean.toString(testType.ttlsEnabled));
+        LOG.info("ttlsEnabled: {} - ServerId: {}", testType.ttlsEnabled, testType.serverId);
+        try {
+            super.setUpWithServerId(testType.serverId);
+        } catch (RuntimeException e) {
+            if ( testType.ttlsEnabled && (testType.serverId >= EphemeralType.MAX_EXTENDED_SERVER_ID) ) {
+                return; // expected
+            }
+            throw e;
+        }
+    }
+
+    @Test
+    public void doTest() throws Exception {
+        if ( testType.ttlsEnabled && (testType.serverId >= EphemeralType.MAX_EXTENDED_SERVER_ID) ) {
+            return;
+        }
+
+        TestableZooKeeper zk = null;
+        try {
+            zk = createClient();
+
+            zk.create("/foo", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
+            zk.delete("/foo", -1);
+
+            if ( testType.ttlsEnabled ) {
+                zk.create("/foo", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_WITH_TTL, new Stat(), 1000);  // should work
+            } else {
+                try {
+                    zk.create("/foo", new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_WITH_TTL, new Stat(), 1000);
+                    Assert.fail("Should have thrown KeeperException.UnimplementedException");
+                } catch (KeeperException.UnimplementedException e) {
+                    // expected
+                }
+            }
+        } finally {
+            if ( zk != null ) {
+                zk.close();
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/282dc836/src/java/test/org/apache/zookeeper/test/ClientBase.java
----------------------------------------------------------------------
diff --git a/src/java/test/org/apache/zookeeper/test/ClientBase.java b/src/java/test/org/apache/zookeeper/test/ClientBase.java
index b9f96e0..8111099 100644
--- a/src/java/test/org/apache/zookeeper/test/ClientBase.java
+++ b/src/java/test/org/apache/zookeeper/test/ClientBase.java
@@ -414,11 +414,12 @@ public abstract class ClientBase extends ZKTestCase {
      * Starting the given server instance
      */
     public static void startServerInstance(File dataDir,
-            ServerCnxnFactory factory, String hostPort) throws IOException,
+            ServerCnxnFactory factory, String hostPort, int serverId) throws IOException,
             InterruptedException {
         final int port = getPort(hostPort);
         LOG.info("STARTING server instance 127.0.0.1:{}", port);
         ZooKeeperServer zks = new ZooKeeperServer(dataDir, dataDir, 3000);
+        zks.setCreateSessionTrackerServerId(serverId);
         factory.startup(zks);
         Assert.assertTrue("waiting for server up", ClientBase.waitForServerUp(
                 "127.0.0.1:" + port, CONNECTION_TIMEOUT, factory.isSecure()));
@@ -427,7 +428,7 @@ public abstract class ClientBase extends ZKTestCase {
     /**
      * This method instantiates a new server. Starting of the server
      * instance has been moved to a separate method
-     * {@link ClientBase#startServerInstance(File, ServerCnxnFactory, String)}.
+     * {@link ClientBase#startServerInstance(File, ServerCnxnFactory, String, int)}.
      * Because any exception on starting the server would leave the server
      * running and the caller would not be able to shutdown the instance. This
      * may affect other test cases.
@@ -496,6 +497,10 @@ public abstract class ClientBase extends ZKTestCase {
 
     @Before
     public void setUp() throws Exception {
+        setUpWithServerId(1);
+    }
+
+    protected void setUpWithServerId(int serverId) throws Exception {
         /* some useful information - log the number of fds used before
          * and after a test is run. Helps to verify we are freeing resources
          * correctly. Unfortunately this only works on unix systems (the
@@ -516,16 +521,20 @@ public abstract class ClientBase extends ZKTestCase {
 
         tmpDir = createTmpDir(BASETEST, true);
 
-        startServer();
+        startServer(serverId);
 
         LOG.info("Client test setup finished");
     }
 
     protected void startServer() throws Exception {
+        startServer(1);
+    }
+
+    private void startServer(int serverId) throws Exception {
         LOG.info("STARTING server");
         serverFactory = createNewServerInstance(serverFactory, hostPort,
                 maxCnxns);
-        startServerInstance(tmpDir, serverFactory, hostPort);
+        startServerInstance(tmpDir, serverFactory, hostPort, serverId);
         // ensure that server and data bean are registered
         Set<ObjectName> children = JMXEnv.ensureParent("InMemoryDataTree",
                 "StandaloneServer_port");

http://git-wip-us.apache.org/repos/asf/zookeeper/blob/282dc836/src/java/test/org/apache/zookeeper/test/TruncateTest.java
----------------------------------------------------------------------
diff --git a/src/java/test/org/apache/zookeeper/test/TruncateTest.java b/src/java/test/org/apache/zookeeper/test/TruncateTest.java
index 9b9fd7a..cae2d9f 100644
--- a/src/java/test/org/apache/zookeeper/test/TruncateTest.java
+++ b/src/java/test/org/apache/zookeeper/test/TruncateTest.java
@@ -151,7 +151,7 @@ public class TruncateTest extends ZKTestCase {
         int maxCnxns = 100;
         ServerCnxnFactory factory = ClientBase.createNewServerInstance(null,
                 hostPort, maxCnxns);
-        ClientBase.startServerInstance(dataDir1, factory, hostPort);
+        ClientBase.startServerInstance(dataDir1, factory, hostPort, 1);
         ClientBase.shutdownServerInstance(factory, hostPort);
 
         // standalone starts with 0 epoch while quorum starts with 1
@@ -160,7 +160,7 @@ public class TruncateTest extends ZKTestCase {
         origfile.renameTo(newfile);
 
         factory = ClientBase.createNewServerInstance(null, hostPort, maxCnxns);
-        ClientBase.startServerInstance(dataDir1, factory, hostPort);
+        ClientBase.startServerInstance(dataDir1, factory, hostPort, 1);
 
         ZooKeeper zk = ClientBase.createZKClient(hostPort, 15000);
         for(int i = 0; i < 50; i++) {


Mime
View raw message