couchbase 1.2.1-x86-mingw32 → 1.2.2-x86-mingw32
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/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 +24 -49
- 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");
|