You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by vi...@apache.org on 2014/02/11 20:30:51 UTC

[1/5] git commit: Fixed AWAIT_* macros which can hang forever if the Clock is paused.

Updated Branches:
  refs/heads/master acee3fec1 -> c0bd75c92


Fixed AWAIT_* macros which can hang forever if the Clock is paused.

- Note that this fix acts as a safeguard to prevent AWAIT_* in tests
  running forever. It does not eliminate the need to advance the clock
  in loops in some tests.
- I found putting Clock::advance() (optionally with os::sleep) in
  AWAIT_* too implicit and takes control over the clock from
  developers who write tests. (See related ticket
  https://issues.apache.org/jira/browse/MESOS-827, will explain
  elsewhere.)

Review: https://reviews.apache.org/r/17805


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/92e2e51b
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/92e2e51b
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/92e2e51b

Branch: refs/heads/master
Commit: 92e2e51b883859163e4af8dbaa5cc0a5095154f2
Parents: acee3fe
Author: Jiang Yan Xu <ya...@jxu.me>
Authored: Tue Feb 11 11:16:52 2014 -0800
Committer: Vinod Kone <vi...@twitter.com>
Committed: Tue Feb 11 11:16:52 2014 -0800

----------------------------------------------------------------------
 3rdparty/libprocess/include/process/gtest.hpp | 48 ++++++++++++++++++++--
 1 file changed, 45 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/92e2e51b/3rdparty/libprocess/include/process/gtest.hpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/include/process/gtest.hpp b/3rdparty/libprocess/include/process/gtest.hpp
index 753ef74..11db008 100644
--- a/3rdparty/libprocess/include/process/gtest.hpp
+++ b/3rdparty/libprocess/include/process/gtest.hpp
@@ -11,6 +11,8 @@
 
 #include <stout/duration.hpp>
 #include <stout/option.hpp>
+#include <stout/os.hpp>
+#include <stout/stopwatch.hpp>
 
 namespace process {
 
@@ -37,8 +39,48 @@ private:
   ClockTestEventListener() {}
 };
 
+
+namespace internal {
+
+// Returns true if the future becomes ready, discarded, or failed
+// after the wait.
+template <typename T>
+bool await(const process::Future<T>& future, const Duration& duration)
+{
+  if (!process::Clock::paused()) {
+    return future.await(duration);
+  }
+
+  // If the clock is paused, no new timers will expire.
+  // Future::await(duration) may hang forever because it depends on
+  // a timer to expire after 'duration'. We instead ensure all
+  // expired timers are flushed and check if the future is satisfied.
+  Stopwatch stopwatch;
+  stopwatch.start();
+
+  // Settle to make sure all expired timers are executed (not
+  // necessarily finished, see below).
+  process::Clock::settle();
+
+  while (future.isPending() && stopwatch.elapsed() < duration) {
+    // Synchronous operations and asynchronous process::Process
+    // operations should finish when the above 'settle()' returns.
+    // Other types of async operations such as io::write() may not.
+    // Therefore we wait the specified duration for it to complete.
+    // Note that nothing prevents the operations to schedule more
+    // timeouts for some time in the future. These timeouts will
+    // never be executed due to the paused process::Clock. In this
+    // case we return after the stopwatch (system clock) runs out.
+    os::sleep(Milliseconds(10));
+  }
+
+  return !future.isPending();
+}
+
+} // namespace internal {
 } // namespace process {
 
