couchbase 1.2.1 → 1.2.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +50 -5
- data/RELEASE_NOTES.markdown +256 -145
- data/couchbase.gemspec +2 -4
- data/examples/chat-em/Gemfile +7 -0
- data/examples/chat-em/README.markdown +45 -0
- data/examples/chat-em/server.rb +82 -0
- data/ext/couchbase_ext/arguments.c +18 -17
- data/ext/couchbase_ext/arithmetic.c +17 -25
- data/ext/couchbase_ext/bucket.c +227 -32
- data/ext/couchbase_ext/context.c +64 -0
- data/ext/couchbase_ext/couchbase_ext.c +106 -14
- data/ext/couchbase_ext/couchbase_ext.h +81 -12
- data/ext/couchbase_ext/delete.c +18 -25
- data/ext/couchbase_ext/eventmachine_plugin.c +452 -0
- data/ext/couchbase_ext/extconf.rb +2 -0
- data/ext/couchbase_ext/get.c +18 -31
- data/ext/couchbase_ext/http.c +40 -31
- data/ext/couchbase_ext/multithread_plugin.c +38 -201
- data/ext/couchbase_ext/observe.c +17 -25
- data/ext/couchbase_ext/plugin_common.c +171 -0
- data/ext/couchbase_ext/result.c +18 -12
- data/ext/couchbase_ext/stats.c +17 -25
- data/ext/couchbase_ext/store.c +43 -47
- data/ext/couchbase_ext/touch.c +18 -25
- data/ext/couchbase_ext/unlock.c +18 -25
- data/ext/couchbase_ext/utils.c +23 -8
- data/ext/couchbase_ext/version.c +16 -24
- data/lib/couchbase.rb +1 -0
- data/lib/couchbase/bucket.rb +1 -1
- data/lib/couchbase/constants.rb +12 -0
- data/lib/couchbase/version.rb +1 -1
- data/lib/couchbase/view.rb +210 -60
- data/lib/couchbase/view_row.rb +103 -61
- data/tasks/compile.rake +1 -1
- data/test/test_async.rb +63 -0
- data/test/test_eventmachine.rb +70 -0
- metadata +143 -174
- data/tasks/doc.rake +0 -27
data/couchbase.gemspec
CHANGED
@@ -38,13 +38,11 @@ Gem::Specification.new do |s|
|
|
38
38
|
s.add_runtime_dependency 'yaji', '~> 0.3.2'
|
39
39
|
s.add_runtime_dependency 'multi_json', '~> 1.0'
|
40
40
|
|
41
|
-
s.add_development_dependency 'rake'
|
41
|
+
s.add_development_dependency 'rake'
|
42
42
|
s.add_development_dependency 'minitest'
|
43
43
|
s.add_development_dependency 'rake-compiler', '>= 0.7.5'
|
44
|
-
s.add_development_dependency 'rdiscount'
|
45
|
-
s.add_development_dependency 'yard'
|
46
|
-
s.add_development_dependency 'yard-xml'
|
47
44
|
s.add_development_dependency 'mini_portile'
|
48
45
|
s.add_development_dependency 'yajl-ruby', '~> 1.1.0'
|
49
46
|
s.add_development_dependency 'active_support'
|
47
|
+
s.add_development_dependency 'eventmachine'
|
50
48
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# Chat Demo
|
2
|
+
|
3
|
+
This is simple demo of the chat built on EventMachine, and using
|
4
|
+
Couchbase to store logs.
|
5
|
+
|
6
|
+
# Quick Start and Usage
|
7
|
+
|
8
|
+
Navigate to the example directory and install dependencies:
|
9
|
+
|
10
|
+
$ cd examples/chat-em
|
11
|
+
$ bundle install
|
12
|
+
|
13
|
+
Execute the server
|
14
|
+
|
15
|
+
$ ruby ./server.rb
|
16
|
+
Hi, this is simple chat server based on EventMachine.
|
17
|
+
To join, just use your telnet or netcat clients to connect to
|
18
|
+
port 9999 on this machine. Press Ctrl-C to stop it.
|
19
|
+
|
20
|
+
Use telnet to join the chat
|
21
|
+
|
22
|
+
$ telnet localhost 9999
|
23
|
+
Trying 127.0.0.1...
|
24
|
+
Connected to localhost.
|
25
|
+
Escape character is '^]'.
|
26
|
+
*** What is your name?
|
27
|
+
avsej
|
28
|
+
*** Hi, avsej!
|
29
|
+
Hi everyone in this chat
|
30
|
+
|
31
|
+
The server will broadcast all your messages and record any event to
|
32
|
+
the Couchbase server. If your server hosted not on the localhost or
|
33
|
+
using bucket different from "default" you might want to change the
|
34
|
+
connection options at the bottom of the `server.rb`, for example in
|
35
|
+
this case it will connect to the bucket "protected" with password
|
36
|
+
"secret".
|
37
|
+
|
38
|
+
Couchbase.connection_options = {
|
39
|
+
:async => true,
|
40
|
+
:engine => :eventmachine,
|
41
|
+
:bucket => "protected",
|
42
|
+
:password => "secret"
|
43
|
+
}
|
44
|
+
|
45
|
+
Happy hacking!
|
@@ -0,0 +1,82 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
["/../../lib", "/.."].each do |path|
|
3
|
+
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + path))
|
4
|
+
end
|
5
|
+
|
6
|
+
require 'bundler'
|
7
|
+
Bundler.require
|
8
|
+
|
9
|
+
require 'couchbase'
|
10
|
+
|
11
|
+
class ChatServer < EM::Connection
|
12
|
+
|
13
|
+
@@clients = []
|
14
|
+
|
15
|
+
def log(message, author = nil)
|
16
|
+
Couchbase.bucket.incr("log:key", :initial => 1) do |res|
|
17
|
+
entry = {
|
18
|
+
'time' => Time.now.utc,
|
19
|
+
'author' => author || "[system]",
|
20
|
+
'message' => message
|
21
|
+
}
|
22
|
+
Couchbase.bucket.set("log:#{res.value}", entry)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def post_init
|
27
|
+
@username = nil
|
28
|
+
send_data("*** What is your name?\n")
|
29
|
+
end
|
30
|
+
|
31
|
+
def receive_data(data)
|
32
|
+
if @username
|
33
|
+
broadcast(data.strip, @username)
|
34
|
+
else
|
35
|
+
name = data.gsub(/\s+|[\[\]]/, '').strip[0..20]
|
36
|
+
if name.empty?
|
37
|
+
send_data("*** What is your name?\n")
|
38
|
+
else
|
39
|
+
@username = name
|
40
|
+
@@clients.push(self)
|
41
|
+
broadcast("#{@username} has joined")
|
42
|
+
send_data("*** Hi, #{@username}!\n")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def unbind
|
48
|
+
@@clients.delete(self)
|
49
|
+
broadcast("#{@username} has left") if @username
|
50
|
+
end
|
51
|
+
|
52
|
+
def broadcast(message, author = nil)
|
53
|
+
prefix = author ? "<#{@username}>" : "***"
|
54
|
+
log(message, author)
|
55
|
+
@@clients.each do |client|
|
56
|
+
unless client == self
|
57
|
+
client.send_data("#{prefix} #{message}\n")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
EventMachine.run do
|
65
|
+
# hit Control + C to stop
|
66
|
+
Signal.trap("INT") { EventMachine.stop }
|
67
|
+
Signal.trap("TERM") { EventMachine.stop }
|
68
|
+
|
69
|
+
Couchbase.connection_options = {:async => true, :engine => :eventmachine}
|
70
|
+
Couchbase.bucket.on_connect do |res|
|
71
|
+
if res.success?
|
72
|
+
puts(<<-MOTD.gsub(/^\s+/, ''))
|
73
|
+
Hi, this is simple chat server based on EventMachine.
|
74
|
+
To join, just use your telnet or netcat clients to connect to
|
75
|
+
port 9999 on this machine. Press Ctrl-C to stop it.
|
76
|
+
MOTD
|
77
|
+
EventMachine.start_server("0.0.0.0", 9999, ChatServer)
|
78
|
+
else
|
79
|
+
puts "Cannot connect to Couchbase Server: #{res.error}"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -56,6 +56,7 @@ cb_params_touch_alloc(struct cb_params_st *params, lcb_size_t size)
|
|
56
56
|
cb_params_touch_init_item(struct cb_params_st *params, lcb_size_t idx, VALUE key_obj, lcb_time_t exptime)
|
57
57
|
{
|
58
58
|
key_obj = cb_unify_key(params->bucket, key_obj, 1);
|
59
|
+
rb_ary_push(params->ensurance, key_obj);
|
59
60
|
params->cmd.touch.items[idx].v.v0.key = RSTRING_PTR(key_obj);
|
60
61
|
params->cmd.touch.items[idx].v.v0.nkey = RSTRING_LEN(key_obj);
|
61
62
|
params->cmd.touch.items[idx].v.v0.exptime = exptime;
|
@@ -140,6 +141,7 @@ cb_params_remove_alloc(struct cb_params_st *params, lcb_size_t size)
|
|
140
141
|
cb_params_remove_init_item(struct cb_params_st *params, lcb_size_t idx, VALUE key_obj, lcb_cas_t cas)
|
141
142
|
{
|
142
143
|
key_obj = cb_unify_key(params->bucket, key_obj, 1);
|
144
|
+
rb_ary_push(params->ensurance, key_obj);
|
143
145
|
params->cmd.remove.items[idx].v.v0.key = RSTRING_PTR(key_obj);
|
144
146
|
params->cmd.remove.items[idx].v.v0.nkey = RSTRING_LEN(key_obj);
|
145
147
|
params->cmd.remove.items[idx].v.v0.cas = cas;
|
@@ -239,6 +241,8 @@ cb_params_store_init_item(struct cb_params_st *params, lcb_size_t idx,
|
|
239
241
|
VALUE val = rb_any_to_s(value_obj);
|
240
242
|
rb_raise(cb_eValueFormatError, "unable to convert value for key '%s' to string: %s", RSTRING_PTR(key_obj), RSTRING_PTR(val));
|
241
243
|
}
|
244
|
+
rb_ary_push(params->ensurance, key_obj);
|
245
|
+
rb_ary_push(params->ensurance, value_obj);
|
242
246
|
params->cmd.store.items[idx].v.v0.datatype = params->cmd.store.datatype;
|
243
247
|
params->cmd.store.items[idx].v.v0.operation = params->cmd.store.operation;
|
244
248
|
params->cmd.store.items[idx].v.v0.key = RSTRING_PTR(key_obj);
|
@@ -347,6 +351,7 @@ cb_params_get_init_item(struct cb_params_st *params, lcb_size_t idx,
|
|
347
351
|
VALUE key_obj, lcb_time_t exptime)
|
348
352
|
{
|
349
353
|
key_obj = cb_unify_key(params->bucket, key_obj, 1);
|
354
|
+
rb_ary_push(params->ensurance, key_obj);
|
350
355
|
if (params->cmd.get.replica) {
|
351
356
|
params->cmd.get.items_gr[idx].v.v0.key = RSTRING_PTR(key_obj);
|
352
357
|
params->cmd.get.items_gr[idx].v.v0.nkey = RSTRING_LEN(key_obj);
|
@@ -461,6 +466,7 @@ cb_params_arith_init_item(struct cb_params_st *params, lcb_size_t idx,
|
|
461
466
|
VALUE key_obj, lcb_int64_t delta)
|
462
467
|
{
|
463
468
|
key_obj = cb_unify_key(params->bucket, key_obj, 1);
|
469
|
+
rb_ary_push(params->ensurance, key_obj);
|
464
470
|
params->cmd.arith.items[idx].v.v0.key = RSTRING_PTR(key_obj);
|
465
471
|
params->cmd.arith.items[idx].v.v0.nkey = RSTRING_LEN(key_obj);
|
466
472
|
params->cmd.arith.items[idx].v.v0.delta = delta * params->cmd.arith.sign;
|
@@ -565,6 +571,7 @@ cb_params_stats_init_item(struct cb_params_st *params, lcb_size_t idx,
|
|
565
571
|
VALUE key_obj)
|
566
572
|
{
|
567
573
|
key_obj = cb_unify_key(params->bucket, key_obj, 1);
|
574
|
+
rb_ary_push(params->ensurance, key_obj);
|
568
575
|
params->cmd.stats.items[idx].v.v0.name = RSTRING_PTR(key_obj);
|
569
576
|
params->cmd.stats.items[idx].v.v0.nname = RSTRING_LEN(key_obj);
|
570
577
|
params->npayload += RSTRING_LEN(key_obj);
|
@@ -616,6 +623,7 @@ cb_params_observe_alloc(struct cb_params_st *params, lcb_size_t size)
|
|
616
623
|
cb_params_observe_init_item(struct cb_params_st *params, lcb_size_t idx, VALUE key_obj)
|
617
624
|
{
|
618
625
|
key_obj = cb_unify_key(params->bucket, key_obj, 1);
|
626
|
+
rb_ary_push(params->ensurance, key_obj);
|
619
627
|
params->cmd.observe.items[idx].v.v0.key = RSTRING_PTR(key_obj);
|
620
628
|
params->cmd.observe.items[idx].v.v0.nkey = RSTRING_LEN(key_obj);
|
621
629
|
params->npayload += RSTRING_LEN(key_obj);
|
@@ -666,6 +674,7 @@ cb_params_unlock_alloc(struct cb_params_st *params, lcb_size_t size)
|
|
666
674
|
cb_params_unlock_init_item(struct cb_params_st *params, lcb_size_t idx, VALUE key_obj, lcb_cas_t cas)
|
667
675
|
{
|
668
676
|
key_obj = cb_unify_key(params->bucket, key_obj, 1);
|
677
|
+
rb_ary_push(params->ensurance, key_obj);
|
669
678
|
params->cmd.unlock.items[idx].v.v0.key = RSTRING_PTR(key_obj);
|
670
679
|
params->cmd.unlock.items[idx].v.v0.nkey = RSTRING_LEN(key_obj);
|
671
680
|
params->cmd.unlock.items[idx].v.v0.cas = cas;
|
@@ -733,6 +742,9 @@ cb_params_version_alloc(struct cb_params_st *params)
|
|
733
742
|
void
|
734
743
|
cb_params_destroy(struct cb_params_st *params)
|
735
744
|
{
|
745
|
+
rb_ary_clear(params->ensurance);
|
746
|
+
params->ensurance = Qfalse;
|
747
|
+
params->args = Qfalse;
|
736
748
|
switch (params->type) {
|
737
749
|
case cb_cmd_get:
|
738
750
|
_release_data_for(get);
|
@@ -765,22 +777,14 @@ cb_params_destroy(struct cb_params_st *params)
|
|
765
777
|
}
|
766
778
|
}
|
767
779
|
|
768
|
-
struct build_params_st
|
769
|
-
{
|
770
|
-
struct cb_params_st *params;
|
771
|
-
int argc;
|
772
|
-
VALUE argv;
|
773
|
-
};
|
774
|
-
|
775
780
|
static VALUE
|
776
781
|
do_params_build(VALUE ptr)
|
777
782
|
{
|
778
783
|
VALUE opts;
|
779
784
|
/* unpack arguments */
|
780
|
-
struct
|
781
|
-
|
782
|
-
|
783
|
-
VALUE argv = p->argv;
|
785
|
+
struct cb_params_st *params = (struct cb_params_st*)ptr;
|
786
|
+
int argc = RARRAY_LEN(params->args);
|
787
|
+
VALUE argv = params->args;
|
784
788
|
|
785
789
|
/* extract options */
|
786
790
|
if (argc > 1 && TYPE(RARRAY_PTR(argv)[argc-1]) == T_HASH) {
|
@@ -873,15 +877,12 @@ do_params_build(VALUE ptr)
|
|
873
877
|
}
|
874
878
|
|
875
879
|
void
|
876
|
-
cb_params_build(struct cb_params_st *params
|
880
|
+
cb_params_build(struct cb_params_st *params)
|
877
881
|
{
|
878
882
|
int fail = 0;
|
879
|
-
|
883
|
+
params->ensurance = rb_ary_new();
|
880
884
|
|
881
|
-
|
882
|
-
args.argc = argc;
|
883
|
-
args.argv = argv;
|
884
|
-
rb_protect(do_params_build, (VALUE)&args, &fail);
|
885
|
+
rb_protect(do_params_build, (VALUE)params, &fail);
|
885
886
|
if (fail) {
|
886
887
|
cb_params_destroy(params);
|
887
888
|
/* raise exception from protected block */
|
@@ -22,7 +22,7 @@ cb_arithmetic_callback(lcb_t handle, const void *cookie, lcb_error_t error, cons
|
|
22
22
|
{
|
23
23
|
struct cb_context_st *ctx = (struct cb_context_st *)cookie;
|
24
24
|
struct cb_bucket_st *bucket = ctx->bucket;
|
25
|
-
VALUE cas, key, val,
|
25
|
+
VALUE cas, key, val, exc, res;
|
26
26
|
ID o;
|
27
27
|
|
28
28
|
ctx->nqueries--;
|
@@ -35,7 +35,7 @@ cb_arithmetic_callback(lcb_t handle, const void *cookie, lcb_error_t error, cons
|
|
35
35
|
if (exc != Qnil) {
|
36
36
|
rb_ivar_set(exc, cb_id_iv_cas, cas);
|
37
37
|
rb_ivar_set(exc, cb_id_iv_operation, o);
|
38
|
-
ctx->exception =
|
38
|
+
ctx->exception = exc;
|
39
39
|
}
|
40
40
|
val = ULL2NUM(resp->v.v0.value);
|
41
41
|
if (bucket->async) { /* asynchronous */
|
@@ -51,16 +51,16 @@ cb_arithmetic_callback(lcb_t handle, const void *cookie, lcb_error_t error, cons
|
|
51
51
|
} else { /* synchronous */
|
52
52
|
if (NIL_P(exc)) {
|
53
53
|
if (ctx->extended) {
|
54
|
-
rb_hash_aset(
|
54
|
+
rb_hash_aset(ctx->rv, key, rb_ary_new3(2, val, cas));
|
55
55
|
} else {
|
56
|
-
rb_hash_aset(
|
56
|
+
rb_hash_aset(ctx->rv, key, val);
|
57
57
|
}
|
58
58
|
}
|
59
59
|
}
|
60
60
|
if (ctx->nqueries == 0) {
|
61
|
-
|
61
|
+
ctx->proc = Qnil;
|
62
62
|
if (bucket->async) {
|
63
|
-
|
63
|
+
cb_context_free(ctx);
|
64
64
|
}
|
65
65
|
}
|
66
66
|
(void)handle;
|
@@ -71,38 +71,30 @@ cb_bucket_arithmetic(int sign, int argc, VALUE *argv, VALUE self)
|
|
71
71
|
{
|
72
72
|
struct cb_bucket_st *bucket = DATA_PTR(self);
|
73
73
|
struct cb_context_st *ctx;
|
74
|
-
VALUE
|
74
|
+
VALUE rv, proc, exc;
|
75
75
|
lcb_error_t err;
|
76
76
|
struct cb_params_st params;
|
77
77
|
|
78
|
-
if (bucket
|
79
|
-
|
78
|
+
if (!cb_bucket_connected_bang(bucket, sign > 0 ? cb_sym_increment : cb_sym_decrement)) {
|
79
|
+
return Qnil;
|
80
80
|
}
|
81
|
-
|
81
|
+
|
82
|
+
memset(¶ms, 0, sizeof(struct cb_params_st));
|
83
|
+
rb_scan_args(argc, argv, "0*&", ¶ms.args, &proc);
|
82
84
|
if (!bucket->async && proc != Qnil) {
|
83
85
|
rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
|
84
86
|
}
|
85
|
-
memset(¶ms, 0, sizeof(struct cb_params_st));
|
86
87
|
params.type = cb_cmd_arith;
|
87
88
|
params.bucket = bucket;
|
88
89
|
params.cmd.arith.sign = sign;
|
89
|
-
cb_params_build(¶ms
|
90
|
-
ctx =
|
91
|
-
if (ctx == NULL) {
|
92
|
-
rb_raise(cb_eClientNoMemoryError, "failed to allocate memory for context");
|
93
|
-
}
|
94
|
-
rv = rb_hash_new();
|
95
|
-
ctx->rv = &rv;
|
96
|
-
ctx->bucket = bucket;
|
97
|
-
ctx->proc = cb_gc_protect(bucket, proc);
|
98
|
-
ctx->exception = Qnil;
|
99
|
-
ctx->nqueries = params.cmd.arith.num;
|
90
|
+
cb_params_build(¶ms);
|
91
|
+
ctx = cb_context_alloc_common(bucket, proc, params.cmd.arith.num);
|
100
92
|
err = lcb_arithmetic(bucket->handle, (const void *)ctx,
|
101
93
|
params.cmd.arith.num, params.cmd.arith.ptr);
|
102
94
|
cb_params_destroy(¶ms);
|
103
95
|
exc = cb_check_error(err, "failed to schedule arithmetic request", Qnil);
|
104
96
|
if (exc != Qnil) {
|
105
|
-
|
97
|
+
cb_context_free(ctx);
|
106
98
|
rb_exc_raise(exc);
|
107
99
|
}
|
108
100
|
bucket->nbytes += params.npayload;
|
@@ -115,9 +107,9 @@ cb_bucket_arithmetic(int sign, int argc, VALUE *argv, VALUE self)
|
|
115
107
|
lcb_wait(bucket->handle);
|
116
108
|
}
|
117
109
|
exc = ctx->exception;
|
118
|
-
|
110
|
+
rv = ctx->rv;
|
111
|
+
cb_context_free(ctx);
|
119
112
|
if (exc != Qnil) {
|
120
|
-
cb_gc_unprotect(bucket, exc);
|
121
113
|
rb_exc_raise(exc);
|
122
114
|
}
|
123
115
|
if (params.cmd.store.num > 1) {
|
data/ext/couchbase_ext/bucket.c
CHANGED
@@ -17,6 +17,24 @@
|
|
17
17
|
|
18
18
|
#include "couchbase_ext.h"
|
19
19
|
|
20
|
+
static VALUE
|
21
|
+
trigger_on_connect_callback(VALUE self)
|
22
|
+
{
|
23
|
+
struct cb_bucket_st *bucket = DATA_PTR(self);
|
24
|
+
VALUE on_connect_proc = bucket->on_connect_proc;
|
25
|
+
if (RTEST(on_connect_proc)) {
|
26
|
+
VALUE res = rb_class_new_instance(0, NULL, cb_cResult);
|
27
|
+
rb_ivar_set(res, cb_id_iv_error, bucket->exception);
|
28
|
+
bucket->exception = Qnil;
|
29
|
+
rb_ivar_set(res, cb_id_iv_operation, cb_sym_connect);
|
30
|
+
rb_ivar_set(res, cb_id_iv_value, self);
|
31
|
+
return rb_funcall(on_connect_proc, cb_id_call, 1, res);
|
32
|
+
} else {
|
33
|
+
bucket->trigger_connect_cb_on_set = 1;
|
34
|
+
return Qnil;
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
20
38
|
static void
|
21
39
|
error_callback(lcb_t handle, lcb_error_t error, const char *errinfo)
|
22
40
|
{
|
@@ -24,6 +42,20 @@ error_callback(lcb_t handle, lcb_error_t error, const char *errinfo)
|
|
24
42
|
|
25
43
|
lcb_breakout(handle);
|
26
44
|
bucket->exception = cb_check_error(error, errinfo, Qnil);
|
45
|
+
if (bucket->async && !bucket->connected) {
|
46
|
+
(void)trigger_on_connect_callback(bucket->self);
|
47
|
+
}
|
48
|
+
}
|
49
|
+
|
50
|
+
static void
|
51
|
+
configuration_callback(lcb_t handle, lcb_configuration_t config)
|
52
|
+
{
|
53
|
+
struct cb_bucket_st *bucket = (struct cb_bucket_st *)lcb_get_cookie(handle);
|
54
|
+
|
55
|
+
if (config == LCB_CONFIGURATION_NEW) {
|
56
|
+
bucket->connected = 1;
|
57
|
+
(void)trigger_on_connect_callback(bucket->self);
|
58
|
+
}
|
27
59
|
}
|
28
60
|
|
29
61
|
void
|
@@ -32,12 +64,21 @@ cb_bucket_free(void *ptr)
|
|
32
64
|
struct cb_bucket_st *bucket = ptr;
|
33
65
|
|
34
66
|
if (bucket) {
|
67
|
+
bucket->destroying = 1;
|
35
68
|
if (bucket->handle) {
|
36
69
|
lcb_destroy(bucket->handle);
|
37
70
|
lcb_destroy_io_ops(bucket->io);
|
38
71
|
}
|
72
|
+
st_free_table(bucket->object_space);
|
73
|
+
xfree(bucket);
|
39
74
|
}
|
40
|
-
|
75
|
+
}
|
76
|
+
|
77
|
+
static int
|
78
|
+
cb_bucket_mark_object_i(st_index_t key, st_data_t value, st_data_t arg)
|
79
|
+
{
|
80
|
+
((mark_f)value)((void*)key, (struct cb_bucket_st*)arg);
|
81
|
+
return ST_CONTINUE;
|
41
82
|
}
|
42
83
|
|
43
84
|
void
|
@@ -54,8 +95,9 @@ cb_bucket_mark(void *ptr)
|
|
54
95
|
rb_gc_mark(bucket->password);
|
55
96
|
rb_gc_mark(bucket->exception);
|
56
97
|
rb_gc_mark(bucket->on_error_proc);
|
98
|
+
rb_gc_mark(bucket->on_connect_proc);
|
57
99
|
rb_gc_mark(bucket->key_prefix_val);
|
58
|
-
|
100
|
+
st_foreach(bucket->object_space, cb_bucket_mark_object_i, (st_data_t)bucket);
|
59
101
|
}
|
60
102
|
}
|
61
103
|
|
@@ -207,6 +249,26 @@ do_scan_connection_options(struct cb_bucket_st *bucket, int argc, VALUE *argv)
|
|
207
249
|
bucket->default_arith_init = NUM2ULL(arg);
|
208
250
|
}
|
209
251
|
}
|
252
|
+
arg = rb_hash_aref(opts, cb_sym_engine);
|
253
|
+
if (arg != Qnil) {
|
254
|
+
if (arg == cb_sym_default) {
|
255
|
+
bucket->engine = cb_sym_default;
|
256
|
+
#ifndef _WIN32
|
257
|
+
} else if (arg == cb_sym_libev) {
|
258
|
+
bucket->engine = cb_sym_libev;
|
259
|
+
} else if (arg == cb_sym_libevent) {
|
260
|
+
bucket->engine = cb_sym_libevent;
|
261
|
+
#ifdef BUILD_EVENTMACHINE_PLUGIN
|
262
|
+
} else if (arg == cb_sym_eventmachine) {
|
263
|
+
bucket->engine = cb_sym_eventmachine;
|
264
|
+
#endif
|
265
|
+
#endif
|
266
|
+
} else {
|
267
|
+
VALUE ins = rb_funcall(arg, rb_intern("inspect"), 0);
|
268
|
+
rb_raise(rb_eArgError, "Couchbase: unknown engine %s", RSTRING_PTR(ins));
|
269
|
+
}
|
270
|
+
}
|
271
|
+
bucket->async = RTEST(rb_hash_aref(opts, cb_sym_async));
|
210
272
|
} else {
|
211
273
|
opts = Qnil;
|
212
274
|
}
|
@@ -223,6 +285,17 @@ do_scan_connection_options(struct cb_bucket_st *bucket, int argc, VALUE *argv)
|
|
223
285
|
rb_str_freeze(bucket->authority);
|
224
286
|
}
|
225
287
|
|
288
|
+
static VALUE
|
289
|
+
em_disconnect_block(VALUE unused, VALUE self)
|
290
|
+
{
|
291
|
+
struct cb_bucket_st *bucket = DATA_PTR(self);
|
292
|
+
if (bucket->handle) {
|
293
|
+
return cb_bucket_disconnect(self);
|
294
|
+
}
|
295
|
+
(void)unused;
|
296
|
+
return Qnil;
|
297
|
+
}
|
298
|
+
|
226
299
|
static void
|
227
300
|
do_connect(struct cb_bucket_st *bucket)
|
228
301
|
{
|
@@ -234,24 +307,37 @@ do_connect(struct cb_bucket_st *bucket)
|
|
234
307
|
lcb_destroy_io_ops(bucket->io);
|
235
308
|
bucket->handle = NULL;
|
236
309
|
bucket->io = NULL;
|
310
|
+
bucket->connected = 0;
|
237
311
|
}
|
238
312
|
|
239
|
-
#ifndef _WIN32
|
240
313
|
{
|
241
314
|
struct lcb_create_io_ops_st ciops;
|
242
315
|
memset(&ciops, 0, sizeof(ciops));
|
243
|
-
ciops.version =
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
316
|
+
ciops.version = 0;
|
317
|
+
|
318
|
+
if (bucket->engine == cb_sym_libevent) {
|
319
|
+
ciops.v.v0.type = LCB_IO_OPS_LIBEVENT;
|
320
|
+
} else if (bucket->engine == cb_sym_libev) {
|
321
|
+
ciops.v.v0.type = LCB_IO_OPS_LIBEV;
|
322
|
+
} else if (bucket->engine == cb_sym_eventmachine) {
|
323
|
+
ciops.version = 1;
|
324
|
+
ciops.v.v1.sofile = NULL;
|
325
|
+
ciops.v.v1.symbol = "cb_create_ruby_em_io_opts";
|
326
|
+
ciops.v.v1.cookie = bucket;
|
327
|
+
} else {
|
328
|
+
#ifdef _WIN32
|
329
|
+
ciops.v.v0.type = LCB_IO_OPS_DEFAULT;
|
250
330
|
#else
|
251
|
-
|
331
|
+
ciops.version = 1;
|
332
|
+
ciops.v.v1.sofile = NULL;
|
333
|
+
ciops.v.v1.symbol = "cb_create_ruby_mt_io_opts";
|
334
|
+
ciops.v.v1.cookie = NULL;
|
252
335
|
#endif
|
253
|
-
|
254
|
-
|
336
|
+
}
|
337
|
+
err = lcb_create_io_ops(&bucket->io, &ciops);
|
338
|
+
if (err != LCB_SUCCESS) {
|
339
|
+
rb_exc_raise(cb_check_error(err, "failed to create IO instance", Qnil));
|
340
|
+
}
|
255
341
|
}
|
256
342
|
|
257
343
|
memset(&create_opts, 0, sizeof(struct lcb_create_st));
|
@@ -279,6 +365,7 @@ do_connect(struct cb_bucket_st *bucket)
|
|
279
365
|
(void)lcb_set_http_data_callback(bucket->handle, cb_http_data_callback);
|
280
366
|
(void)lcb_set_observe_callback(bucket->handle, cb_observe_callback);
|
281
367
|
(void)lcb_set_unlock_callback(bucket->handle, cb_unlock_callback);
|
368
|
+
(void)lcb_set_configuration_callback(bucket->handle, configuration_callback);
|
282
369
|
|
283
370
|
if (bucket->timeout > 0) {
|
284
371
|
lcb_set_timeout(bucket->handle, bucket->timeout);
|
@@ -294,13 +381,19 @@ do_connect(struct cb_bucket_st *bucket)
|
|
294
381
|
rb_exc_raise(cb_check_error(err, "failed to connect libcouchbase instance to server", Qnil));
|
295
382
|
}
|
296
383
|
bucket->exception = Qnil;
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
bucket->
|
303
|
-
|
384
|
+
if (bucket->engine == cb_sym_eventmachine && !bucket->async_disconnect_hook_set) {
|
385
|
+
bucket->async_disconnect_hook_set = 1;
|
386
|
+
rb_block_call(em_m, cb_id_add_shutdown_hook, 0, NULL, em_disconnect_block, bucket->self);
|
387
|
+
}
|
388
|
+
if (!bucket->async) {
|
389
|
+
lcb_wait(bucket->handle);
|
390
|
+
if (bucket->exception != Qnil) {
|
391
|
+
lcb_destroy(bucket->handle);
|
392
|
+
lcb_destroy_io_ops(bucket->io);
|
393
|
+
bucket->handle = NULL;
|
394
|
+
bucket->io = NULL;
|
395
|
+
rb_exc_raise(bucket->exception);
|
396
|
+
}
|
304
397
|
}
|
305
398
|
}
|
306
399
|
|
@@ -374,6 +467,17 @@ cb_bucket_alloc(VALUE klass)
|
|
374
467
|
* non positive number forces creation missing keys with given default
|
375
468
|
* value. Setting it to +true+ will use zero as initial value. (see
|
376
469
|
* {Bucket#incr} and {Bucket#decr}).
|
470
|
+
* @option options [Symbol] :engine (:default) the IO engine to use
|
471
|
+
* Currently following engines are supported:
|
472
|
+
* :default :: Built-in engine (multi-thread friendly)
|
473
|
+
* :libevent :: libevent IO plugin from libcouchbase (optional)
|
474
|
+
* :libev :: libev IO plugin from libcouchbase (optional)
|
475
|
+
* :eventmachine :: EventMachine plugin (builtin, but requires EM gem and ruby 1.9+)
|
476
|
+
* @option options [true, false] :async (false) If true, the
|
477
|
+
* connection instance will be considered always asynchronous and
|
478
|
+
* IO interaction will be occured only when {Couchbase::Bucket#run}
|
479
|
+
* called. See {Couchbase::Bucket#on_connect} to hook your code
|
480
|
+
* after the instance will be connected.
|
377
481
|
*
|
378
482
|
* @example Initialize connection using default options
|
379
483
|
* Couchbase.new
|
@@ -406,14 +510,13 @@ cb_bucket_init(int argc, VALUE *argv, VALUE self)
|
|
406
510
|
bucket->self = self;
|
407
511
|
bucket->exception = Qnil;
|
408
512
|
bucket->type = LCB_TYPE_BUCKET;
|
409
|
-
bucket->hostname =
|
513
|
+
bucket->hostname = cb_vStrLocalhost;
|
410
514
|
bucket->port = 8091;
|
411
515
|
bucket->pool = cb_vStrDefault;
|
412
|
-
rb_str_freeze(bucket->pool);
|
413
516
|
bucket->bucket = cb_vStrDefault;
|
414
|
-
rb_str_freeze(bucket->bucket);
|
415
517
|
bucket->username = Qnil;
|
416
518
|
bucket->password = Qnil;
|
519
|
+
bucket->engine = cb_sym_default;
|
417
520
|
bucket->async = 0;
|
418
521
|
bucket->quiet = 0;
|
419
522
|
bucket->default_ttl = 0;
|
@@ -421,11 +524,16 @@ cb_bucket_init(int argc, VALUE *argv, VALUE self)
|
|
421
524
|
bucket->default_format = cb_sym_document;
|
422
525
|
bucket->default_observe_timeout = 2500000;
|
423
526
|
bucket->on_error_proc = Qnil;
|
527
|
+
bucket->on_connect_proc = Qnil;
|
424
528
|
bucket->timeout = 0;
|
425
529
|
bucket->environment = cb_sym_production;
|
426
530
|
bucket->key_prefix_val = Qnil;
|
427
531
|
bucket->node_list = Qnil;
|
428
|
-
bucket->object_space =
|
532
|
+
bucket->object_space = st_init_numtable();
|
533
|
+
bucket->destroying = 0;
|
534
|
+
bucket->connected = 0;
|
535
|
+
bucket->on_connect_proc = Qnil;
|
536
|
+
bucket->async_disconnect_hook_set = 0;
|
429
537
|
|
430
538
|
do_scan_connection_options(bucket, argc, argv);
|
431
539
|
do_connect(bucket);
|
@@ -467,6 +575,7 @@ cb_bucket_init_copy(VALUE copy, VALUE orig)
|
|
467
575
|
copy_b->bucket = orig_b->bucket;
|
468
576
|
copy_b->username = orig_b->username;
|
469
577
|
copy_b->password = orig_b->password;
|
578
|
+
copy_b->engine = orig_b->engine;
|
470
579
|
copy_b->async = orig_b->async;
|
471
580
|
copy_b->quiet = orig_b->quiet;
|
472
581
|
copy_b->default_format = orig_b->default_format;
|
@@ -475,10 +584,17 @@ cb_bucket_init_copy(VALUE copy, VALUE orig)
|
|
475
584
|
copy_b->environment = orig_b->environment;
|
476
585
|
copy_b->timeout = orig_b->timeout;
|
477
586
|
copy_b->exception = Qnil;
|
587
|
+
copy_b->async_disconnect_hook_set = 0;
|
478
588
|
if (orig_b->on_error_proc != Qnil) {
|
479
589
|
copy_b->on_error_proc = rb_funcall(orig_b->on_error_proc, cb_id_dup, 0);
|
480
590
|
}
|
591
|
+
if (orig_b->on_connect_proc != Qnil) {
|
592
|
+
copy_b->on_connect_proc = rb_funcall(orig_b->on_connect_proc, cb_id_dup, 0);
|
593
|
+
}
|
481
594
|
copy_b->key_prefix_val = orig_b->key_prefix_val;
|
595
|
+
copy_b->object_space = st_init_numtable();
|
596
|
+
copy_b->destroying = 0;
|
597
|
+
copy_b->connected = 0;
|
482
598
|
|
483
599
|
do_connect(copy_b);
|
484
600
|
|
@@ -529,7 +645,7 @@ cb_bucket_reconnect(int argc, VALUE *argv, VALUE self)
|
|
529
645
|
cb_bucket_connected_p(VALUE self)
|
530
646
|
{
|
531
647
|
struct cb_bucket_st *bucket = DATA_PTR(self);
|
532
|
-
return bucket->handle ? Qtrue : Qfalse;
|
648
|
+
return (bucket->handle && bucket->connected) ? Qtrue : Qfalse;
|
533
649
|
}
|
534
650
|
|
535
651
|
/* Document-method: async?
|
@@ -654,6 +770,51 @@ cb_bucket_on_error_get(VALUE self)
|
|
654
770
|
}
|
655
771
|
}
|
656
772
|
|
773
|
+
static
|
774
|
+
VALUE trigger_on_connect_callback_block(VALUE nil, VALUE self)
|
775
|
+
{
|
776
|
+
(void)nil;
|
777
|
+
return trigger_on_connect_callback(self);
|
778
|
+
}
|
779
|
+
|
780
|
+
VALUE
|
781
|
+
cb_bucket_on_connect_set(VALUE self, VALUE val)
|
782
|
+
{
|
783
|
+
struct cb_bucket_st *bucket = DATA_PTR(self);
|
784
|
+
|
785
|
+
if (rb_respond_to(val, cb_id_call)) {
|
786
|
+
bucket->on_connect_proc = val;
|
787
|
+
if (bucket->trigger_connect_cb_on_set) {
|
788
|
+
bucket->trigger_connect_cb_on_set = 0;
|
789
|
+
if (bucket->async) {
|
790
|
+
VALUE args[] = {INT2FIX(0)};
|
791
|
+
/* setup timer with zero interval to call on_connect
|
792
|
+
* callback on the next tick */
|
793
|
+
rb_block_call(bucket->self, cb_id_create_timer, 1,
|
794
|
+
args, trigger_on_connect_callback_block, bucket->self);
|
795
|
+
} else {
|
796
|
+
trigger_on_connect_callback(self);
|
797
|
+
}
|
798
|
+
}
|
799
|
+
} else {
|
800
|
+
bucket->on_connect_proc = Qnil;
|
801
|
+
}
|
802
|
+
|
803
|
+
return bucket->on_connect_proc;
|
804
|
+
}
|
805
|
+
|
806
|
+
VALUE
|
807
|
+
cb_bucket_on_connect_get(VALUE self)
|
808
|
+
{
|
809
|
+
struct cb_bucket_st *bucket = DATA_PTR(self);
|
810
|
+
|
811
|
+
if (rb_block_given_p()) {
|
812
|
+
return cb_bucket_on_connect_set(self, rb_block_proc());
|
813
|
+
} else {
|
814
|
+
return bucket->on_connect_proc;
|
815
|
+
}
|
816
|
+
}
|
817
|
+
|
657
818
|
VALUE
|
658
819
|
cb_bucket_timeout_get(VALUE self)
|
659
820
|
{
|
@@ -947,7 +1108,7 @@ cb_bucket_inspect(VALUE self)
|
|
947
1108
|
rb_id2name(SYM2ID(bucket->default_format)),
|
948
1109
|
bucket->default_flags,
|
949
1110
|
bucket->quiet ? "true" : "false",
|
950
|
-
bucket->handle ? "true" : "false",
|
1111
|
+
(bucket->handle && bucket->connected) ? "true" : "false",
|
951
1112
|
bucket->timeout);
|
952
1113
|
rb_str_buf_cat2(str, buf);
|
953
1114
|
if (RTEST(bucket->key_prefix_val)) {
|
@@ -978,12 +1139,14 @@ cb_maybe_do_loop(struct cb_bucket_st *bucket)
|
|
978
1139
|
do_run(VALUE *args)
|
979
1140
|
{
|
980
1141
|
VALUE self = args[0], opts = args[1], proc = args[2], exc;
|
1142
|
+
VALUE was_async = args[3];
|
981
1143
|
struct cb_bucket_st *bucket = DATA_PTR(self);
|
982
1144
|
|
983
1145
|
if (bucket->handle == NULL) {
|
984
1146
|
rb_raise(cb_eConnectError, "closed connection");
|
985
1147
|
}
|
986
|
-
|
1148
|
+
|
1149
|
+
if (bucket->running) {
|
987
1150
|
rb_raise(cb_eInvalidError, "nested #run");
|
988
1151
|
}
|
989
1152
|
bucket->threshold = 0;
|
@@ -996,12 +1159,29 @@ do_run(VALUE *args)
|
|
996
1159
|
}
|
997
1160
|
}
|
998
1161
|
bucket->async = 1;
|
999
|
-
|
1162
|
+
bucket->running = 1;
|
1163
|
+
if (proc != Qnil) {
|
1164
|
+
cb_proc_call(bucket, proc, 1, self);
|
1165
|
+
}
|
1166
|
+
if (bucket->exception != Qnil) {
|
1167
|
+
exc = bucket->exception;
|
1168
|
+
bucket->exception = Qnil;
|
1169
|
+
if (was_async) {
|
1170
|
+
cb_async_error_notify(bucket, exc);
|
1171
|
+
/* XXX return here? */
|
1172
|
+
} else {
|
1173
|
+
rb_exc_raise(exc);
|
1174
|
+
}
|
1175
|
+
}
|
1000
1176
|
do_loop(bucket);
|
1001
1177
|
if (bucket->exception != Qnil) {
|
1002
1178
|
exc = bucket->exception;
|
1003
1179
|
bucket->exception = Qnil;
|
1004
|
-
|
1180
|
+
if (!was_async) {
|
1181
|
+
rb_exc_raise(exc);
|
1182
|
+
}
|
1183
|
+
/* async connections notified immediately from the callbacks
|
1184
|
+
* via cb_async_error_notify() */
|
1005
1185
|
}
|
1006
1186
|
return Qnil;
|
1007
1187
|
}
|
@@ -1012,7 +1192,9 @@ ensure_run(VALUE *args)
|
|
1012
1192
|
VALUE self = args[0];
|
1013
1193
|
struct cb_bucket_st *bucket = DATA_PTR(self);
|
1014
1194
|
|
1015
|
-
bucket->
|
1195
|
+
bucket->running = 0;
|
1196
|
+
bucket->async = args[3];
|
1197
|
+
bucket->running = args[4];
|
1016
1198
|
return Qnil;
|
1017
1199
|
}
|
1018
1200
|
|
@@ -1054,6 +1236,12 @@ ensure_run(VALUE *args)
|
|
1054
1236
|
* end
|
1055
1237
|
* # all commands were executed and sent is 3 now
|
1056
1238
|
*
|
1239
|
+
* @example Use {Couchbase::Bucket#run} without block for async connection
|
1240
|
+
* c = Couchbase.new(:async => true)
|
1241
|
+
* c.run # ensure that instance connected
|
1242
|
+
* c.set("foo", "bar"){|r| puts r.cas}
|
1243
|
+
* c.run
|
1244
|
+
*
|
1057
1245
|
* @return [nil]
|
1058
1246
|
*
|
1059
1247
|
* @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
|
@@ -1061,11 +1249,17 @@ ensure_run(VALUE *args)
|
|
1061
1249
|
VALUE
|
1062
1250
|
cb_bucket_run(int argc, VALUE *argv, VALUE self)
|
1063
1251
|
{
|
1064
|
-
|
1252
|
+
struct cb_bucket_st *bucket = DATA_PTR(self);
|
1253
|
+
VALUE args[5];
|
1065
1254
|
|
1066
|
-
|
1255
|
+
/* it is allowed to omit block for async connections */
|
1256
|
+
if (!bucket->async) {
|
1257
|
+
rb_need_block();
|
1258
|
+
}
|
1067
1259
|
args[0] = self;
|
1068
1260
|
rb_scan_args(argc, argv, "01&", &args[1], &args[2]);
|
1261
|
+
args[3] = bucket->async;
|
1262
|
+
args[4] = bucket->running;
|
1069
1263
|
rb_ensure(do_run, (VALUE)args, ensure_run, (VALUE)args);
|
1070
1264
|
return Qnil;
|
1071
1265
|
}
|
@@ -1115,6 +1309,7 @@ cb_bucket_disconnect(VALUE self)
|
|
1115
1309
|
lcb_destroy_io_ops(bucket->io);
|
1116
1310
|
bucket->handle = NULL;
|
1117
1311
|
bucket->io = NULL;
|
1312
|
+
bucket->connected = 0;
|
1118
1313
|
return Qtrue;
|
1119
1314
|
} else {
|
1120
1315
|
rb_raise(cb_eConnectError, "closed connection");
|