myelin-zookeeper_client 0.0.2
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/ext/zookeeper_c/extconf.rb +9 -0
- data/ext/zookeeper_c/zookeeper_ruby.c +198 -0
- data/lib/zookeeper_client.rb +78 -0
- data/test/test_basic.rb +62 -0
- metadata +54 -0
@@ -0,0 +1,198 @@
|
|
1
|
+
/* Ruby wrapper for the ZooKeeper C API
|
2
|
+
* Phillip Pearson <pp@myelin.co.nz>
|
3
|
+
*/
|
4
|
+
|
5
|
+
#define THREADED
|
6
|
+
|
7
|
+
#include "ruby.h"
|
8
|
+
|
9
|
+
#include "c-client-src/zookeeper.h"
|
10
|
+
#include <errno.h>
|
11
|
+
|
12
|
+
#include <stdio.h>
|
13
|
+
|
14
|
+
static VALUE ZooKeeper = Qnil;
|
15
|
+
static VALUE eNoNode = Qnil;
|
16
|
+
static VALUE eBadVersion = Qnil;
|
17
|
+
|
18
|
+
struct zk_rb_data {
|
19
|
+
zhandle_t *zh;
|
20
|
+
clientid_t myid;
|
21
|
+
};
|
22
|
+
|
23
|
+
static void watcher(zhandle_t *zh, int type, int state, const char *path) {
|
24
|
+
VALUE self, watcher_id;
|
25
|
+
|
26
|
+
return; // watchers don't work in ruby yet
|
27
|
+
|
28
|
+
self = (VALUE)zoo_get_context(zh);;
|
29
|
+
watcher_id = rb_intern("watcher");
|
30
|
+
|
31
|
+
fprintf(stderr,"C watcher %d state = %d for %s.\n", type, state, (path ? path: "null"));
|
32
|
+
rb_funcall(self, watcher_id, 3, INT2FIX(type), INT2FIX(state), rb_str_new2(path));
|
33
|
+
}
|
34
|
+
|
35
|
+
static void check_errors(int rc) {
|
36
|
+
switch (rc) {
|
37
|
+
case ZOK: /* all good! */ break;
|
38
|
+
case ZBADARGUMENTS: rb_raise(rb_eRuntimeError, "invalid input parameters");
|
39
|
+
case ZMARSHALLINGERROR: rb_raise(rb_eRuntimeError, "failed to marshall a request; possibly out of memory");
|
40
|
+
case ZOPERATIONTIMEOUT: rb_raise(rb_eRuntimeError, "failed to flush the buffers within the specified timeout");
|
41
|
+
case ZCONNECTIONLOSS: rb_raise(rb_eRuntimeError, "a network error occured while attempting to send request to server");
|
42
|
+
case ZSYSTEMERROR: rb_raise(rb_eRuntimeError, "a system (OS) error occured; it's worth checking errno to get details");
|
43
|
+
case ZNONODE: rb_raise(eNoNode, "the node does not exist");
|
44
|
+
case ZNOAUTH: rb_raise(rb_eRuntimeError, "the client does not have permission");
|
45
|
+
case ZBADVERSION: rb_raise(eBadVersion, "expected version does not match actual version");
|
46
|
+
case ZINVALIDSTATE: rb_raise(rb_eRuntimeError, "zhandle state is either SESSION_EXPIRED_STATE or AUTH_FAILED_STATE");
|
47
|
+
case ZNODEEXISTS: rb_raise(rb_eRuntimeError, "the node already exists");
|
48
|
+
case ZNOCHILDRENFOREPHEMERALS: rb_raise(rb_eRuntimeError, "cannot create children of ephemeral nodes");
|
49
|
+
case ZINVALIDACL: rb_raise(rb_eRuntimeError, "invalid ACL specified");
|
50
|
+
default: rb_raise(rb_eRuntimeError, "unknown error returned from zookeeper: %d", rc);
|
51
|
+
}
|
52
|
+
}
|
53
|
+
|
54
|
+
static void free_zk_rb_data(struct zk_rb_data* ptr) {
|
55
|
+
/*fprintf(stderr, "free zk_rb_data at %p\n", ptr);*/
|
56
|
+
zookeeper_close(ptr->zh);
|
57
|
+
}
|
58
|
+
|
59
|
+
static VALUE array_from_stat(const struct Stat* stat) {
|
60
|
+
return rb_ary_new3(8,
|
61
|
+
LL2NUM(stat->czxid),
|
62
|
+
LL2NUM(stat->mzxid),
|
63
|
+
LL2NUM(stat->ctime),
|
64
|
+
LL2NUM(stat->mtime),
|
65
|
+
INT2NUM(stat->version),
|
66
|
+
INT2NUM(stat->cversion),
|
67
|
+
INT2NUM(stat->aversion),
|
68
|
+
LL2NUM(stat->ephemeralOwner));
|
69
|
+
}
|
70
|
+
|
71
|
+
static VALUE method_initialize(VALUE self, VALUE hostPort) {
|
72
|
+
VALUE data;
|
73
|
+
struct zk_rb_data* zk = NULL;
|
74
|
+
|
75
|
+
Check_Type(hostPort, T_STRING);
|
76
|
+
|
77
|
+
data = Data_Make_Struct(ZooKeeper, struct zk_rb_data, 0, free_zk_rb_data, zk);
|
78
|
+
|
79
|
+
zoo_set_debug_level(LOG_LEVEL_INFO);
|
80
|
+
zoo_deterministic_conn_order(0);
|
81
|
+
zk->zh = zookeeper_init(RSTRING(hostPort)->ptr, watcher, 10000, &zk->myid, (void*)self, 0);
|
82
|
+
if (!zk->zh) {
|
83
|
+
rb_raise(rb_eRuntimeError, "error connecting to zookeeper: %d", errno);
|
84
|
+
}
|
85
|
+
|
86
|
+
rb_iv_set(self, "@data", data);
|
87
|
+
|
88
|
+
return Qnil;
|
89
|
+
}
|
90
|
+
|
91
|
+
static VALUE method_ls(VALUE self, VALUE path) {
|
92
|
+
struct zk_rb_data* zk;
|
93
|
+
struct String_vector strings;
|
94
|
+
int i;
|
95
|
+
VALUE output;
|
96
|
+
|
97
|
+
Check_Type(path, T_STRING);
|
98
|
+
Data_Get_Struct(rb_iv_get(self, "@data"), struct zk_rb_data, zk);
|
99
|
+
|
100
|
+
check_errors(zoo_get_children(zk->zh, RSTRING(path)->ptr, 0, &strings));
|
101
|
+
|
102
|
+
output = rb_ary_new();
|
103
|
+
for (i = 0; i < strings.count; ++i) {
|
104
|
+
rb_ary_push(output, rb_str_new2(strings.data[i]));
|
105
|
+
}
|
106
|
+
return output;
|
107
|
+
}
|
108
|
+
|
109
|
+
static VALUE method_exists(VALUE self, VALUE path, VALUE watch) {
|
110
|
+
struct zk_rb_data* zk;
|
111
|
+
struct Stat stat;
|
112
|
+
|
113
|
+
Check_Type(path, T_STRING);
|
114
|
+
Data_Get_Struct(rb_iv_get(self, "@data"), struct zk_rb_data, zk);
|
115
|
+
|
116
|
+
check_errors(zoo_exists(zk->zh, RSTRING(path)->ptr, (watch != Qfalse && watch != Qnil), &stat));
|
117
|
+
|
118
|
+
return array_from_stat(&stat);
|
119
|
+
}
|
120
|
+
|
121
|
+
static VALUE method_create(VALUE self, VALUE path, VALUE value, VALUE flags) {
|
122
|
+
struct zk_rb_data* zk;
|
123
|
+
char realpath[10240];
|
124
|
+
|
125
|
+
Check_Type(path, T_STRING);
|
126
|
+
Check_Type(value, T_STRING);
|
127
|
+
Check_Type(flags, T_FIXNUM);
|
128
|
+
Data_Get_Struct(rb_iv_get(self, "@data"), struct zk_rb_data, zk);
|
129
|
+
|
130
|
+
check_errors(zoo_create(zk->zh, RSTRING(path)->ptr, RSTRING(value)->ptr, RSTRING(value)->len,
|
131
|
+
&OPEN_ACL_UNSAFE, FIX2INT(flags), realpath, 10240));
|
132
|
+
|
133
|
+
return rb_str_new2(realpath);
|
134
|
+
}
|
135
|
+
|
136
|
+
static VALUE method_delete(VALUE self, VALUE path, VALUE version) {
|
137
|
+
struct zk_rb_data* zk;
|
138
|
+
|
139
|
+
Check_Type(path, T_STRING);
|
140
|
+
Check_Type(version, T_FIXNUM);
|
141
|
+
Data_Get_Struct(rb_iv_get(self, "@data"), struct zk_rb_data, zk);
|
142
|
+
|
143
|
+
check_errors(zoo_delete(zk->zh, RSTRING(path)->ptr, FIX2INT(version)));
|
144
|
+
|
145
|
+
return Qnil;
|
146
|
+
}
|
147
|
+
|
148
|
+
static VALUE method_get(VALUE self, VALUE path) {
|
149
|
+
struct zk_rb_data* zk;
|
150
|
+
char data[1024];
|
151
|
+
int data_len = 1024;
|
152
|
+
struct Stat stat;
|
153
|
+
|
154
|
+
Check_Type(path, T_STRING);
|
155
|
+
Data_Get_Struct(rb_iv_get(self, "@data"), struct zk_rb_data, zk);
|
156
|
+
|
157
|
+
check_errors(zoo_get(zk->zh, RSTRING(path)->ptr, 0, data, &data_len, &stat));
|
158
|
+
/*printf("got some data; version=%d\n", stat.version);*/
|
159
|
+
|
160
|
+
return rb_ary_new3(2,
|
161
|
+
rb_str_new(data, data_len),
|
162
|
+
array_from_stat(&stat));
|
163
|
+
}
|
164
|
+
|
165
|
+
static VALUE method_set(VALUE self, VALUE path, VALUE data, VALUE version) {
|
166
|
+
struct zk_rb_data* zk;
|
167
|
+
|
168
|
+
Check_Type(path, T_STRING);
|
169
|
+
Check_Type(data, T_STRING);
|
170
|
+
Check_Type(version, T_FIXNUM);
|
171
|
+
Data_Get_Struct(rb_iv_get(self, "@data"), struct zk_rb_data, zk);
|
172
|
+
|
173
|
+
check_errors(zoo_set(zk->zh, RSTRING(path)->ptr, RSTRING(data)->ptr, RSTRING(data)->len, FIX2INT(version)));
|
174
|
+
|
175
|
+
return Qnil;
|
176
|
+
}
|
177
|
+
|
178
|
+
void Init_zookeeper_c() {
|
179
|
+
ZooKeeper = rb_define_class("CZooKeeper", rb_cObject);
|
180
|
+
rb_define_method(ZooKeeper, "initialize", method_initialize, 1);
|
181
|
+
rb_define_method(ZooKeeper, "ls", method_ls, 1);
|
182
|
+
rb_define_method(ZooKeeper, "exists", method_exists, 2);
|
183
|
+
rb_define_method(ZooKeeper, "create", method_create, 3);
|
184
|
+
rb_define_method(ZooKeeper, "delete", method_delete, 2);
|
185
|
+
rb_define_method(ZooKeeper, "get", method_get, 1);
|
186
|
+
rb_define_method(ZooKeeper, "set", method_set, 3);
|
187
|
+
|
188
|
+
eNoNode = rb_define_class_under(ZooKeeper, "NoNodeError", rb_eRuntimeError);
|
189
|
+
eBadVersion = rb_define_class_under(ZooKeeper, "BadVersionError", rb_eRuntimeError);
|
190
|
+
|
191
|
+
rb_define_const(ZooKeeper, "EPHEMERAL", INT2FIX(EPHEMERAL));
|
192
|
+
rb_define_const(ZooKeeper, "SEQUENCE", INT2FIX(SEQUENCE));
|
193
|
+
|
194
|
+
rb_define_const(ZooKeeper, "SESSION_EVENT", INT2FIX(SESSION_EVENT));
|
195
|
+
rb_define_const(ZooKeeper, "CONNECTED_STATE", INT2FIX(CONNECTED_STATE));
|
196
|
+
rb_define_const(ZooKeeper, "AUTH_FAILED_STATE", INT2FIX(AUTH_FAILED_STATE));
|
197
|
+
rb_define_const(ZooKeeper, "EXPIRED_SESSION_STATE", INT2FIX(EXPIRED_SESSION_STATE));
|
198
|
+
}
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# Ruby wrapper for the ZooKeeper C API
|
2
|
+
# Phillip Pearson <pp@myelin.co.nz>
|
3
|
+
|
4
|
+
require 'zookeeper_c'
|
5
|
+
|
6
|
+
class ZkStat
|
7
|
+
attr_reader :version
|
8
|
+
def initialize(ary)
|
9
|
+
@czxid, @mzxid, @ctime, @mtime, @version, @cversion, @aversion, @ephemeralOwner = ary
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class ZooKeeper < CZooKeeper
|
14
|
+
def initialize(host)
|
15
|
+
super(host)
|
16
|
+
@watchers = {} # path => [ block, block, ... ]
|
17
|
+
end
|
18
|
+
|
19
|
+
def exists(path, &blk)
|
20
|
+
(@watchers[path] ||= []) << blk if blk
|
21
|
+
ZkStat.new(super(path, !!blk))
|
22
|
+
end
|
23
|
+
|
24
|
+
def stat(path, &blk)
|
25
|
+
exists(path, &blk)
|
26
|
+
rescue ZooKeeper::NoNodeError
|
27
|
+
nil
|
28
|
+
end
|
29
|
+
|
30
|
+
def get(path)
|
31
|
+
value, stat = super
|
32
|
+
[value, ZkStat.new(stat)]
|
33
|
+
end
|
34
|
+
|
35
|
+
def try_acquire(path, value)
|
36
|
+
# create the parent node if it doesn't exist already
|
37
|
+
create(path, "lock node", 0) unless stat(path)
|
38
|
+
|
39
|
+
# attempt to obtain the lock
|
40
|
+
realpath = create("#{path}/lock-", value, ZooKeeper::EPHEMERAL | ZooKeeper::SEQUENCE)
|
41
|
+
#puts "created lock node #{realpath}"
|
42
|
+
|
43
|
+
# see if we got it
|
44
|
+
serial = /lock-(\d+)$/.match(realpath).captures[0].to_i
|
45
|
+
have_lock = true
|
46
|
+
ls(path).each do |child|
|
47
|
+
if m = /lock-(\d+)$/.match(child)
|
48
|
+
if m.captures[0].to_i < serial
|
49
|
+
have_lock = false
|
50
|
+
break
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# call block
|
56
|
+
yield(have_lock)
|
57
|
+
|
58
|
+
# release the lock
|
59
|
+
#puts "deleting #{realpath}"
|
60
|
+
delete(realpath, stat(realpath).version)
|
61
|
+
end
|
62
|
+
|
63
|
+
def watcher(type, state, path)
|
64
|
+
raise Exception("watchers don't work in ruby yet") # ... until I figure out how to synchronize access to the Ruby interpreter
|
65
|
+
|
66
|
+
return unless type == SESSION_EVENT
|
67
|
+
|
68
|
+
case state
|
69
|
+
when CONNECTED_STATE
|
70
|
+
puts "ruby watcher; got an event for #{path}"
|
71
|
+
|
72
|
+
when AUTH_FAILED_STATE
|
73
|
+
raise Exception, "auth failure"
|
74
|
+
when EXPIRED_SESSION_STATE
|
75
|
+
raise Exception, "session expired"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/test/test_basic.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'zookeeper_client'
|
3
|
+
|
4
|
+
z = ZooKeeper.new("localhost:2181")
|
5
|
+
|
6
|
+
puts "root: #{z.ls("/").inspect}"
|
7
|
+
|
8
|
+
path = "/testing_node"
|
9
|
+
|
10
|
+
puts "working with path #{path}"
|
11
|
+
|
12
|
+
stat = z.stat(path)
|
13
|
+
puts "exists? #{stat.inspect}"
|
14
|
+
|
15
|
+
unless stat.nil?
|
16
|
+
z.ls(path).each do |o|
|
17
|
+
puts " child object: #{o}"
|
18
|
+
end
|
19
|
+
puts "delete: #{z.delete(path, stat.version).inspect}"
|
20
|
+
end
|
21
|
+
|
22
|
+
puts "create: #{z.create(path, "initial value", 0).inspect}"
|
23
|
+
|
24
|
+
value, stat = z.get(path)
|
25
|
+
puts "current value #{value}, stat #{stat.inspect}"
|
26
|
+
|
27
|
+
puts "set: #{z.set(path, "this is a test", stat.version).inspect}"
|
28
|
+
|
29
|
+
value, stat = z.get(path)
|
30
|
+
puts "new value: #{value.inspect} #{stat.inspect}"
|
31
|
+
|
32
|
+
puts "delete: #{z.delete(path, stat.version).inspect}"
|
33
|
+
|
34
|
+
begin
|
35
|
+
puts "exists? #{z.exists(path)}"
|
36
|
+
raise Exception, "it shouldn't exist"
|
37
|
+
rescue ZooKeeper::NoNodeError
|
38
|
+
puts "doesn't exist - good, because we just deleted it!"
|
39
|
+
end
|
40
|
+
|
41
|
+
puts "trying a watcher"
|
42
|
+
puts z
|
43
|
+
if s = z.stat("/test"); z.delete("/test", s.version); end
|
44
|
+
z.create("/test", "foo", 0)
|
45
|
+
z.exists("/test") do
|
46
|
+
puts "callback!!!"
|
47
|
+
end
|
48
|
+
puts "now changing it"
|
49
|
+
z.set("/test", "bar", 0)
|
50
|
+
sleep 1
|
51
|
+
puts "did the watcher say something?"
|
52
|
+
|
53
|
+
puts "let's try using a lock"
|
54
|
+
z.try_acquire("/test_lock", "this is the content of the lock file") do |have_lock|
|
55
|
+
puts have_lock ? "we have the lock!" : "failed to obtain lock :("
|
56
|
+
if have_lock
|
57
|
+
puts "sleeping"
|
58
|
+
sleep 5
|
59
|
+
end
|
60
|
+
end
|
61
|
+
puts "done with locking..."
|
62
|
+
|
metadata
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: myelin-zookeeper_client
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Phillip Pearson
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-07-16 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: zookeeper_client is a Ruby library to interface with the ZooKeeper replicated object store / lock server.
|
17
|
+
email: pp@myelin.co.nz
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions:
|
21
|
+
- ext/zookeeper_c/extconf.rb
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
24
|
+
files:
|
25
|
+
- lib/zookeeper_client.rb
|
26
|
+
- ext/zookeeper_c/zookeeper_ruby.c
|
27
|
+
has_rdoc: false
|
28
|
+
homepage: http://github.com/myelin/zookeeper_client
|
29
|
+
post_install_message:
|
30
|
+
rdoc_options: []
|
31
|
+
|
32
|
+
require_paths:
|
33
|
+
- lib
|
34
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
35
|
+
requirements:
|
36
|
+
- - ">="
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: "0"
|
39
|
+
version:
|
40
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: "0"
|
45
|
+
version:
|
46
|
+
requirements: []
|
47
|
+
|
48
|
+
rubyforge_project:
|
49
|
+
rubygems_version: 1.2.0
|
50
|
+
signing_key:
|
51
|
+
specification_version: 2
|
52
|
+
summary: Ruby wrapper for the ZooKeeper C client library
|
53
|
+
test_files:
|
54
|
+
- test/test_basic.rb
|