myelin-zookeeper_client 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,9 @@
1
+ require 'mkmf'
2
+ extension_name = 'zookeeper_c'
3
+ dir_config(extension_name)
4
+
5
+ if have_library("zookeeper_mt", "zoo_set_debug_level") then
6
+ create_makefile(extension_name)
7
+ else
8
+ puts "No ZooKeeper C client library available"
9
+ end
@@ -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
@@ -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