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.
@@ -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