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.