couchbase 1.1.5 → 1.2.0.beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/.gitignore +2 -1
  2. data/.travis.yml +12 -1
  3. data/HISTORY.markdown +112 -1
  4. data/README.markdown +149 -6
  5. data/couchbase.gemspec +5 -1
  6. data/ext/couchbase_ext/.gitignore +4 -0
  7. data/ext/couchbase_ext/arguments.c +973 -0
  8. data/ext/couchbase_ext/arithmetic.c +322 -0
  9. data/ext/couchbase_ext/bucket.c +1092 -0
  10. data/ext/couchbase_ext/couchbase_ext.c +618 -3247
  11. data/ext/couchbase_ext/couchbase_ext.h +519 -0
  12. data/ext/couchbase_ext/delete.c +167 -0
  13. data/ext/couchbase_ext/extconf.rb +24 -5
  14. data/ext/couchbase_ext/get.c +301 -0
  15. data/ext/couchbase_ext/gethrtime.c +124 -0
  16. data/ext/couchbase_ext/http.c +402 -0
  17. data/ext/couchbase_ext/observe.c +174 -0
  18. data/ext/couchbase_ext/result.c +126 -0
  19. data/ext/couchbase_ext/stats.c +169 -0
  20. data/ext/couchbase_ext/store.c +522 -0
  21. data/ext/couchbase_ext/timer.c +192 -0
  22. data/ext/couchbase_ext/touch.c +190 -0
  23. data/ext/couchbase_ext/unlock.c +180 -0
  24. data/ext/couchbase_ext/utils.c +471 -0
  25. data/ext/couchbase_ext/version.c +147 -0
  26. data/lib/action_dispatch/middleware/session/couchbase_store.rb +38 -0
  27. data/lib/active_support/cache/couchbase_store.rb +356 -0
  28. data/lib/couchbase.rb +24 -3
  29. data/lib/couchbase/bucket.rb +372 -9
  30. data/lib/couchbase/result.rb +26 -0
  31. data/lib/couchbase/utils.rb +59 -0
  32. data/lib/couchbase/version.rb +1 -1
  33. data/lib/couchbase/view.rb +305 -0
  34. data/lib/couchbase/view_row.rb +230 -0
  35. data/lib/ext/multi_json_fix.rb +47 -0
  36. data/lib/rack/session/couchbase.rb +104 -0
  37. data/tasks/compile.rake +5 -14
  38. data/test/setup.rb +6 -2
  39. data/test/test_arithmetic.rb +32 -2
  40. data/test/test_async.rb +18 -4
  41. data/test/test_bucket.rb +11 -1
  42. data/test/test_cas.rb +13 -3
  43. data/test/test_couchbase_rails_cache_store.rb +294 -0
  44. data/test/test_delete.rb +60 -3
  45. data/test/test_format.rb +28 -17
  46. data/test/test_get.rb +91 -14
  47. data/test/test_store.rb +31 -1
  48. data/test/{test_flush.rb → test_timer.rb} +11 -18
  49. data/test/test_touch.rb +33 -5
  50. data/test/test_unlock.rb +120 -0
  51. data/test/test_utils.rb +26 -0
  52. metadata +101 -8
