slyphon-zookeeper 0.1.7 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|