+
 template <typename T>
 ::testing::AssertionResult AwaitAssertReady(
     const char* expr,
@@ -46,7 +88,7 @@ template <typename T>
     const process::Future<T>& actual,
     const Duration& duration)
 {
-  if (!actual.await(duration)) {
+  if (!process::internal::await(actual, duration)) {
     return ::testing::AssertionFailure()
       << "Failed to wait " << duration << " for " << expr;
   } else if (actual.isDiscarded()) {
@@ -68,7 +110,7 @@ template <typename T>
     const process::Future<T>& actual,
     const Duration& duration)
 {
-  if (!actual.await(duration)) {
+  if (!process::internal::await(actual, duration)) {
     return ::testing::AssertionFailure()
       << "Failed to wait " << duration << " for " << expr;
   } else if (actual.isDiscarded()) {
@@ -90,7 +132,7 @@ template <typename T>
     const process::Future<T>& actual,
     const Duration& duration)
 {
-  if (!actual.await(duration)) {
+  if (!process::internal::await(actual, duration)) {
     return ::testing::AssertionFailure()
       << "Failed to wait " << duration << " for " << expr;
   } else if (actual.isFailed()) {


[5/5] git commit: Removed an unnecessary intermediate ZooKeeper WatcherProcess.

Posted by vi...@apache.org.
Removed an unnecessary intermediate ZooKeeper WatcherProcess.

Review: https://reviews.apache.org/r/17440


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

Branch: refs/heads/master
Commit: c0bd75c92713cd85872f3cb5064fac57fe099b5f
Parents: c84d080
Author: Jiang Yan Xu <ya...@jxu.me>
Authored: Tue Feb 11 11:20:56 2014 -0800
Committer: Vinod Kone <vi...@twitter.com>
Committed: Tue Feb 11 11:20:56 2014 -0800

----------------------------------------------------------------------
 src/zookeeper/zookeeper.cpp | 132 +--------------------------------------
 src/zookeeper/zookeeper.hpp |  17 ++---
 2 files changed, 12 insertions(+), 137 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/c0bd75c9/src/zookeeper/zookeeper.cpp
----------------------------------------------------------------------
diff --git a/src/zookeeper/zookeeper.cpp b/src/zookeeper/zookeeper.cpp
index 5720f4c..f90515d 100644
--- a/src/zookeeper/zookeeper.cpp
+++ b/src/zookeeper/zookeeper.cpp
@@ -47,116 +47,6 @@ using std::string;
 using std::vector;
 
 
-// Singleton instance of WatcherProcessManager.
-class WatcherProcessManager;
-
-WatcherProcessManager* manager;
-
-
-// In order to make callbacks on Watcher, we create a proxy
-// WatcherProcess. The ZooKeeperImpl (defined below) dispatches
-// "events" to the WatcherProcess which then invokes
-// Watcher::process. The major benefit of this approach is that a
-// WatcherProcess lifetime can precisely match the lifetime of a
-// Watcher, so the ZooKeeperImpl won't end up calling into an object
-// that has been deleted. In the worst case, the ZooKeeperImpl will
-// dispatch to a dead WatcherProcess, which will just get dropped on
-// the floor. In addition, the callbacks in the Watcher can manipulate
-// the ZooKeeper object freely, calling delete on it if necessary
-// (e.g., after a session expiration). We wanted to keep the Watcher
-// interface clean and simple, so rather than add a member in Watcher
-// that points to a WatcherProcess instance (or points to a
-// WatcherImpl), we choose to create a WatcherProcessManager that
-// stores the Watcher and WatcherProcess associations. The
-// WatcherProcessManager is akin to having a shared dictionary or
-// hashtable and using locks to access it rather then sending and
-// receiving messages. Their is probably a performance hit here, but
-// it would be interesting to see how bad the perforamnce is across a
-// range of low and high-contention states.
-class WatcherProcess : public Process<WatcherProcess>
-{
-public:
-  WatcherProcess(Watcher* watcher) : watcher(watcher) {}
-
-  void event(ZooKeeper* zk, int type, int state, const string& path)
-  {
-    watcher->process(zk, type, state, path);
-  }
-
-private:
-  Watcher* watcher;
-};
-
-
-class WatcherProcessManager : public Process<WatcherProcessManager>
-{
-public:
-  WatcherProcess* create(Watcher* watcher)
-  {
-    WatcherProcess* process = new WatcherProcess(watcher);
-    spawn(process);
-    processes[watcher] = process;
-    return process;
-  }
-
-  bool destroy(Watcher* watcher)
-  {
-   if (processes.count(watcher) > 0) {
-      WatcherProcess* process = processes[watcher];
-      processes.erase(watcher);
-      process::terminate(process->self());
-      process::wait(process->self());
-      delete process;
-      return true;
-    }
-
-    return false;
-  }
-
-  PID<WatcherProcess> lookup(Watcher* watcher)
-  {
-    if (processes.count(watcher) > 0) {
-      return processes[watcher]->self();
-    }
-
-    return PID<WatcherProcess>();
-  }
-
-private:
-  map<Watcher*, WatcherProcess*> processes;
-};
-
-
-Watcher::Watcher()
-{
-  // Confirm we have created the WatcherProcessManager.
-  static process::Once* initialize = new process::Once();
-
-  // Confirm everything is initialized.
-  if (!initialize->once()) {
-    manager = new WatcherProcessManager();
-    process::spawn(manager);
-    initialize->done();
-  }
-
-  WatcherProcess* process =
-    process::dispatch(manager->self(),
-                      &WatcherProcessManager::create,
-                      this).get();
-
-  if (process == NULL) {
-    fatal("failed to initialize Watcher");
-  }
-}
-
-
-Watcher::~Watcher()
-{
-  process::dispatch(manager->self(), &WatcherProcessManager::destroy, this)
-    .await();
-}
-
-
 class ZooKeeperImpl
 {
 public:
@@ -172,17 +62,6 @@ public:
       LOG(FATAL) << "Cannot instantiate ZooKeeper with NULL watcher";
     }
 
-    // Lookup PID of the WatcherProcess associated with the Watcher.
-    pid = process::dispatch(manager->self(),
-                            &WatcherProcessManager::lookup,
-                            watcher).get();
-
-    // N.B. The Watcher and thus WatcherProcess may already be gone,
-    // in which case, each dispatch to the WatcherProcess that we do
-    // will just get dropped on the floor.
-
-    // TODO(benh): Link with WatcherProcess PID?
-
     zh = zookeeper_init(
         servers.c_str(),
         event,
@@ -351,6 +230,8 @@ public:
   }
 
 private:
+  // This method is registered as a watcher callback function and is
+  // invoked by a single ZooKeeper event thread.
   static void event(
       zhandle_t* zh,
       int type,
@@ -359,13 +240,7 @@ private:
       void* ctx)
   {
     ZooKeeperImpl* impl = static_cast<ZooKeeperImpl*>(ctx);
-    process::dispatch(
-        impl->pid,
-        &WatcherProcess::event,
-        impl->zk,
-        type,
-        state,
-        string(path));
+    impl->watcher->process(impl->zk, type, state, string(path));
   }
 
 
@@ -490,7 +365,6 @@ private:
   zhandle_t* zh; // ZooKeeper connection handle.
 
   Watcher* watcher; // Associated Watcher instance.
-  PID<WatcherProcess> pid; // PID of WatcherProcess that invokes Watcher.
 };
 
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/c0bd75c9/src/zookeeper/zookeeper.hpp
----------------------------------------------------------------------
diff --git a/src/zookeeper/zookeeper.hpp b/src/zookeeper/zookeeper.hpp
index f50aca6..242e2de 100644
--- a/src/zookeeper/zookeeper.hpp
+++ b/src/zookeeper/zookeeper.hpp
@@ -45,19 +45,20 @@ class ZooKeeperImpl;
  * a client handles these events by registering a callback object with
  * the client. The callback object is expected to be an instance of a
  * class that implements Watcher interface.
+ *
+ * Note that the watcher is invoked by ZooKeeper from a single thread.
+ * See http://zookeeper.apache.org/doc/trunk/zookeeperProgrammers.html#C+Binding
  */
 class Watcher
 {
 public:
-  virtual void process(ZooKeeper *zk,
-		       int type,
-		       int state,
-		       const std::string &path) = 0;
+  virtual void process(
+      ZooKeeper *zk,
+      int type,
+      int state,
+      const std::string &path) = 0;
 
-  Watcher();
-  virtual ~Watcher();
-  Watcher(const Watcher &that);
-  Watcher & operator = (const Watcher &that);
+  virtual ~Watcher() {}
 };
 
 


[4/5] git commit: Fixed flaky MasterZooKeeperTest.LostZooKeeperCluster.

Posted by vi...@apache.org.
Fixed flaky MasterZooKeeperTest.LostZooKeeperCluster.

Review: https://reviews.apache.org/r/17864


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

Branch: refs/heads/master
Commit: c84d080266c4d2d9e15b73e08cdb848006488632
Parents: 9ad3c04
Author: Jiang Yan Xu <ya...@jxu.me>
Authored: Tue Feb 11 11:20:37 2014 -0800
Committer: Vinod Kone <vi...@twitter.com>
Committed: Tue Feb 11 11:20:37 2014 -0800

----------------------------------------------------------------------
 src/tests/master_tests.cpp | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/c84d0802/src/tests/master_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/master_tests.cpp b/src/tests/master_tests.cpp
index 815149a..70dab6f 100644
--- a/src/tests/master_tests.cpp
+++ b/src/tests/master_tests.cpp
@@ -1481,7 +1481,7 @@ TEST_F(MasterZooKeeperTest, LostZooKeeperCluster)
   // Need to drop these two dispatches because otherwise the master
   // will EXIT.
   Future<Nothing> masterDetected = DROP_DISPATCH(_, &Master::detected);
-  DROP_DISPATCH(_, &Master::lostCandidacy);
+  Future<Nothing> lostCandidacy = DROP_DISPATCH(_, &Master::lostCandidacy);
 
   Future<Nothing> slaveDetected = FUTURE_DISPATCH(_, &Slave::detected);
 
@@ -1491,7 +1491,8 @@ TEST_F(MasterZooKeeperTest, LostZooKeeperCluster)
 
   while (schedulerDisconnected.isPending() ||
          masterDetected.isPending() ||
-         slaveDetected.isPending()) {
+         slaveDetected.isPending() ||
+         lostCandidacy.isPending()) {
     Clock::advance(MASTER_CONTENDER_ZK_SESSION_TIMEOUT);
     Clock::settle();
   }
@@ -1501,6 +1502,7 @@ TEST_F(MasterZooKeeperTest, LostZooKeeperCluster)
   // Master, slave and scheduler all lose the leading master.
   AWAIT_READY(schedulerDisconnected);
   AWAIT_READY(masterDetected);
+  AWAIT_READY(lostCandidacy);
   AWAIT_READY(slaveDetected);
 
   driver.stop();


[2/5] git commit: Removed the while loops in ZooKeeperTest.

Posted by vi...@apache.org.
Removed the while loops in ZooKeeperTest.

Review: https://reviews.apache.org/r/17806


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/44508c2c
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/44508c2c
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/44508c2c

Branch: refs/heads/master
Commit: 44508c2c7510b1980c502742c4fb3ff1a047a6b6
Parents: 92e2e51
Author: Jiang Yan Xu <ya...@jxu.me>
Authored: Tue Feb 11 11:18:04 2014 -0800
Committer: Vinod Kone <vi...@twitter.com>
Committed: Tue Feb 11 11:18:04 2014 -0800

----------------------------------------------------------------------
 src/tests/zookeeper_tests.cpp | 35 +++++++++++++++++++++++------------
 1 file changed, 23 insertions(+), 12 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/44508c2c/src/tests/zookeeper_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/zookeeper_tests.cpp b/src/tests/zookeeper_tests.cpp
index 615338a..be9fa06 100644
--- a/src/tests/zookeeper_tests.cpp
+++ b/src/tests/zookeeper_tests.cpp
@@ -213,16 +213,23 @@ TEST_F(ZooKeeperTest, LeaderDetectorTimeoutHandling)
 
   leader = detector.detect(leader.get());
 
+  Future<Nothing> reconnecting = FUTURE_DISPATCH(
+      group.process->self(),
+      &GroupProcess::reconnecting);
+
   server->shutdownNetwork();
 
+  AWAIT_READY(reconnecting);
+
   Clock::pause();
 
-  // We may need to advance multiple times because we could have
-  // advanced the clock before the timer in Group starts.
-  while (leader.isPending()) {
-    Clock::advance(timeout);
-    Clock::settle();
-  }
+  // Settle to make sure 'reconnecting' schedules the timeout before
+  // we advance.
+  Clock::settle();
+  Clock::advance(timeout);
+
+  AWAIT_READY(leader);
+
   Clock::resume();
 
   // The detect operation times out.
@@ -351,16 +358,20 @@ TEST_F(ZooKeeperTest, LeaderContender)
   AWAIT_READY(candidated);
   lostCandidacy = candidated.get();
 
+  Future<Nothing> reconnecting = FUTURE_DISPATCH(
+      group.process->self(),
+      &GroupProcess::reconnecting);
+
   server->shutdownNetwork();
 
+  AWAIT_READY(reconnecting);
+
   Clock::pause();
 
-  // We may need to advance multiple times because we could have
-  // advanced the clock before the timer in Group starts.
-  while (lostCandidacy.isPending()) {
-    Clock::advance(timeout);
-    Clock::settle();
-  }
+  // Settle to make sure 'reconnecting()' schedules the timeout
+  // before we advance.
+  Clock::settle();
+  Clock::advance(timeout);
 
   // Server failure results in candidacy loss.
   AWAIT_READY(lostCandidacy);


[3/5] git commit: Made Group's state transitions monotonic except when expiration occurs, in which case it is reset to the initial state (DISCONNECTED).

Posted by vi...@apache.org.
Made Group's state transitions monotonic except when expiration occurs,
in which case it is reset to the initial state (DISCONNECTED).

- Reconnecting does not reset the state to CONNECTING now. The steps
  the group has taken to set up the group are remembered (thus not
  redone) across reconnections.
- Improved comments on state transitions.
- Added the DISCONNECTED state back for the parity between Group and
  ZooKeeperStorage.

Review: https://reviews.apache.org/r/17858


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/9ad3c042
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/9ad3c042
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/9ad3c042

Branch: refs/heads/master
Commit: 9ad3c042cf35dc1685502bd31fdb2a6e4a62e3e8
Parents: 44508c2
Author: Jiang Yan Xu <ya...@jxu.me>
Authored: Tue Feb 11 11:18:21 2014 -0800
Committer: Vinod Kone <vi...@twitter.com>
Committed: Tue Feb 11 11:18:21 2014 -0800

----------------------------------------------------------------------
 src/zookeeper/group.cpp | 62 +++++++++++++++++++++++++++++++++-----------
 src/zookeeper/group.hpp | 12 ++++++++-
 2 files changed, 58 insertions(+), 16 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/9ad3c042/src/zookeeper/group.cpp
----------------------------------------------------------------------
diff --git a/src/zookeeper/group.cpp b/src/zookeeper/group.cpp
index a61a384..793763e 100644
--- a/src/zookeeper/group.cpp
+++ b/src/zookeeper/group.cpp
@@ -82,7 +82,7 @@ GroupProcess::GroupProcess(
         : ZOO_OPEN_ACL_UNSAFE),
     watcher(NULL),
     zk(NULL),
-    state(CONNECTING),
+    state(DISCONNECTED),
     retrying(false)
 {}
 
@@ -101,7 +101,7 @@ GroupProcess::GroupProcess(
         : ZOO_OPEN_ACL_UNSAFE),
     watcher(NULL),
     zk(NULL),
-    state(CONNECTING),
+    state(DISCONNECTED),
     retrying(false)
 {}
 
@@ -310,7 +310,22 @@ void GroupProcess::connected(bool reconnect)
   LOG(INFO) << "Group process (" << self() << ") "
             << (reconnect ? "reconnected" : "connected") << " to ZooKeeper";
 
-  state = CONNECTED;
+  if (!reconnect) {
+    // This is the first time the ZooKeeper client connects to
+    // ZooKeeper service. (It could be also the first time for the
+    // group or after session expiration which causes a new ZooKeeper
+    // client instance to be created.)
+    CHECK_EQ(state, CONNECTING);
+    state = CONNECTED;
+  } else {
+    // This means we are reconnecting within the same ZooKeeper
+    // session. We could have completed authenticate() or create()
+    // before we lost the connection (thus the state can be the any
+    // of the following three) so 'sync()' below will check the state
+    // and only execute necessary operations accordingly.
+    CHECK(state == CONNECTED || state == AUTHENTICATED || state == READY)
+      << state;
+  }
 
   // Cancel and cleanup the reconnect timer (if necessary).
   if (timer.isSome()) {
@@ -399,7 +414,10 @@ void GroupProcess::reconnecting()
 
   LOG(INFO) << "Lost connection to ZooKeeper, attempting to reconnect ...";
 
-  state = CONNECTING;
+  // Set 'retrying' to false to prevent retry() from executing sync()
+  // before the group reconnects to ZooKeeper. The group will sync
+  // with ZooKeeper after it is connected.
+  retrying = false;
 
   // ZooKeeper won't tell us of a session expiration until we
   // reconnect, which could occur much much later than the session was
@@ -447,6 +465,9 @@ void GroupProcess::expired()
     return;
   }
 
+  // Cancel the retries. Group will sync() after it reconnects to ZK.
+  retrying = false;
+
   // Cancel and cleanup the reconnect timer (if necessary).
   if (timer.isSome()) {
     Timer::cancel(timer.get());
@@ -482,12 +503,14 @@ void GroupProcess::expired()
   // then. We could imagine doing this for owned memberships too, but
   // for now we proactively cancel them above.
 
-  state = CONNECTING;
+  state = DISCONNECTED;
 
   delete CHECK_NOTNULL(zk);
   delete CHECK_NOTNULL(watcher);
   watcher = new ProcessWatcher<GroupProcess>(self());
   zk = new ZooKeeper(servers, timeout, watcher);
+
+  state = CONNECTING;
 }
 
 
@@ -754,9 +777,12 @@ Try<bool> GroupProcess::sync()
     << pending.joins.size() << ", " << pending.cancels.size() << ", "
     << pending.datas.size() << ")";
 
-  CHECK_NE(state, CONNECTING);
+  // The state may be CONNECTED or AUTHENTICATED if Group setup has
+  // not finished.
+  CHECK(state == CONNECTED || state == AUTHENTICATED || state == READY)
+    << state;
 
-  // Authenticate with ZK if not already created.
+  // Authenticate with ZK if not already authenticated.
   if (state == CONNECTED) {
     Try<bool> authenticated = authenticate();
     if (authenticated.isError() || !authenticated.get()) {
@@ -840,18 +866,21 @@ Try<bool> GroupProcess::sync()
 
 void GroupProcess::retry(const Duration& duration)
 {
-  CHECK(retrying);
+  if (!retrying) {
+    // Retry could be cancelled before it is scheduled.
+    return;
+  }
+
+  // We cancel the retries when the group aborts and when its ZK
+  // session expires so 'retrying' should be false in the condition
+  // check above.
+  CHECK(error.isNone());
+  CHECK(state == CONNECTED || state == CONNECTING || state == READY)
+    << state;
 
   // Will reset it to true if another retry is necessary.
   retrying = false;
 
-  if (error.isSome() || state == CONNECTING) {
-    // The delayed retry can be invoked while group has aborted or
-    // is trying to (re)connect to ZK. In this case we directly
-    // return and we will sync when group is connected to ZK.
-    return;
-  }
-
   Try<bool> synced = sync();
 
   if (synced.isError()) {
@@ -873,6 +902,9 @@ void GroupProcess::abort(const string& message)
 
   LOG(ERROR) << "Group aborting: " << message;
 
+  // Cancel the retries.
+  retrying = false;
+
   fail(&pending.joins, message);
   fail(&pending.cancels, message);
   fail(&pending.datas, message);

http://git-wip-us.apache.org/repos/asf/mesos/blob/9ad3c042/src/zookeeper/group.hpp
----------------------------------------------------------------------
diff --git a/src/zookeeper/group.hpp b/src/zookeeper/group.hpp
index e51ebb2..d1ead38 100644
--- a/src/zookeeper/group.hpp
+++ b/src/zookeeper/group.hpp
@@ -246,7 +246,17 @@ private:
   Watcher* watcher;
   ZooKeeper* zk;
 
-  enum State {     // Group connection state.
+  // Group connection state.
+  // Normal state transitions:
+  //   DISCONNECTED -> CONNECTING -> CONNECTED -> AUTHENTICATED
+  //   -> READY.
+  // Reconnection does not change the current state and the state is
+  // only reset to DISCONNECTED after session expiration. Therefore
+  // the client's "progress" in setting up the group is preserved
+  // across reconnections. This means authenticate() and create() are
+  // only successfully executed once in one ZooKeeper session.
+  enum State {
+    DISCONNECTED,  // The initial state.
     CONNECTING,    // ZooKeeper connecting.
     CONNECTED,     // ZooKeeper connected but before group setup.
     AUTHENTICATED, // ZooKeeper connected and authenticated.