@@ -0,0 +1,174 @@
1
+ /* vim: ft=c et ts=8 sts=4 sw=4 cino=
2
+ *
3
+ * Copyright 2011, 2012 Couchbase, Inc.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ #include "couchbase_ext.h"
19
+
20
+ void
21
+ observe_callback(lcb_t handle, const void *cookie, lcb_error_t error, const lcb_observe_resp_t *resp)
22
+ {
23
+ struct context_st *ctx = (struct context_st *)cookie;
24
+ struct bucket_st *bucket = ctx->bucket;
25
+ VALUE key, res, *rv = ctx->rv;
26
+
27
+ if (resp->v.v0.key) {
28
+ key = STR_NEW((const char*)resp->v.v0.key, resp->v.v0.nkey);
29
+ ctx->exception = cb_check_error(error, "failed to execute observe request", key);
30
+ if (ctx->exception) {
31
+ cb_gc_protect(bucket, ctx->exception);
32
+ }
33
+ res = rb_class_new_instance(0, NULL, cResult);
34
+ rb_ivar_set(res, id_iv_completed, Qfalse);
35
+ rb_ivar_set(res, id_iv_error, ctx->exception);
36
+ rb_ivar_set(res, id_iv_operation, sym_observe);
37
+ rb_ivar_set(res, id_iv_key, key);
38
+ rb_ivar_set(res, id_iv_cas, ULL2NUM(resp->v.v0.cas));
39
+ rb_ivar_set(res, id_iv_from_master, resp->v.v0.from_master ? Qtrue : Qfalse);
40
+ rb_ivar_set(res, id_iv_time_to_persist, ULONG2NUM(resp->v.v0.ttp));
41
+ rb_ivar_set(res, id_iv_time_to_replicate, ULONG2NUM(resp->v.v0.ttr));
42
+ switch (resp->v.v0.status) {
43
+ case LCB_OBSERVE_FOUND:
44
+ rb_ivar_set(res, id_iv_status, sym_found);
45
+ break;
46
+ case LCB_OBSERVE_PERSISTED:
47
+ rb_ivar_set(res, id_iv_status, sym_persisted);
48
+ break;
49
+ case LCB_OBSERVE_NOT_FOUND:
50
+ rb_ivar_set(res, id_iv_status, sym_not_found);
51
+ break;
52
+ default:
53
+ rb_ivar_set(res, id_iv_status, Qnil);
54
+ }
55
+ if (bucket->async) { /* asynchronous */
56
+ if (ctx->proc != Qnil) {
57
+ cb_proc_call(ctx->proc, 1, res);
58
+ }
59
+ } else { /* synchronous */
60
+ if (NIL_P(ctx->exception)) {
61
+ VALUE stats = rb_hash_aref(*rv, key);
62
+ if (NIL_P(stats)) {
63
+ stats = rb_ary_new();
64
+ rb_hash_aset(*rv, key, stats);
65
+ }
66
+ rb_ary_push(stats, res);
67
+ }
68
+ }
69
+ } else {
70
+ if (bucket->async && ctx->proc != Qnil) {
71
+ res = rb_class_new_instance(0, NULL, cResult);
72
+ rb_ivar_set(res, id_iv_completed, Qtrue);
73
+ cb_proc_call(ctx->proc, 1, res);
74
+ }
75
+ ctx->nqueries--;
76
+ cb_gc_unprotect(bucket, ctx->proc);
77
+ }
78
+ (void)handle;
79
+ }
80
+
81
+ /*
82
+ * Observe key state
83
+ *
84
+ * @since 1.2.0.dp6
85
+ *
86
+ * @overload observe(*keys, options = {})
87
+ * @param keys [String, Symbol, Array] One or several keys to fetch
88
+ * @param options [Hash] Options for operation.
89
+ *
90
+ * @yieldparam ret [Result] the result of operation in asynchronous mode
91
+ * (valid attributes: +error+, +status+, +operation+, +key+, +cas+,
92
+ * +from_master+, +time_to_persist+, +time_to_replicate+).
93
+ *
94
+ * @return [Hash<String, Array<Result>>, Array<Result>] the state of the
95
+ * keys on all nodes. If the +keys+ argument was String or Symbol, this
96
+ * method will return just array of results (result per each node),
97
+ * otherwise it will return hash map.
98
+ *
99
+ * @example Observe single key
100
+ * c.observe("foo")
101
+ * #=> [#<Couchbase::Result:0x00000001650df0 ...>, ...]
102
+ *
103
+ * @example Observe multiple keys
104
+ * keys = ["foo", "bar"]
105
+ * stats = c.observe(keys)
106
+ * stats.size #=> 2
107
+ * stats["foo"] #=> [#<Couchbase::Result:0x00000001650df0 ...>, ...]
108
+ */
109
+
110
+ VALUE
111
+ cb_bucket_observe(int argc, VALUE *argv, VALUE self)
112
+ {
113
+ struct bucket_st *bucket = DATA_PTR(self);
114
+ struct context_st *ctx;
115
+ VALUE args, rv, proc, exc;
116
+ lcb_error_t err;
117
+ struct params_st params;
118
+
119
+ if (bucket->handle == NULL) {
120
+ rb_raise(eConnectError, "closed connection");
121
+ }
122
+ rb_scan_args(argc, argv, "0*&", &args, &proc);
123
+ if (!bucket->async && proc != Qnil) {
124
+ rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
125
+ }
126
+ memset(&params, 0, sizeof(struct params_st));
127
+ params.type = cmd_observe;
128
+ params.bucket = bucket;
129
+ cb_params_build(&params, RARRAY_LEN(args), args);
130
+ ctx = xcalloc(1, sizeof(struct context_st));
131
+ if (ctx == NULL) {
132
+ rb_raise(eClientNoMemoryError, "failed to allocate memory for context");
133
+ }
134
+ ctx->proc = cb_gc_protect(bucket, proc);
135
+ ctx->bucket = bucket;
136
+ rv = rb_hash_new();
137
+ ctx->rv = &rv;
138
+ ctx->exception = Qnil;
139
+ ctx->nqueries = params.cmd.observe.num;
140
+ err = lcb_observe(bucket->handle, (const void *)ctx,
141
+ params.cmd.observe.num, params.cmd.observe.ptr);
142
+ cb_params_destroy(&params);
143
+ exc = cb_check_error(err, "failed to schedule observe request", Qnil);
144
+ if (exc != Qnil) {
145
+ xfree(ctx);
146
+ rb_exc_raise(exc);
147
+ }
148
+ bucket->nbytes += params.npayload;
149
+ if (bucket->async) {
150
+ maybe_do_loop(bucket);
151
+ return Qnil;
152
+ } else {
153
+ if (ctx->nqueries > 0) {
154
+ /* we have some operations pending */
155
+ lcb_wait(bucket->handle);
156
+ }
157
+ exc = ctx->exception;
158
+ xfree(ctx);
159
+ if (exc != Qnil) {
160
+ cb_gc_unprotect(bucket, exc);
161
+ rb_exc_raise(exc);
162
+ }
163
+ if (bucket->exception != Qnil) {
164
+ rb_exc_raise(bucket->exception);
165
+ }
166
+ if (params.cmd.observe.num > 1 || params.cmd.observe.array) {
167
+ return rv; /* return as a hash {key => {}, ...} */
168
+ } else {
169
+ VALUE vv = Qnil;
170
+ rb_hash_foreach(rv, cb_first_value_i, (VALUE)&vv);
171
+ return vv; /* return first value */
172
+ }
173
+ }
174
+ }
@@ -0,0 +1,126 @@
1
+ /* vim: ft=c et ts=8 sts=4 sw=4 cino=
2
+ *
3
+ * Copyright 2011, 2012 Couchbase, Inc.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ #include "couchbase_ext.h"
19
+
20
+ /*
21
+ * Check if result of operation was successful.
22
+ *
23
+ * @since 1.0.0
24
+ *
25
+ * @return [true, false] +false+ if there is an +error+ object attached,
26
+ * +false+ otherwise.
27
+ */
28
+ VALUE
29
+ cb_result_success_p(VALUE self)
30
+ {
31
+ return RTEST(rb_ivar_get(self, id_iv_error)) ? Qfalse : Qtrue;
32
+ }
33
+
34
+ /*
35
+ * Returns a string containing a human-readable representation of the Result.
36
+ *
37
+ * @since 1.0.0
38
+ *
39
+ * @return [String]
40
+ */
41
+ VALUE
42
+ cb_result_inspect(VALUE self)
43
+ {
44
+ VALUE str, attr, error;
45
+ char buf[100];
46
+
47
+ str = rb_str_buf_new2("#<");
48
+ rb_str_buf_cat2(str, rb_obj_classname(self));
49
+ snprintf(buf, 100, ":%p", (void *)self);
50
+ rb_str_buf_cat2(str, buf);
51
+
52
+ attr = rb_ivar_get(self, id_iv_error);
53
+ if (RTEST(attr)) {
54
+ error = rb_ivar_get(attr, id_iv_error);
55
+ } else {
56
+ error = INT2FIX(0);
57
+ }
58
+ rb_str_buf_cat2(str, " error=0x");
59
+ rb_str_append(str, rb_funcall(error, id_to_s, 1, INT2FIX(16)));
60
+
61
+ attr = rb_ivar_get(self, id_iv_operation);
62
+ if (RTEST(attr)) {
63
+ rb_str_buf_cat2(str, " operation=");
64
+ rb_str_append(str, rb_inspect(attr));
65
+ }
66
+
67
+ attr = rb_ivar_get(self, id_iv_key);
68
+ if (RTEST(attr)) {
69
+ rb_str_buf_cat2(str, " key=");
70
+ rb_str_append(str, rb_inspect(attr));
71
+ }
72
+
73
+ attr = rb_ivar_get(self, id_iv_status);
74
+ if (RTEST(attr)) {
75
+ rb_str_buf_cat2(str, " status=");
76
+ rb_str_append(str, rb_inspect(attr));
77
+ }
78
+
79
+ attr = rb_ivar_get(self, id_iv_cas);
80
+ if (RTEST(attr)) {
81
+ rb_str_buf_cat2(str, " cas=");
82
+ rb_str_append(str, rb_inspect(attr));
83
+ }
84
+
85
+ attr = rb_ivar_get(self, id_iv_flags);
86
+ if (RTEST(attr)) {
87
+ rb_str_buf_cat2(str, " flags=0x");
88
+ rb_str_append(str, rb_funcall(attr, id_to_s, 1, INT2FIX(16)));
89
+ }
90
+
91
+ attr = rb_ivar_get(self, id_iv_node);
92
+ if (RTEST(attr)) {
93
+ rb_str_buf_cat2(str, " node=");
94
+ rb_str_append(str, rb_inspect(attr));
95
+ }
96
+
97
+ attr = rb_ivar_get(self, id_iv_from_master);
98
+ if (attr != Qnil) {
99
+ rb_str_buf_cat2(str, " from_master=");
100
+ rb_str_append(str, rb_inspect(attr));
101
+ }
102
+
103
+ attr = rb_ivar_get(self, id_iv_time_to_persist);
104
+ if (RTEST(attr)) {
105
+ rb_str_buf_cat2(str, " time_to_persist=");
106
+ rb_str_append(str, rb_inspect(attr));
107
+ }
108
+
109
+ attr = rb_ivar_get(self, id_iv_time_to_replicate);
110
+ if (RTEST(attr)) {
111
+ rb_str_buf_cat2(str, " time_to_replicate=");
112
+ rb_str_append(str, rb_inspect(attr));
113
+ }
114
+
115
+ attr = rb_ivar_get(self, id_iv_headers);
116
+ if (RTEST(attr)) {
117
+ rb_str_buf_cat2(str, " headers=");
118
+ rb_str_append(str, rb_inspect(attr));
119
+ }
120
+
121
+ rb_str_buf_cat2(str, ">");
122
+
123
+ return str;
124
+ }
125
+
126
+
@@ -0,0 +1,169 @@
1
+ /* vim: ft=c et ts=8 sts=4 sw=4 cino=
2
+ *
3
+ * Copyright 2011, 2012 Couchbase, Inc.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ #include "couchbase_ext.h"
19
+
20
+ void
21
+ stat_callback(lcb_t handle, const void *cookie, lcb_error_t error, const lcb_server_stat_resp_t *resp)
22
+ {
23
+ struct context_st *ctx = (struct context_st *)cookie;
24
+ struct bucket_st *bucket = ctx->bucket;
25
+ VALUE stats, node, key, val, *rv = ctx->rv, exc = Qnil, res;
26
+
27
+ node = resp->v.v0.server_endpoint ? STR_NEW_CSTR(resp->v.v0.server_endpoint) : Qnil;
28
+ exc = cb_check_error(error, "failed to fetch stats", node);
29
+ if (exc != Qnil) {
30
+ rb_ivar_set(exc, id_iv_operation, sym_stats);
31
+ if (NIL_P(ctx->exception)) {
32
+ ctx->exception = cb_gc_protect(bucket, exc);
33
+ }
34
+ }
35
+ if (node != Qnil) {
36
+ key = STR_NEW((const char*)resp->v.v0.key, resp->v.v0.nkey);
37
+ val = STR_NEW((const char*)resp->v.v0.bytes, resp->v.v0.nbytes);
38
+ if (bucket->async) { /* asynchronous */
39
+ if (ctx->proc != Qnil) {
40
+ res = rb_class_new_instance(0, NULL, cResult);
41
+ rb_ivar_set(res, id_iv_error, exc);
42
+ rb_ivar_set(res, id_iv_operation, sym_stats);
43
+ rb_ivar_set(res, id_iv_node, node);
44
+ rb_ivar_set(res, id_iv_key, key);
45
+ rb_ivar_set(res, id_iv_value, val);
46
+ cb_proc_call(ctx->proc, 1, res);
47
+ }
48
+ } else { /* synchronous */
49
+ if (NIL_P(exc)) {
50
+ stats = rb_hash_aref(*rv, key);
51
+ if (NIL_P(stats)) {
52
+ stats = rb_hash_new();
53
+ rb_hash_aset(*rv, key, stats);
54
+ }
55
+ rb_hash_aset(stats, node, val);
56
+ }
57
+ }
58
+ } else {
59
+ ctx->nqueries--;
60
+ cb_gc_unprotect(bucket, ctx->proc);
61
+ }
62
+ (void)handle;
63
+ }
64
+
65
+ /*
66
+ * Request server statistics.
67
+ *
68
+ * @since 1.0.0
69
+ *
70
+ * Fetches stats from each node in cluster. Without a key specified the
71
+ * server will respond with a "default" set of statistical information. In
72
+ * asynchronous mode each statistic is returned in separate call where the
73
+ * Result object yielded (+#key+ contains the name of the statistical item
74
+ * and the +#value+ contains the value, the +#node+ will indicate the server
75
+ * address). In synchronous mode it returns the hash of stats keys and
76
+ * node-value pairs as a value.
77
+ *
78
+ * @overload stats(arg = nil)
79
+ * @param [String] arg argument to STATS query
80
+ * @yieldparam [Result] ret the object with +node+, +key+ and +value+
81
+ * attributes.
82
+ *
83
+ * @example Found how many items in the bucket
84
+ * total = 0
85
+ * c.stats["total_items"].each do |key, value|
86
+ * total += value.to_i
87
+ * end
88
+ *
89
+ * @example Found total items number asynchronously
90
+ * total = 0
91
+ * c.run do
92
+ * c.stats do |ret|
93
+ * if ret.key == "total_items"
94
+ * total += ret.value.to_i
95
+ * end
96
+ * end
97
+ * end
98
+ *
99
+ * @example Get memory stats (works on couchbase buckets)
100
+ * c.stats(:memory) #=> {"mem_used"=>{...}, ...}
101
+ *
102
+ * @return [Hash] where keys are stat keys, values are host-value pairs
103
+ *
104
+ * @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
105
+ * @raise [ArgumentError] when passing the block in synchronous mode
106
+ */
107
+ VALUE
108
+ cb_bucket_stats(int argc, VALUE *argv, VALUE self)
109
+ {
110
+ struct bucket_st *bucket = DATA_PTR(self);
111
+ struct context_st *ctx;
112
+ VALUE rv, exc, args, proc;
113
+ lcb_error_t err;
114
+ struct params_st params;
115
+
116
+ if (bucket->handle == NULL) {
117
+ rb_raise(eConnectError, "closed connection");
118
+ }
119
+ rb_scan_args(argc, argv, "0*&", &args, &proc);
120
+ if (!bucket->async && proc != Qnil) {
121
+ rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
122
+ }
123
+ memset(&params, 0, sizeof(struct params_st));
124
+ params.type = cmd_stats;
125
+ params.bucket = bucket;
126
+ cb_params_build(&params, RARRAY_LEN(args), args);
127
+ ctx = xcalloc(1, sizeof(struct context_st));
128
+ if (ctx == NULL) {
129
+ rb_raise(eClientNoMemoryError, "failed to allocate memory for context");
130
+ }
131
+ rv = rb_hash_new();
132
+ ctx->rv = &rv;
133
+ ctx->bucket = bucket;
134
+ ctx->proc = cb_gc_protect(bucket, proc);
135
+ ctx->exception = Qnil;
136
+ ctx->nqueries = params.cmd.stats.num;
137
+ err = lcb_server_stats(bucket->handle, (const void *)ctx,
138
+ params.cmd.stats.num, params.cmd.stats.ptr);
139
+ exc = cb_check_error(err, "failed to schedule stat request", Qnil);
140
+ cb_params_destroy(&params);
141
+ if (exc != Qnil) {
142
+ xfree(ctx);
143
+ rb_exc_raise(exc);
144
+ }
145
+ bucket->nbytes += params.npayload;
146
+ if (bucket->async) {
147
+ maybe_do_loop(bucket);
148
+ return Qnil;
149
+ } else {
150
+ if (ctx->nqueries > 0) {
151
+ /* we have some operations pending */
152
+ lcb_wait(bucket->handle);
153
+ }
154
+ exc = ctx->exception;
155
+ xfree(ctx);
156
+ if (exc != Qnil) {
157
+ cb_gc_unprotect(bucket, exc);
158
+ rb_exc_raise(exc);
159
+ }
160
+ if (bucket->exception != Qnil) {
161
+ rb_exc_raise(bucket->exception);
162
+ }
163
+ return rv;
164
+ }
165
+
166
+ return Qnil;
167
+ }
168
+
169
+