@@ -70,7 +70,15 @@ Revision history
- add str_error() call for translating errors to human-readable strings
- add state() method from #72702
-0.39 May 13, 2014
+0.39 May 13, 2015
- add ZSESSIONMOVED error constant
- bump ZooKeeper version dependency to 3.2.0
- Fix handling of non-integer-seconds watch timeouts
+
+0.40 May 23, 2015
+ - update docs better explain Net::ZooKeeper constructor error handling
+ - fix test failure from interference with other nodes
+
+0.41 Jun 17, 2015
+ - fix deadlocks caused by premature watch garbage collection. (#5)
+ - added set_ignore_session_events function
@@ -43,5 +43,5 @@
"web" : "https://github.com/mark-5/p5-net-zookeeper"
}
},
- "version" : "0.39"
+ "version" : "0.41"
}
@@ -21,4 +21,4 @@ resources:
bugtracker: https://github.com/mark-5/p5-net-zookeeper/issues
homepage: https://github.com/mark-5/p5-net-zookeeper
repository: https://github.com/mark-5/p5-net-zookeeper.git
-version: '0.39'
+version: '0.41'
@@ -26,7 +26,7 @@ package Net::ZooKeeper;
require Exporter;
require XSLoader;
-our $VERSION = '0.39';
+our $VERSION = '0.41';
our @ISA = qw(Exporter);
@@ -739,7 +739,7 @@ The following methods are defined for the Net::ZooKeeper class.
Creates a new Net::ZooKeeper handle object and attempts to
connect to the one of the servers of the given ZooKeeper
-cluster. As described in the L</Internal POSIX Threads> and
+cluster. On failure, undef will be returned and the error message will be accessible in "$!". As described in the L</Internal POSIX Threads> and
L</Connection Order> sections, the ZooKeeper client code will
create an IO thread which maintains the connection with a
regular "heartbeat" request. In the event of a connection error
@@ -1144,6 +1144,8 @@ watch object as the value of a C<'watch'> option to a
Net::ZooKeeper method; methods which accept a C<'watch'> option
are C<exists()>, C<get_children()>, and C<get()>.
+By default, the C<wait()> method will return when session events(such as disconnecting and reconnecting from a server) are triggered. If a watch should ignore these session events, call C<Net::ZooKeeper::set_ignore_session_events(1)>.
+
When the C<wait()> method is invoked with a C<'timeout'>
option, it waits for no more than the number of milliseconds
specified by the C<'timeout'> option.
@@ -1201,6 +1203,12 @@ be randomly reordered prior to connection.
See the L</Connection Order> section for more details.
+=item set_ignore_session_events()
+
+ Net::ZooKeeper::set_ignore_session_events(1);
+
+By default, ZooKeeper will trigger all pending watches for any session event(such as disconnecting and reconnecting to a server). If C<Net::ZooKeeper::set_ignore_session_events()> is set to a true value, session events will be ignored and C<wait()> calls will only return after the watch event is triggered or times out.
+
=back
=head1 EXPORTS
@@ -72,6 +72,7 @@ struct zk_watch_t {
pthread_mutex_t mutex;
pthread_cond_t cond;
int done;
+ int watch_event_triggered;
int ret;
int event_type;
int event_state;
@@ -163,12 +164,22 @@ static zk_key_t zk_watch_keys[NUM_WATCH_KEYS] = {
{"state", 0, 0, 0, 0}
};
+static int zk_ignore_session_events = 0;
static void _zk_watcher(zhandle_t *handle, int type, int state,
const char *path, void *context)
{
zk_watch_t *watch_ctx = context;
+ bool is_session_event = type == ZOO_SESSION_EVENT;
+ if (is_session_event && zk_ignore_session_events) {
+ return;
+ }
+ if (!is_session_event) {
+ // mark this watch for garbage collection
+ watch_ctx->watch_event_triggered = 1;
+ }
+
pthread_mutex_lock(&watch_ctx->mutex);
watch_ctx->event_type = type;
@@ -279,7 +290,9 @@ static unsigned int _zk_release_watches(pTHX_ zk_watch_t *first_watch,
if (!final) {
pthread_mutex_lock(&watch->mutex);
- done = watch->done;
+ // only release watches if the watch event has been triggered
+ // otherwise zookeeper may trigger it again
+ done = watch->watch_event_triggered;
pthread_mutex_unlock(&watch->mutex);
}
@@ -753,6 +766,13 @@ zk_set_deterministic_conn_order(flag)
XSRETURN_EMPTY;
+void
+zk_set_ignore_session_events(flag)
+ bool flag
+ PPCODE:
+ zk_ignore_session_events = flag;
+
+ XSRETURN_EMPTY;
void
zk_new(package, hosts, ...)
@@ -211,17 +211,14 @@ sub create
'acl' => ZOO_OPEN_ACL_UNSAFE);
}
-sub get_first_child
+sub get_children_absolute
{
my($self, $path) = @_;
my @child_paths = $self->get_children($path);
- if (@child_paths > 0) {
- return $path . (($path =~ /\/$/) ? '' : '/') . $child_paths[0];
- }
-
- return undef;
+ my $prefix = $path . (($path =~ /\/$/) ? '' : '/');
+ return map {$prefix . $_} @child_paths;
}
sub stat
@@ -298,9 +295,10 @@ SKIP: {
skip 'no connection to ZooKeeper', 1 unless
(defined($path) and $path eq $node_path);
- my $child_path = $sub_zkh->get_first_child($root_path);
- is($child_path, $node_path,
- 'get_first_child(): retrieved first child with subclassed handle');
+ my @children = $sub_zkh->get_children_absolute($root_path);
+ my $child_exists =!! grep {$_ eq $node_path} @children;
+ ok($child_exists,
+ 'get_children_absolute(): retrieved new child with subclassed handle');
}
my $sub_stat = $sub_zkh->stat();