slyphon-zookeeper 0.1.7 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +1 -0
- data/ext/zookeeper_base.rb +46 -16
- data/ext/zookeeper_c.c +99 -16
- data/ext/zookeeper_lib.c +23 -7
- data/java/zookeeper_base.rb +154 -37
- data/lib/zookeeper.rb +72 -1
- data/lib/zookeeper/common.rb +7 -21
- data/lib/zookeeper/em_client.rb +135 -0
- data/slyphon-zookeeper.gemspec +2 -2
- data/spec/em_spec.rb +138 -0
- data/spec/spec_helper.rb +19 -0
- metadata +110 -86
data/.gitignore
CHANGED
data/ext/zookeeper_base.rb
CHANGED
@@ -16,7 +16,8 @@ class ZookeeperBase < CZookeeper
|
|
16
16
|
ZOO_LOG_LEVEL_WARN = 2
|
17
17
|
ZOO_LOG_LEVEL_INFO = 3
|
18
18
|
ZOO_LOG_LEVEL_DEBUG = 4
|
19
|
-
|
19
|
+
|
20
|
+
|
20
21
|
def reopen(timeout = 10, watcher=nil)
|
21
22
|
watcher ||= @default_watcher
|
22
23
|
|
@@ -26,13 +27,15 @@ class ZookeeperBase < CZookeeper
|
|
26
27
|
set_default_global_watcher(&watcher)
|
27
28
|
end
|
28
29
|
|
29
|
-
|
30
|
+
@start_stop_mutex.synchronize do
|
31
|
+
init(@host)
|
30
32
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
33
|
+
if timeout > 0
|
34
|
+
time_to_stop = Time.now + timeout
|
35
|
+
until state == Zookeeper::ZOO_CONNECTED_STATE
|
36
|
+
break if Time.now > time_to_stop
|
37
|
+
sleep 0.1
|
38
|
+
end
|
36
39
|
end
|
37
40
|
end
|
38
41
|
|
@@ -46,9 +49,12 @@ class ZookeeperBase < CZookeeper
|
|
46
49
|
@current_req_id = 1
|
47
50
|
@host = host
|
48
51
|
|
52
|
+
@start_stop_mutex = Mutex.new
|
53
|
+
|
49
54
|
watcher ||= get_default_global_watcher
|
50
55
|
|
51
|
-
@_running = nil
|
56
|
+
@_running = nil # used by the C layer
|
57
|
+
@_closed = false # also used by the C layer
|
52
58
|
|
53
59
|
yield self if block_given?
|
54
60
|
|
@@ -76,12 +82,32 @@ class ZookeeperBase < CZookeeper
|
|
76
82
|
end
|
77
83
|
|
78
84
|
def close
|
79
|
-
@
|
80
|
-
|
81
|
-
|
82
|
-
|
85
|
+
@start_stop_mutex.synchronize do
|
86
|
+
@_running = false if @_running
|
87
|
+
end
|
88
|
+
|
89
|
+
if @dispatcher
|
90
|
+
wake_event_loop! unless @_closed
|
91
|
+
@dispatcher.join
|
92
|
+
end
|
93
|
+
|
94
|
+
@start_stop_mutex.synchronize do
|
95
|
+
unless @_closed
|
96
|
+
close_handle
|
97
|
+
|
98
|
+
# this is set up in the C init method, but it's easier to
|
99
|
+
# do the teardown here
|
100
|
+
begin
|
101
|
+
@selectable_io.close if @selectable_io
|
102
|
+
rescue IOError
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
83
107
|
|
84
|
-
|
108
|
+
def set_debug_level(int)
|
109
|
+
warn "DEPRECATION WARNING: #{self.class.name}#set_debug_level, it has moved to the class level and will be removed in a future release"
|
110
|
+
self.class.set_debug_level(int)
|
85
111
|
end
|
86
112
|
|
87
113
|
# set the watcher object/proc that will receive all global events (such as session/state events)
|
@@ -92,16 +118,20 @@ class ZookeeperBase < CZookeeper
|
|
92
118
|
end
|
93
119
|
end
|
94
120
|
|
95
|
-
|
121
|
+
def closed?
|
122
|
+
@start_stop_mutex.synchronize { false|@_closed }
|
123
|
+
end
|
124
|
+
|
96
125
|
def running?
|
97
|
-
false|@_running
|
126
|
+
@start_stop_mutex.synchronize { false|@_running }
|
98
127
|
end
|
99
128
|
|
129
|
+
protected
|
100
130
|
def setup_dispatch_thread!
|
101
131
|
@dispatcher = Thread.new do
|
102
132
|
while running?
|
103
133
|
begin # calling user code, so protect ourselves
|
104
|
-
dispatch_next_callback
|
134
|
+
dispatch_next_callback
|
105
135
|
rescue Exception => e
|
106
136
|
$stderr.puts "Error in dispatch thread, #{e.class}: #{e.message}\n" << e.backtrace.map{|n| "\t#{n}"}.join("\n")
|
107
137
|
end
|
data/ext/zookeeper_c.c
CHANGED
@@ -17,11 +17,16 @@
|
|
17
17
|
#include <stdio.h>
|
18
18
|
#include <stdlib.h>
|
19
19
|
#include <unistd.h>
|
20
|
+
#include <sys/fcntl.h>
|
21
|
+
#include <pthread.h>
|
20
22
|
|
21
23
|
#include "zookeeper_lib.h"
|
22
24
|
|
25
|
+
|
23
26
|
static VALUE Zookeeper = Qnil;
|
24
27
|
|
28
|
+
// slyphon: possibly add a lock to this for synchronizing during get_next_event
|
29
|
+
|
25
30
|
struct zkrb_instance_data {
|
26
31
|
zhandle_t *zh;
|
27
32
|
clientid_t myid;
|
@@ -69,9 +74,53 @@ static void print_zkrb_instance_data(struct zkrb_instance_data* ptr) {
|
|
69
74
|
fprintf(stderr, "}\n");
|
70
75
|
}
|
71
76
|
|
77
|
+
// cargo culted from io.c
|
78
|
+
static VALUE zkrb_new_instance _((VALUE));
|
79
|
+
|
80
|
+
static VALUE zkrb_new_instance(VALUE args) {
|
81
|
+
return rb_class_new_instance(2, (VALUE*)args+1, *(VALUE*)args);
|
82
|
+
}
|
83
|
+
|
84
|
+
static int zkrb_dup(int orig) {
|
85
|
+
int fd;
|
86
|
+
|
87
|
+
fd = dup(orig);
|
88
|
+
if (fd < 0) {
|
89
|
+
if (errno == EMFILE || errno == ENFILE || errno == ENOMEM) {
|
90
|
+
rb_gc();
|
91
|
+
fd = dup(orig);
|
92
|
+
}
|
93
|
+
if (fd < 0) {
|
94
|
+
rb_sys_fail(0);
|
95
|
+
}
|
96
|
+
}
|
97
|
+
return fd;
|
98
|
+
}
|
99
|
+
|
100
|
+
static VALUE create_selectable_io(zkrb_queue_t *q) {
|
101
|
+
// rb_cIO is the ruby IO class?
|
102
|
+
|
103
|
+
int pipe, state, read_fd;
|
104
|
+
VALUE args[3], reader;
|
105
|
+
|
106
|
+
read_fd = zkrb_dup(q->pipe_read);
|
107
|
+
|
108
|
+
args[0] = rb_cIO;
|
109
|
+
args[1] = INT2NUM(read_fd);
|
110
|
+
args[2] = INT2FIX(O_RDONLY);
|
111
|
+
reader = rb_protect(zkrb_new_instance, (VALUE)args, &state);
|
112
|
+
|
113
|
+
if (state) {
|
114
|
+
rb_jump_tag(state);
|
115
|
+
}
|
116
|
+
|
117
|
+
return reader;
|
118
|
+
}
|
119
|
+
|
72
120
|
static VALUE method_init(int argc, VALUE* argv, VALUE self) {
|
73
121
|
VALUE hostPort;
|
74
122
|
VALUE options;
|
123
|
+
|
75
124
|
rb_scan_args(argc, argv, "11", &hostPort, &options);
|
76
125
|
|
77
126
|
if (NIL_P(options)) {
|
@@ -121,8 +170,10 @@ static VALUE method_init(int argc, VALUE* argv, VALUE self) {
|
|
121
170
|
}
|
122
171
|
|
123
172
|
rb_iv_set(self, "@data", data);
|
173
|
+
rb_iv_set(self, "@selectable_io", create_selectable_io(zk_local_ctx->queue));
|
124
174
|
rb_iv_set(self, "@_running", Qtrue);
|
125
175
|
|
176
|
+
|
126
177
|
return Qnil;
|
127
178
|
}
|
128
179
|
|
@@ -421,7 +472,10 @@ static int is_running(VALUE self) {
|
|
421
472
|
return RTEST(rval);
|
422
473
|
}
|
423
474
|
|
424
|
-
|
475
|
+
|
476
|
+
/* slyphon: NEED TO PROTECT THIS AGAINST SHUTDOWN */
|
477
|
+
|
478
|
+
static VALUE method_get_next_event(VALUE self, VALUE blocking) {
|
425
479
|
char buf[64];
|
426
480
|
FETCH_DATA_PTR(self, zk);
|
427
481
|
|
@@ -439,19 +493,35 @@ static VALUE method_get_next_event(VALUE self) {
|
|
439
493
|
|
440
494
|
/* Wait for an event using rb_thread_select() on the queue's pipe */
|
441
495
|
if (event == NULL) {
|
442
|
-
|
443
|
-
|
496
|
+
if (NIL_P(blocking) || (blocking == Qfalse)) {
|
497
|
+
return Qnil; // no event for us
|
498
|
+
}
|
499
|
+
else {
|
500
|
+
int fd = zk->queue->pipe_read;
|
501
|
+
ssize_t bytes_read = 0;
|
502
|
+
|
503
|
+
fd_set rset; // a file descriptor set for use w/ select()
|
504
|
+
|
505
|
+
FD_ZERO(&rset); // FD_ZERO clears the set
|
506
|
+
FD_SET(fd, &rset); // FD_SET adds fd to the rset
|
444
507
|
|
445
|
-
|
446
|
-
|
508
|
+
// first arg is nfds: "the highest-numbered file descriptor in any of the three sets, plus 1"
|
509
|
+
// why? F*** you, that's why!
|
447
510
|
|
448
|
-
|
449
|
-
|
511
|
+
if (rb_thread_select(fd + 1, &rset, NULL, NULL, NULL) == -1)
|
512
|
+
rb_raise(rb_eRuntimeError, "select failed: %d", errno);
|
450
513
|
|
451
|
-
|
452
|
-
rb_raise(rb_eRuntimeError, "read failed: %d", errno);
|
514
|
+
bytes_read = read(fd, buf, sizeof(buf));
|
453
515
|
|
454
|
-
|
516
|
+
if (bytes_read == -1) {
|
517
|
+
rb_raise(rb_eRuntimeError, "read failed: %d", errno);
|
518
|
+
}
|
519
|
+
else if (ZKRBDebugging) {
|
520
|
+
fprintf(stderr, "read %d bytes from the queue's pipe\n", bytes_read);
|
521
|
+
}
|
522
|
+
|
523
|
+
continue;
|
524
|
+
}
|
455
525
|
}
|
456
526
|
|
457
527
|
VALUE hash = zkrb_event_to_ruby(event);
|
@@ -493,11 +563,22 @@ static VALUE method_wake_event_loop_bang(VALUE self) {
|
|
493
563
|
// return Qnil;
|
494
564
|
// }
|
495
565
|
|
496
|
-
static VALUE
|
566
|
+
static VALUE method_close_handle(VALUE self) {
|
497
567
|
FETCH_DATA_PTR(self, zk);
|
498
568
|
|
569
|
+
if (ZKRBDebugging) {
|
570
|
+
fprintf(stderr, "CLOSING ZK INSTANCE:");
|
571
|
+
print_zkrb_instance_data(zk);
|
572
|
+
}
|
573
|
+
|
574
|
+
// this is a value on the ruby side we can check to see if destroy_zkrb_instance
|
575
|
+
// has been called
|
576
|
+
rb_iv_set(self, "@_closed", Qtrue);
|
577
|
+
|
578
|
+
|
499
579
|
/* Note that after zookeeper_close() returns, ZK handle is invalid */
|
500
580
|
int rc = destroy_zkrb_instance(zk);
|
581
|
+
|
501
582
|
return INT2FIX(rc);
|
502
583
|
}
|
503
584
|
|
@@ -521,8 +602,7 @@ static VALUE method_recv_timeout(VALUE self) {
|
|
521
602
|
return INT2NUM(zoo_recv_timeout(zk->zh));
|
522
603
|
}
|
523
604
|
|
524
|
-
|
525
|
-
static VALUE method_set_debug_level(VALUE self, VALUE level) {
|
605
|
+
static VALUE klass_method_set_debug_level(VALUE klass, VALUE level) {
|
526
606
|
Check_Type(level, T_FIXNUM);
|
527
607
|
ZKRBDebugging = (FIX2INT(level) == ZOO_LOG_LEVEL_DEBUG);
|
528
608
|
zoo_set_debug_level(FIX2INT(level));
|
@@ -549,7 +629,7 @@ static void zkrb_define_methods(void) {
|
|
549
629
|
DEFINE_METHOD(set_acl, 5);
|
550
630
|
DEFINE_METHOD(get_acl, 3);
|
551
631
|
DEFINE_METHOD(client_id, 0);
|
552
|
-
DEFINE_METHOD(
|
632
|
+
DEFINE_METHOD(close_handle, 0);
|
553
633
|
DEFINE_METHOD(deterministic_conn_order, 1);
|
554
634
|
DEFINE_METHOD(is_unrecoverable, 0);
|
555
635
|
DEFINE_METHOD(recv_timeout, 1);
|
@@ -559,13 +639,16 @@ static void zkrb_define_methods(void) {
|
|
559
639
|
// DEFINE_METHOD(async, 1);
|
560
640
|
|
561
641
|
// methods for the ruby-side event manager
|
562
|
-
DEFINE_METHOD(get_next_event,
|
642
|
+
DEFINE_METHOD(get_next_event, 1);
|
563
643
|
DEFINE_METHOD(has_events, 0);
|
564
644
|
|
565
645
|
// Make these class methods?
|
566
|
-
DEFINE_METHOD(set_debug_level, 1);
|
567
646
|
DEFINE_METHOD(zerror, 1);
|
568
647
|
|
648
|
+
rb_define_singleton_method(Zookeeper, "set_debug_level", klass_method_set_debug_level, 1);
|
649
|
+
|
650
|
+
rb_attr(Zookeeper, rb_intern("selectable_io"), 1, 0, Qtrue);
|
651
|
+
|
569
652
|
rb_define_method(Zookeeper, "wake_event_loop!", method_wake_event_loop_bang, 0);
|
570
653
|
}
|
571
654
|
|
data/ext/zookeeper_lib.c
CHANGED
@@ -140,18 +140,26 @@ void zkrb_event_free(zkrb_event_t *event) {
|
|
140
140
|
}
|
141
141
|
case ZKRB_STRINGS: {
|
142
142
|
struct zkrb_strings_completion *strings_ctx = event->completion.strings_completion;
|
143
|
-
|
144
|
-
|
145
|
-
|
143
|
+
if (strings_ctx->values != NULL) {
|
144
|
+
int k;
|
145
|
+
for (k = 0; k < strings_ctx->values->count; ++k) free(strings_ctx->values->data[k]);
|
146
|
+
free(strings_ctx->values);
|
147
|
+
}
|
146
148
|
free(strings_ctx);
|
147
149
|
break;
|
148
150
|
}
|
149
151
|
case ZKRB_STRINGS_STAT: {
|
150
152
|
struct zkrb_strings_stat_completion *strings_stat_ctx = event->completion.strings_stat_completion;
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
153
|
+
if (strings_stat_ctx->values != NULL) {
|
154
|
+
int k;
|
155
|
+
for (k = 0; k < strings_stat_ctx->values->count; ++k) free(strings_stat_ctx->values->data[k]);
|
156
|
+
free(strings_stat_ctx->values);
|
157
|
+
}
|
158
|
+
|
159
|
+
if (strings_stat_ctx->stat != NULL) {
|
160
|
+
free(strings_stat_ctx->stat);
|
161
|
+
}
|
162
|
+
|
155
163
|
free(strings_stat_ctx);
|
156
164
|
break;
|
157
165
|
}
|
@@ -200,33 +208,40 @@ VALUE zkrb_event_to_ruby(zkrb_event_t *event) {
|
|
200
208
|
break;
|
201
209
|
}
|
202
210
|
case ZKRB_STAT: {
|
211
|
+
if (ZKRBDebugging) fprintf(stderr, "zkrb_event_to_ruby ZKRB_STAT\n");
|
203
212
|
struct zkrb_stat_completion *stat_ctx = event->completion.stat_completion;
|
204
213
|
rb_hash_aset(hash, GET_SYM("stat"), stat_ctx->stat ? zkrb_stat_to_rarray(stat_ctx->stat) : Qnil);
|
205
214
|
break;
|
206
215
|
}
|
207
216
|
case ZKRB_STRING: {
|
217
|
+
if (ZKRBDebugging) fprintf(stderr, "zkrb_event_to_ruby ZKRB_STRING\n");
|
208
218
|
struct zkrb_string_completion *string_ctx = event->completion.string_completion;
|
209
219
|
rb_hash_aset(hash, GET_SYM("string"), string_ctx->value ? rb_str_new2(string_ctx->value) : Qnil);
|
210
220
|
break;
|
211
221
|
}
|
212
222
|
case ZKRB_STRINGS: {
|
223
|
+
if (ZKRBDebugging) fprintf(stderr, "zkrb_event_to_ruby ZKRB_STRINGS\n");
|
213
224
|
struct zkrb_strings_completion *strings_ctx = event->completion.strings_completion;
|
214
225
|
rb_hash_aset(hash, GET_SYM("strings"), strings_ctx->values ? zkrb_string_vector_to_ruby(strings_ctx->values) : Qnil);
|
215
226
|
break;
|
216
227
|
}
|
217
228
|
case ZKRB_STRINGS_STAT: {
|
229
|
+
if (ZKRBDebugging) fprintf(stderr, "zkrb_event_to_ruby ZKRB_STRINGS_STAT\n");
|
218
230
|
struct zkrb_strings_stat_completion *strings_stat_ctx = event->completion.strings_stat_completion;
|
219
231
|
rb_hash_aset(hash, GET_SYM("strings"), strings_stat_ctx->values ? zkrb_string_vector_to_ruby(strings_stat_ctx->values) : Qnil);
|
220
232
|
rb_hash_aset(hash, GET_SYM("stat"), strings_stat_ctx->stat ? zkrb_stat_to_rarray(strings_stat_ctx->stat) : Qnil);
|
221
233
|
break;
|
222
234
|
}
|
223
235
|
case ZKRB_ACL: {
|
236
|
+
if (ZKRBDebugging) fprintf(stderr, "zkrb_event_to_ruby ZKRB_ACL\n");
|
224
237
|
struct zkrb_acl_completion *acl_ctx = event->completion.acl_completion;
|
225
238
|
rb_hash_aset(hash, GET_SYM("acl"), acl_ctx->acl ? zkrb_acl_vector_to_ruby(acl_ctx->acl) : Qnil);
|
226
239
|
rb_hash_aset(hash, GET_SYM("stat"), acl_ctx->stat ? zkrb_stat_to_rarray(acl_ctx->stat) : Qnil);
|
227
240
|
break;
|
228
241
|
}
|
229
242
|
case ZKRB_WATCHER: {
|
243
|
+
if (ZKRBDebugging) fprintf(stderr, "zkrb_event_to_ruby ZKRB_WATCHER\n");
|
244
|
+
struct zkrb_acl_completion *acl_ctx = event->completion.acl_completion;
|
230
245
|
struct zkrb_watcher_completion *watcher_ctx = event->completion.watcher_completion;
|
231
246
|
rb_hash_aset(hash, GET_SYM("type"), INT2FIX(watcher_ctx->type));
|
232
247
|
rb_hash_aset(hash, GET_SYM("state"), INT2FIX(watcher_ctx->state));
|
@@ -422,6 +437,7 @@ void zkrb_strings_stat_callback(
|
|
422
437
|
struct zkrb_strings_stat_completion *sc = malloc(sizeof(struct zkrb_strings_stat_completion));
|
423
438
|
sc->stat = NULL;
|
424
439
|
if (stat != NULL) { sc->stat = malloc(sizeof(struct Stat)); memcpy(sc->stat, stat, sizeof(struct Stat)); }
|
440
|
+
|
425
441
|
sc->values = (strings != NULL) ? zkrb_clone_string_vector(strings) : NULL;
|
426
442
|
|
427
443
|
ZKH_SETUP_EVENT(queue, event);
|
data/java/zookeeper_base.rb
CHANGED
@@ -59,6 +59,53 @@ class ZookeeperBase
|
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
62
|
+
class QueueWithPipe
|
63
|
+
attr_writer :clear_reads_on_pop
|
64
|
+
|
65
|
+
def initialize
|
66
|
+
r, w = IO.pipe
|
67
|
+
@pipe = { :read => r, :write => w }
|
68
|
+
@queue = Queue.new
|
69
|
+
|
70
|
+
# with the EventMachine client, we want to let EM handle clearing the
|
71
|
+
# event pipe, so we set this to false
|
72
|
+
@clear_reads_on_pop = true
|
73
|
+
end
|
74
|
+
|
75
|
+
def push(obj)
|
76
|
+
rv = @queue.push(obj)
|
77
|
+
@pipe[:write].write('0')
|
78
|
+
logger.debug { "pushed #{obj.inspect} onto queue and wrote to pipe" }
|
79
|
+
rv
|
80
|
+
end
|
81
|
+
|
82
|
+
def pop(non_blocking=false)
|
83
|
+
rv = @queue.pop(non_blocking)
|
84
|
+
|
85
|
+
# if non_blocking is true and an exception is raised, this won't get called
|
86
|
+
@pipe[:read].read(1) if clear_reads_on_pop?
|
87
|
+
|
88
|
+
rv
|
89
|
+
end
|
90
|
+
|
91
|
+
def close
|
92
|
+
@pipe.values.each { |io| io.close unless io.closed? }
|
93
|
+
end
|
94
|
+
|
95
|
+
def selectable_io
|
96
|
+
@pipe[:read]
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
def clear_reads_on_pop?
|
101
|
+
@clear_reads_on_pop
|
102
|
+
end
|
103
|
+
|
104
|
+
def logger
|
105
|
+
Zookeeper.logger
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
62
109
|
# used for internal dispatching
|
63
110
|
module JavaCB #:nodoc:
|
64
111
|
class Callback
|
@@ -78,13 +125,24 @@ class ZookeeperBase
|
|
78
125
|
include JZK::AsyncCallback::DataCallback
|
79
126
|
|
80
127
|
def processResult(rc, path, queue, data, stat)
|
81
|
-
queue.
|
128
|
+
logger.debug { "#{self.class.name}#processResult rc: #{rc}, req_id: #{req_id}, path: #{path}, queue: #{queue.inspect}, data: #{data.inspect}, stat: #{stat.inspect}" }
|
129
|
+
|
130
|
+
hash = {
|
82
131
|
:rc => rc,
|
83
132
|
:req_id => req_id,
|
84
133
|
:path => path,
|
85
|
-
:data => String.from_java_bytes(data),
|
86
|
-
:stat => stat.to_hash,
|
87
|
-
}
|
134
|
+
:data => (data && String.from_java_bytes(data)),
|
135
|
+
:stat => (stat && stat.to_hash),
|
136
|
+
}
|
137
|
+
|
138
|
+
# if rc == Zookeeper::ZOK
|
139
|
+
# hash.merge!({
|
140
|
+
# :data => String.from_java_bytes(data),
|
141
|
+
# :stat => stat.to_hash,
|
142
|
+
# })
|
143
|
+
# end
|
144
|
+
|
145
|
+
queue.push(hash)
|
88
146
|
end
|
89
147
|
end
|
90
148
|
|
@@ -92,6 +150,7 @@ class ZookeeperBase
|
|
92
150
|
include JZK::AsyncCallback::StringCallback
|
93
151
|
|
94
152
|
def processResult(rc, path, queue, str)
|
153
|
+
logger.debug { "#{self.class.name}#processResult rc: #{rc}, req_id: #{req_id}, path: #{path}, queue: #{queue.inspect}, str: #{str.inspect}" }
|
95
154
|
queue.push(:rc => rc, :req_id => req_id, :path => path, :string => str)
|
96
155
|
end
|
97
156
|
end
|
@@ -100,7 +159,7 @@ class ZookeeperBase
|
|
100
159
|
include JZK::AsyncCallback::StatCallback
|
101
160
|
|
102
161
|
def processResult(rc, path, queue, stat)
|
103
|
-
logger.debug { "
|
162
|
+
logger.debug { "#{self.class.name}#processResult rc: #{rc.inspect}, req_id: #{req_id}, path: #{path.inspect}, queue: #{queue.inspect}, stat: #{stat.inspect}" }
|
104
163
|
queue.push(:rc => rc, :req_id => req_id, :stat => (stat and stat.to_hash), :path => path)
|
105
164
|
end
|
106
165
|
end
|
@@ -109,7 +168,16 @@ class ZookeeperBase
|
|
109
168
|
include JZK::AsyncCallback::Children2Callback
|
110
169
|
|
111
170
|
def processResult(rc, path, queue, children, stat)
|
112
|
-
|
171
|
+
logger.debug { "#{self.class.name}#processResult rc: #{rc}, req_id: #{req_id}, path: #{path}, queue: #{queue.inspect}, children: #{children.inspect}, stat: #{stat.inspect}" }
|
172
|
+
hash = {
|
173
|
+
:rc => rc,
|
174
|
+
:req_id => req_id,
|
175
|
+
:path => path,
|
176
|
+
:strings => (children && children.to_a),
|
177
|
+
:stat => (stat and stat.to_hash),
|
178
|
+
}
|
179
|
+
|
180
|
+
queue.push(hash)
|
113
181
|
end
|
114
182
|
end
|
115
183
|
|
@@ -117,9 +185,9 @@ class ZookeeperBase
|
|
117
185
|
include JZK::AsyncCallback::ACLCallback
|
118
186
|
|
119
187
|
def processResult(rc, path, queue, acl, stat)
|
120
|
-
logger.debug { "ACLCallback#processResult #{rc.inspect} #{path.inspect} #{queue.inspect} #{acl.inspect} #{stat.inspect}" }
|
188
|
+
logger.debug { "ACLCallback#processResult rc: #{rc.inspect}, req_id: #{req_id}, path: #{path.inspect}, queue: #{queue.inspect}, acl: #{acl.inspect}, stat: #{stat.inspect}" }
|
121
189
|
a = Array(acl).map { |a| a.to_hash }
|
122
|
-
queue.push(:rc => rc, :req_id => req_id, :path => path, :acl => a, :stat => stat.to_hash)
|
190
|
+
queue.push(:rc => rc, :req_id => req_id, :path => path, :acl => a, :stat => (stat && stat.to_hash))
|
123
191
|
end
|
124
192
|
end
|
125
193
|
|
@@ -127,6 +195,7 @@ class ZookeeperBase
|
|
127
195
|
include JZK::AsyncCallback::VoidCallback
|
128
196
|
|
129
197
|
def processResult(rc, path, queue)
|
198
|
+
logger.debug { "#{self.class.name}#processResult rc: #{rc}, req_id: #{req_id}, queue: #{queue.inspect}" }
|
130
199
|
queue.push(:rc => rc, :req_id => req_id, :path => path)
|
131
200
|
end
|
132
201
|
end
|
@@ -140,7 +209,7 @@ class ZookeeperBase
|
|
140
209
|
end
|
141
210
|
|
142
211
|
def process(event)
|
143
|
-
logger.debug { "WatcherCallback got event: #{event.to_hash}" }
|
212
|
+
logger.debug { "WatcherCallback got event: #{event.to_hash.inspect}" }
|
144
213
|
hash = event.to_hash.merge(:req_id => req_id)
|
145
214
|
@event_queue.push(hash)
|
146
215
|
end
|
@@ -156,26 +225,32 @@ class ZookeeperBase
|
|
156
225
|
set_default_global_watcher(&watcher)
|
157
226
|
end
|
158
227
|
|
159
|
-
@
|
228
|
+
@start_stop_mutex.synchronize do
|
229
|
+
@jzk = JZK::ZooKeeper.new(@host, DEFAULT_SESSION_TIMEOUT, JavaCB::WatcherCallback.new(@event_queue))
|
160
230
|
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
231
|
+
if timeout > 0
|
232
|
+
time_to_stop = Time.now + timeout
|
233
|
+
until connected?
|
234
|
+
break if Time.now > time_to_stop
|
235
|
+
sleep 0.1
|
236
|
+
end
|
166
237
|
end
|
167
238
|
end
|
168
239
|
|
169
240
|
state
|
170
241
|
end
|
171
242
|
|
172
|
-
def initialize(host, timeout=10, watcher=nil)
|
243
|
+
def initialize(host, timeout=10, watcher=nil, options={})
|
173
244
|
@host = host
|
174
|
-
@event_queue =
|
245
|
+
@event_queue = QueueWithPipe.new
|
175
246
|
@current_req_id = 0
|
176
247
|
@req_mutex = Monitor.new
|
177
248
|
@watcher_reqs = {}
|
178
249
|
@completion_reqs = {}
|
250
|
+
@_running = nil
|
251
|
+
@_closed = false
|
252
|
+
@options = {}
|
253
|
+
@start_stop_mutex = Mutex.new
|
179
254
|
|
180
255
|
watcher ||= get_default_global_watcher
|
181
256
|
|
@@ -184,6 +259,7 @@ class ZookeeperBase
|
|
184
259
|
|
185
260
|
reopen(timeout, watcher)
|
186
261
|
return nil unless connected?
|
262
|
+
@_running = true
|
187
263
|
setup_dispatch_thread!
|
188
264
|
end
|
189
265
|
|
@@ -203,6 +279,22 @@ class ZookeeperBase
|
|
203
279
|
state == JZK::ZooKeeper::States::ASSOCIATING
|
204
280
|
end
|
205
281
|
|
282
|
+
def running?
|
283
|
+
@_running
|
284
|
+
end
|
285
|
+
|
286
|
+
def closed?
|
287
|
+
@_closed
|
288
|
+
end
|
289
|
+
|
290
|
+
def self.set_debug_level(*a)
|
291
|
+
# IGNORED IN JRUBY
|
292
|
+
end
|
293
|
+
|
294
|
+
def set_debug_level(*a)
|
295
|
+
# IGNORED IN JRUBY
|
296
|
+
end
|
297
|
+
|
206
298
|
def get(req_id, path, callback, watcher)
|
207
299
|
handle_keeper_exception do
|
208
300
|
watch_cb = watcher ? create_watcher(req_id, path) : false
|
@@ -332,20 +424,36 @@ class ZookeeperBase
|
|
332
424
|
|
333
425
|
class DispatchShutdownException < StandardError; end
|
334
426
|
|
427
|
+
def wake_event_loop!
|
428
|
+
@event_queue.push(KILL_TOKEN) # ignored by dispatch_next_callback
|
429
|
+
end
|
430
|
+
|
335
431
|
def close
|
336
432
|
@req_mutex.synchronize do
|
337
|
-
if @
|
338
|
-
|
339
|
-
|
340
|
-
|
433
|
+
@_running = false if @_running
|
434
|
+
end
|
435
|
+
|
436
|
+
# XXX: why is wake_event_loop! here?
|
437
|
+
if @dispatcher
|
438
|
+
wake_event_loop!
|
439
|
+
@dispatcher.join
|
440
|
+
end
|
341
441
|
|
342
|
-
|
343
|
-
|
344
|
-
@
|
442
|
+
unless @_closed
|
443
|
+
@start_stop_mutex.synchronize do
|
444
|
+
@_closed = true
|
445
|
+
close_handle
|
345
446
|
end
|
447
|
+
|
448
|
+
@event_queue.close
|
346
449
|
end
|
450
|
+
end
|
347
451
|
|
348
|
-
|
452
|
+
def close_handle
|
453
|
+
if @jzk
|
454
|
+
@jzk.close
|
455
|
+
wait_until { !connected? }
|
456
|
+
end
|
349
457
|
end
|
350
458
|
|
351
459
|
# set the watcher object/proc that will receive all global events (such as session/state events)
|
@@ -360,6 +468,23 @@ class ZookeeperBase
|
|
360
468
|
end
|
361
469
|
end
|
362
470
|
|
471
|
+
# by accessing this selectable_io you indicate that you intend to clear it
|
472
|
+
# when you have delivered an event by reading one byte per event.
|
473
|
+
#
|
474
|
+
def selectable_io
|
475
|
+
@event_queue.clear_reads_on_pop = false
|
476
|
+
@event_queue.selectable_io
|
477
|
+
end
|
478
|
+
|
479
|
+
def get_next_event(blocking=true)
|
480
|
+
@event_queue.pop(!blocking).tap do |event|
|
481
|
+
logger.debug { "get_next_event delivering event: #{event.inspect}" }
|
482
|
+
raise DispatchShutdownException if event == KILL_TOKEN
|
483
|
+
end
|
484
|
+
rescue ThreadError
|
485
|
+
nil
|
486
|
+
end
|
487
|
+
|
363
488
|
protected
|
364
489
|
def handle_keeper_exception
|
365
490
|
yield
|
@@ -376,28 +501,23 @@ class ZookeeperBase
|
|
376
501
|
end
|
377
502
|
|
378
503
|
def create_watcher(req_id, path)
|
504
|
+
logger.debug { "creating watcher for req_id: #{req_id} path: #{path}" }
|
379
505
|
lambda do |event|
|
506
|
+
logger.debug { "watcher for req_id #{req_id}, path: #{path} called back" }
|
380
507
|
h = { :req_id => req_id, :type => event.type.int_value, :state => event.state.int_value, :path => path }
|
381
508
|
@event_queue.push(h)
|
382
509
|
end
|
383
510
|
end
|
384
511
|
|
385
|
-
def get_next_event
|
386
|
-
@event_queue.pop.tap do |event|
|
387
|
-
raise DispatchShutdownException if event == KILL_TOKEN
|
388
|
-
end
|
389
|
-
end
|
390
|
-
|
391
512
|
# method to wait until block passed returns true or timeout (default is 10 seconds) is reached
|
392
513
|
def wait_until(timeout=10, &block)
|
393
514
|
time_to_stop = Time.now + timeout
|
394
515
|
until yield do
|
395
516
|
break if Time.now > time_to_stop
|
396
|
-
sleep 0.
|
517
|
+
sleep 0.1
|
397
518
|
end
|
398
519
|
end
|
399
520
|
|
400
|
-
protected
|
401
521
|
# TODO: Make all global puts configurable
|
402
522
|
def get_default_global_watcher
|
403
523
|
Proc.new { |args|
|
@@ -406,13 +526,10 @@ class ZookeeperBase
|
|
406
526
|
}
|
407
527
|
end
|
408
528
|
|
409
|
-
private
|
410
529
|
def setup_dispatch_thread!
|
411
530
|
logger.debug { "starting dispatch thread" }
|
412
531
|
@dispatcher = Thread.new do
|
413
|
-
|
414
|
-
|
415
|
-
while Thread.current[:running]
|
532
|
+
while running?
|
416
533
|
begin
|
417
534
|
dispatch_next_callback
|
418
535
|
rescue DispatchShutdownException
|