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.
- data/.gitignore +2 -1
- data/.travis.yml +12 -1
- data/HISTORY.markdown +112 -1
- data/README.markdown +149 -6
- data/couchbase.gemspec +5 -1
- data/ext/couchbase_ext/.gitignore +4 -0
- data/ext/couchbase_ext/arguments.c +973 -0
- data/ext/couchbase_ext/arithmetic.c +322 -0
- data/ext/couchbase_ext/bucket.c +1092 -0
- data/ext/couchbase_ext/couchbase_ext.c +618 -3247
- data/ext/couchbase_ext/couchbase_ext.h +519 -0
- data/ext/couchbase_ext/delete.c +167 -0
- data/ext/couchbase_ext/extconf.rb +24 -5
- data/ext/couchbase_ext/get.c +301 -0
- data/ext/couchbase_ext/gethrtime.c +124 -0
- data/ext/couchbase_ext/http.c +402 -0
- data/ext/couchbase_ext/observe.c +174 -0
- data/ext/couchbase_ext/result.c +126 -0
- data/ext/couchbase_ext/stats.c +169 -0
- data/ext/couchbase_ext/store.c +522 -0
- data/ext/couchbase_ext/timer.c +192 -0
- data/ext/couchbase_ext/touch.c +190 -0
- data/ext/couchbase_ext/unlock.c +180 -0
- data/ext/couchbase_ext/utils.c +471 -0
- data/ext/couchbase_ext/version.c +147 -0
- data/lib/action_dispatch/middleware/session/couchbase_store.rb +38 -0
- data/lib/active_support/cache/couchbase_store.rb +356 -0
- data/lib/couchbase.rb +24 -3
- data/lib/couchbase/bucket.rb +372 -9
- data/lib/couchbase/result.rb +26 -0
- data/lib/couchbase/utils.rb +59 -0
- data/lib/couchbase/version.rb +1 -1
- data/lib/couchbase/view.rb +305 -0
- data/lib/couchbase/view_row.rb +230 -0
- data/lib/ext/multi_json_fix.rb +47 -0
- data/lib/rack/session/couchbase.rb +104 -0
- data/tasks/compile.rake +5 -14
- data/test/setup.rb +6 -2
- data/test/test_arithmetic.rb +32 -2
- data/test/test_async.rb +18 -4
- data/test/test_bucket.rb +11 -1
- data/test/test_cas.rb +13 -3
- data/test/test_couchbase_rails_cache_store.rb +294 -0
- data/test/test_delete.rb +60 -3
- data/test/test_format.rb +28 -17
- data/test/test_get.rb +91 -14
- data/test/test_store.rb +31 -1
- data/test/{test_flush.rb → test_timer.rb} +11 -18
- data/test/test_touch.rb +33 -5
- data/test/test_unlock.rb +120 -0
- data/test/test_utils.rb +26 -0
- metadata +101 -8
@@ -0,0 +1,192 @@
|
|
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
|
+
cb_timer_free(void *ptr)
|
22
|
+
{
|
23
|
+
xfree(ptr);
|
24
|
+
}
|
25
|
+
|
26
|
+
void
|
27
|
+
cb_timer_mark(void *ptr)
|
28
|
+
{
|
29
|
+
struct timer_st *timer = ptr;
|
30
|
+
if (timer) {
|
31
|
+
rb_gc_mark(timer->callback);
|
32
|
+
}
|
33
|
+
}
|
34
|
+
|
35
|
+
VALUE
|
36
|
+
cb_timer_alloc(VALUE klass)
|
37
|
+
{
|
38
|
+
VALUE obj;
|
39
|
+
struct timer_st *timer;
|
40
|
+
|
41
|
+
/* allocate new bucket struct and set it to zero */
|
42
|
+
obj = Data_Make_Struct(klass, struct timer_st, cb_timer_mark,
|
43
|
+
cb_timer_free, timer);
|
44
|
+
return obj;
|
45
|
+
}
|
46
|
+
|
47
|
+
/*
|
48
|
+
* Returns a string containing a human-readable representation of the
|
49
|
+
* Timer.
|
50
|
+
*
|
51
|
+
* @since 1.2.0.dp6
|
52
|
+
*
|
53
|
+
* @return [String]
|
54
|
+
*/
|
55
|
+
VALUE
|
56
|
+
cb_timer_inspect(VALUE self)
|
57
|
+
{
|
58
|
+
VALUE str;
|
59
|
+
struct timer_st *tm = DATA_PTR(self);
|
60
|
+
char buf[200];
|
61
|
+
|
62
|
+
str = rb_str_buf_new2("#<");
|
63
|
+
rb_str_buf_cat2(str, rb_obj_classname(self));
|
64
|
+
snprintf(buf, 20, ":%p", (void *)self);
|
65
|
+
rb_str_buf_cat2(str, buf);
|
66
|
+
snprintf(buf, 100, " timeout:%u periodic:%s>",
|
67
|
+
tm->usec, tm->periodic ? "true" : "false");
|
68
|
+
rb_str_buf_cat2(str, buf);
|
69
|
+
|
70
|
+
return str;
|
71
|
+
}
|
72
|
+
|
73
|
+
/*
|
74
|
+
* Cancel the timer.
|
75
|
+
*
|
76
|
+
* @since 1.2.0.dp6
|
77
|
+
*
|
78
|
+
* This operation makes sense for periodic timers or if one need to cancel
|
79
|
+
* regular timer before it will be triggered.
|
80
|
+
*
|
81
|
+
* @example Cancel periodic timer
|
82
|
+
* n = 1
|
83
|
+
* c.run do
|
84
|
+
* tm = c.create_periodic_timer(500000) do
|
85
|
+
* c.incr("foo") do
|
86
|
+
* if n == 5
|
87
|
+
* tm.cancel
|
88
|
+
* else
|
89
|
+
* n += 1
|
90
|
+
* end
|
91
|
+
* end
|
92
|
+
* end
|
93
|
+
* end
|
94
|
+
*
|
95
|
+
* @return [String]
|
96
|
+
*/
|
97
|
+
VALUE
|
98
|
+
cb_timer_cancel(VALUE self)
|
99
|
+
{
|
100
|
+
struct timer_st *tm = DATA_PTR(self);
|
101
|
+
lcb_timer_destroy(tm->bucket->handle, tm->timer);
|
102
|
+
return self;
|
103
|
+
}
|
104
|
+
|
105
|
+
static VALUE
|
106
|
+
trigger_timer(VALUE timer)
|
107
|
+
{
|
108
|
+
struct timer_st *tm = DATA_PTR(timer);
|
109
|
+
return cb_proc_call(tm->callback, 1, timer);
|
110
|
+
}
|
111
|
+
|
112
|
+
static void
|
113
|
+
timer_callback(lcb_timer_t timer, lcb_t instance,
|
114
|
+
const void *cookie)
|
115
|
+
{
|
116
|
+
struct timer_st *tm = (struct timer_st *)cookie;
|
117
|
+
int error = 0;
|
118
|
+
|
119
|
+
rb_protect(trigger_timer, tm->self, &error);
|
120
|
+
if (error) {
|
121
|
+
lcb_timer_destroy(instance, timer);
|
122
|
+
}
|
123
|
+
(void)cookie;
|
124
|
+
}
|
125
|
+
|
126
|
+
/*
|
127
|
+
* Initialize new Timer
|
128
|
+
*
|
129
|
+
* @since 1.2.0
|
130
|
+
*
|
131
|
+
* The timers could used to trigger reccuring events or implement timeouts.
|
132
|
+
* The library will call given block after time interval pass.
|
133
|
+
*
|
134
|
+
* @param bucket [Bucket] the connection object
|
135
|
+
* @param interval [Fixnum] the interval in microseconds
|
136
|
+
* @param options [Hash]
|
137
|
+
* @option options [Boolean] :periodic (false) set it to +true+ if the timer
|
138
|
+
* should be triggered until it will be canceled.
|
139
|
+
*
|
140
|
+
* @yieldparam [Timer] timer the current timer
|
141
|
+
*
|
142
|
+
* @example Create regular timer for 0.5 second
|
143
|
+
* c.run do
|
144
|
+
* Couchbase::Timer.new(c, 500000) do
|
145
|
+
* puts "ding-dong"
|
146
|
+
* end
|
147
|
+
* end
|
148
|
+
*
|
149
|
+
* @example Create periodic timer
|
150
|
+
* n = 10
|
151
|
+
* c.run do
|
152
|
+
* Couchbase::Timer.new(c, 500000, :periodic => true) do |tm|
|
153
|
+
* puts "#{n}"
|
154
|
+
* n -= 1
|
155
|
+
* tm.cancel if n.zero?
|
156
|
+
* end
|
157
|
+
* end
|
158
|
+
*
|
159
|
+
*
|
160
|
+
* @return [Couchbase::Timer]
|
161
|
+
*/
|
162
|
+
VALUE
|
163
|
+
cb_timer_init(int argc, VALUE *argv, VALUE self)
|
164
|
+
{
|
165
|
+
struct timer_st *tm = DATA_PTR(self);
|
166
|
+
VALUE bucket, opts, timeout, exc, cb;
|
167
|
+
lcb_error_t err;
|
168
|
+
|
169
|
+
rb_need_block();
|
170
|
+
rb_scan_args(argc, argv, "21&", &bucket, &timeout, &opts, &cb);
|
171
|
+
|
172
|
+
if (CLASS_OF(bucket) != cBucket) {
|
173
|
+
rb_raise(rb_eTypeError, "wrong argument type (expected Couchbase::Bucket)");
|
174
|
+
}
|
175
|
+
tm->self = self;
|
176
|
+
tm->callback = cb;
|
177
|
+
tm->usec = NUM2ULONG(timeout);
|
178
|
+
tm->bucket = DATA_PTR(bucket);
|
179
|
+
if (opts != Qnil) {
|
180
|
+
Check_Type(opts, T_HASH);
|
181
|
+
tm->periodic = RTEST(rb_hash_aref(opts, sym_periodic));
|
182
|
+
}
|
183
|
+
tm->timer = lcb_timer_create(tm->bucket->handle, tm, tm->usec,
|
184
|
+
tm->periodic, timer_callback, &err);
|
185
|
+
exc = cb_check_error(err, "failed to attach the timer", Qnil);
|
186
|
+
if (exc != Qnil) {
|
187
|
+
rb_exc_raise(exc);
|
188
|
+
}
|
189
|
+
|
190
|
+
return self;
|
191
|
+
}
|
192
|
+
|
@@ -0,0 +1,190 @@
|
|
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
|
+
touch_callback(lcb_t handle, const void *cookie, lcb_error_t error, const lcb_touch_resp_t *resp)
|
22
|
+
{
|
23
|
+
struct context_st *ctx = (struct context_st *)cookie;
|
24
|
+
struct bucket_st *bucket = ctx->bucket;
|
25
|
+
VALUE key, *rv = ctx->rv, exc = Qnil, res;
|
26
|
+
|
27
|
+
ctx->nqueries--;
|
28
|
+
key = STR_NEW((const char*)resp->v.v0.key, resp->v.v0.nkey);
|
29
|
+
strip_key_prefix(bucket, key);
|
30
|
+
|
31
|
+
if (error != LCB_KEY_ENOENT || !ctx->quiet) {
|
32
|
+
exc = cb_check_error(error, "failed to touch value", key);
|
33
|
+
if (exc != Qnil) {
|
34
|
+
rb_ivar_set(exc, id_iv_operation, sym_touch);
|
35
|
+
if (NIL_P(ctx->exception)) {
|
36
|
+
ctx->exception = cb_gc_protect(bucket, exc);
|
37
|
+
}
|
38
|
+
}
|
39
|
+
}
|
40
|
+
|
41
|
+
if (bucket->async) { /* asynchronous */
|
42
|
+
if (ctx->proc != Qnil) {
|
43
|
+
res = rb_class_new_instance(0, NULL, cResult);
|
44
|
+
rb_ivar_set(res, id_iv_error, exc);
|
45
|
+
rb_ivar_set(res, id_iv_operation, sym_touch);
|
46
|
+
rb_ivar_set(res, id_iv_key, key);
|
47
|
+
cb_proc_call(ctx->proc, 1, res);
|
48
|
+
}
|
49
|
+
} else { /* synchronous */
|
50
|
+
rb_hash_aset(*rv, key, (error == LCB_SUCCESS) ? Qtrue : Qfalse);
|
51
|
+
}
|
52
|
+
if (ctx->nqueries == 0) {
|
53
|
+
cb_gc_unprotect(bucket, ctx->proc);
|
54
|
+
}
|
55
|
+
(void)handle;
|
56
|
+
}
|
57
|
+
|
58
|
+
/*
|
59
|
+
* Update the expiry time of an item
|
60
|
+
*
|
61
|
+
* @since 1.0.0
|
62
|
+
*
|
63
|
+
* The +touch+ method allow you to update the expiration time on a given
|
64
|
+
* key. This can be useful for situations where you want to prevent an item
|
65
|
+
* from expiring without resetting the associated value. For example, for a
|
66
|
+
* session database you might want to keep the session alive in the database
|
67
|
+
* each time the user accesses a web page without explicitly updating the
|
68
|
+
* session value, keeping the user's session active and available.
|
69
|
+
*
|
70
|
+
* @overload touch(key, options = {})
|
71
|
+
* @param key [String, Symbol] Key used to reference the value.
|
72
|
+
* @param options [Hash] Options for operation.
|
73
|
+
* @option options [Fixnum] :ttl (self.default_ttl) Expiry time for key.
|
74
|
+
* Values larger than 30*24*60*60 seconds (30 days) are interpreted as
|
75
|
+
* absolute times (from the epoch).
|
76
|
+
* @option options [true, false] :quiet (self.quiet) If set to +true+, the
|
77
|
+
* operation won't raise error for missing key, it will return +nil+.
|
78
|
+
*
|
79
|
+
* @yieldparam ret [Result] the result of operation in asynchronous mode
|
80
|
+
* (valid attributes: +error+, +operation+, +key+).
|
81
|
+
*
|
82
|
+
* @return [true, false] +true+ if the operation was successful and +false+
|
83
|
+
* otherwise.
|
84
|
+
*
|
85
|
+
* @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
|
86
|
+
*
|
87
|
+
* @raise [ArgumentError] when passing the block in synchronous mode
|
88
|
+
*
|
89
|
+
* @example Touch value using +default_ttl+
|
90
|
+
* c.touch("foo")
|
91
|
+
*
|
92
|
+
* @example Touch value using custom TTL (10 seconds)
|
93
|
+
* c.touch("foo", :ttl => 10)
|
94
|
+
*
|
95
|
+
* @overload touch(keys)
|
96
|
+
* @param keys [Hash] The Hash where keys represent the keys in the
|
97
|
+
* database, values -- the expiry times for corresponding key. See
|
98
|
+
* description of +:ttl+ argument above for more information about TTL
|
99
|
+
* values.
|
100
|
+
*
|
101
|
+
* @yieldparam ret [Result] the result of operation for each key in
|
102
|
+
* asynchronous mode (valid attributes: +error+, +operation+, +key+).
|
103
|
+
*
|
104
|
+
* @return [Hash] Mapping keys to result of touch operation (+true+ if the
|
105
|
+
* operation was successful and +false+ otherwise)
|
106
|
+
*
|
107
|
+
* @example Touch several values
|
108
|
+
* c.touch("foo" => 10, :bar => 20) #=> {"foo" => true, "bar" => true}
|
109
|
+
*
|
110
|
+
* @example Touch several values in async mode
|
111
|
+
* c.run do
|
112
|
+
* c.touch("foo" => 10, :bar => 20) do |ret|
|
113
|
+
* ret.operation #=> :touch
|
114
|
+
* ret.success? #=> true
|
115
|
+
* ret.key #=> "foo" and "bar" in separate calls
|
116
|
+
* end
|
117
|
+
* end
|
118
|
+
*
|
119
|
+
* @example Touch single value
|
120
|
+
* c.touch("foo" => 10) #=> true
|
121
|
+
*
|
122
|
+
*/
|
123
|
+
VALUE
|
124
|
+
cb_bucket_touch(int argc, VALUE *argv, VALUE self)
|
125
|
+
{
|
126
|
+
struct bucket_st *bucket = DATA_PTR(self);
|
127
|
+
struct context_st *ctx;
|
128
|
+
VALUE args, rv, proc, exc;
|
129
|
+
lcb_error_t err;
|
130
|
+
struct params_st params;
|
131
|
+
|
132
|
+
if (bucket->handle == NULL) {
|
133
|
+
rb_raise(eConnectError, "closed connection");
|
134
|
+
}
|
135
|
+
rb_scan_args(argc, argv, "0*&", &args, &proc);
|
136
|
+
if (!bucket->async && proc != Qnil) {
|
137
|
+
rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
|
138
|
+
}
|
139
|
+
rb_funcall(args, id_flatten_bang, 0);
|
140
|
+
memset(¶ms, 0, sizeof(struct params_st));
|
141
|
+
params.type = cmd_touch;
|
142
|
+
params.bucket = bucket;
|
143
|
+
cb_params_build(¶ms, RARRAY_LEN(args), args);
|
144
|
+
ctx = xcalloc(1, sizeof(struct context_st));
|
145
|
+
if (ctx == NULL) {
|
146
|
+
rb_raise(eClientNoMemoryError, "failed to allocate memory for context");
|
147
|
+
}
|
148
|
+
ctx->proc = cb_gc_protect(bucket, proc);
|
149
|
+
ctx->bucket = bucket;
|
150
|
+
rv = rb_hash_new();
|
151
|
+
ctx->rv = &rv;
|
152
|
+
ctx->exception = Qnil;
|
153
|
+
ctx->quiet = params.cmd.touch.quiet;
|
154
|
+
ctx->nqueries = params.cmd.touch.num;
|
155
|
+
err = lcb_touch(bucket->handle, (const void *)ctx,
|
156
|
+
params.cmd.touch.num, params.cmd.touch.ptr);
|
157
|
+
cb_params_destroy(¶ms);
|
158
|
+
exc = cb_check_error(err, "failed to schedule touch request", Qnil);
|
159
|
+
if (exc != Qnil) {
|
160
|
+
xfree(ctx);
|
161
|
+
rb_exc_raise(exc);
|
162
|
+
}
|
163
|
+
bucket->nbytes += params.npayload;
|
164
|
+
if (bucket->async) {
|
165
|
+
maybe_do_loop(bucket);
|
166
|
+
return Qnil;
|
167
|
+
} else {
|
168
|
+
if (ctx->nqueries > 0) {
|
169
|
+
/* we have some operations pending */
|
170
|
+
lcb_wait(bucket->handle);
|
171
|
+
}
|
172
|
+
exc = ctx->exception;
|
173
|
+
xfree(ctx);
|
174
|
+
if (exc != Qnil) {
|
175
|
+
rb_exc_raise(cb_gc_unprotect(bucket, exc));
|
176
|
+
}
|
177
|
+
if (bucket->exception != Qnil) {
|
178
|
+
rb_exc_raise(bucket->exception);
|
179
|
+
}
|
180
|
+
if (params.cmd.touch.num > 1) {
|
181
|
+
return rv; /* return as a hash {key => true, ...} */
|
182
|
+
} else {
|
183
|
+
VALUE vv = Qnil;
|
184
|
+
rb_hash_foreach(rv, cb_first_value_i, (VALUE)&vv);
|
185
|
+
return vv;
|
186
|
+
}
|
187
|
+
}
|
188
|
+
}
|
189
|
+
|
190
|
+
|
@@ -0,0 +1,180 @@
|
|
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
|
+
unlock_callback(lcb_t handle, const void *cookie, lcb_error_t error, const lcb_unlock_resp_t *resp)
|
22
|
+
{
|
23
|
+
struct context_st *ctx = (struct context_st *)cookie;
|
24
|
+
struct bucket_st *bucket = ctx->bucket;
|
25
|
+
VALUE key, *rv = ctx->rv, exc = Qnil, res;
|
26
|
+
|
27
|
+
ctx->nqueries--;
|
28
|
+
key = STR_NEW((const char*)resp->v.v0.key, resp->v.v0.nkey);
|
29
|
+
strip_key_prefix(bucket, key);
|
30
|
+
|
31
|
+
if (error != LCB_KEY_ENOENT || !ctx->quiet) {
|
32
|
+
exc = cb_check_error(error, "failed to unlock value", key);
|
33
|
+
if (exc != Qnil) {
|
34
|
+
rb_ivar_set(exc, id_iv_operation, sym_unlock);
|
35
|
+
if (NIL_P(ctx->exception)) {
|
36
|
+
ctx->exception = cb_gc_protect(bucket, exc);
|
37
|
+
}
|
38
|
+
}
|
39
|
+
}
|
40
|
+
|
41
|
+
if (bucket->async) { /* asynchronous */
|
42
|
+
if (ctx->proc != Qnil) {
|
43
|
+
res = rb_class_new_instance(0, NULL, cResult);
|
44
|
+
rb_ivar_set(res, id_iv_error, exc);
|
45
|
+
rb_ivar_set(res, id_iv_operation, sym_unlock);
|
46
|
+
rb_ivar_set(res, id_iv_key, key);
|
47
|
+
cb_proc_call(ctx->proc, 1, res);
|
48
|
+
}
|
49
|
+
} else { /* synchronous */
|
50
|
+
rb_hash_aset(*rv, key, (error == LCB_SUCCESS) ? Qtrue : Qfalse);
|
51
|
+
}
|
52
|
+
if (ctx->nqueries == 0) {
|
53
|
+
cb_gc_unprotect(bucket, ctx->proc);
|
54
|
+
}
|
55
|
+
(void)handle;
|
56
|
+
}
|
57
|
+
|
58
|
+
/*
|
59
|
+
* Unlock key
|
60
|
+
*
|
61
|
+
* @since 1.2.0
|
62
|
+
*
|
63
|
+
* The +unlock+ method allow you to unlock key once locked by {Bucket#get}
|
64
|
+
* with +:lock+ option.
|
65
|
+
*
|
66
|
+
* @overload unlock(key, options = {})
|
67
|
+
* @param key [String, Symbol] Key used to reference the value.
|
68
|
+
* @param options [Hash] Options for operation.
|
69
|
+
* @option options [Fixnum] :cas The CAS value must match the current one
|
70
|
+
* from the storage.
|
71
|
+
* @option options [true, false] :quiet (self.quiet) If set to +true+, the
|
72
|
+
* operation won't raise error for missing key, it will return +nil+.
|
73
|
+
*
|
74
|
+
* @return [true, false] +true+ if the operation was successful and +false+
|
75
|
+
* otherwise.
|
76
|
+
*
|
77
|
+
* @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
|
78
|
+
*
|
79
|
+
* @raise [ArgumentError] when passing the block in synchronous mode
|
80
|
+
*
|
81
|
+
* @raise [Couchbase::Error::NotFound] if key(s) not found in the storage
|
82
|
+
*
|
83
|
+
* @raise [Couchbase::Error::TemporaryFail] if either the key wasn't
|
84
|
+
* locked or given CAS value doesn't match to actual in the storage
|
85
|
+
*
|
86
|
+
* @example Unlock the single key
|
87
|
+
* val, _, cas = c.get("foo", :lock => true, :extended => true)
|
88
|
+
* c.unlock("foo", :cas => cas)
|
89
|
+
*
|
90
|
+
* @overload unlock(keys)
|
91
|
+
* @param keys [Hash] The Hash where keys represent the keys in the
|
92
|
+
* database, values -- the CAS for corresponding key.
|
93
|
+
*
|
94
|
+
* @yieldparam ret [Result] the result of operation for each key in
|
95
|
+
* asynchronous mode (valid attributes: +error+, +operation+, +key+).
|
96
|
+
*
|
97
|
+
* @return [Hash] Mapping keys to result of unlock operation (+true+ if the
|
98
|
+
* operation was successful and +false+ otherwise)
|
99
|
+
*
|
100
|
+
* @example Unlock several keys
|
101
|
+
* c.unlock("foo" => cas1, :bar => cas2) #=> {"foo" => true, "bar" => true}
|
102
|
+
*
|
103
|
+
* @example Unlock several values in async mode
|
104
|
+
* c.run do
|
105
|
+
* c.unlock("foo" => 10, :bar => 20) do |ret|
|
106
|
+
* ret.operation #=> :unlock
|
107
|
+
* ret.success? #=> true
|
108
|
+
* ret.key #=> "foo" and "bar" in separate calls
|
109
|
+
* end
|
110
|
+
* end
|
111
|
+
*
|
112
|
+
*/
|
113
|
+
VALUE
|
114
|
+
cb_bucket_unlock(int argc, VALUE *argv, VALUE self)
|
115
|
+
{
|
116
|
+
struct bucket_st *bucket = DATA_PTR(self);
|
117
|
+
struct context_st *ctx;
|
118
|
+
VALUE args, rv, proc, exc;
|
119
|
+
lcb_error_t err;
|
120
|
+
struct params_st params;
|
121
|
+
|
122
|
+
if (bucket->handle == NULL) {
|
123
|
+
rb_raise(eConnectError, "closed connection");
|
124
|
+
}
|
125
|
+
rb_scan_args(argc, argv, "0*&", &args, &proc);
|
126
|
+
if (!bucket->async && proc != Qnil) {
|
127
|
+
rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
|
128
|
+
}
|
129
|
+
rb_funcall(args, id_flatten_bang, 0);
|
130
|
+
memset(¶ms, 0, sizeof(struct params_st));
|
131
|
+
params.type = cmd_unlock;
|
132
|
+
params.bucket = bucket;
|
133
|
+
cb_params_build(¶ms, RARRAY_LEN(args), args);
|
134
|
+
ctx = xcalloc(1, sizeof(struct context_st));
|
135
|
+
if (ctx == NULL) {
|
136
|
+
rb_raise(eClientNoMemoryError, "failed to allocate memory for context");
|
137
|
+
}
|
138
|
+
ctx->proc = cb_gc_protect(bucket, proc);
|
139
|
+
ctx->bucket = bucket;
|
140
|
+
rv = rb_hash_new();
|
141
|
+
ctx->rv = &rv;
|
142
|
+
ctx->exception = Qnil;
|
143
|
+
ctx->quiet = params.cmd.unlock.quiet;
|
144
|
+
ctx->nqueries = params.cmd.unlock.num;
|
145
|
+
err = lcb_unlock(bucket->handle, (const void *)ctx,
|
146
|
+
params.cmd.unlock.num, params.cmd.unlock.ptr);
|
147
|
+
cb_params_destroy(¶ms);
|
148
|
+
exc = cb_check_error(err, "failed to schedule unlock request", Qnil);
|
149
|
+
if (exc != Qnil) {
|
150
|
+
xfree(ctx);
|
151
|
+
rb_exc_raise(exc);
|
152
|
+
}
|
153
|
+
bucket->nbytes += params.npayload;
|
154
|
+
if (bucket->async) {
|
155
|
+
maybe_do_loop(bucket);
|
156
|
+
return Qnil;
|
157
|
+
} else {
|
158
|
+
if (ctx->nqueries > 0) {
|
159
|
+
/* we have some operations pending */
|
160
|
+
lcb_wait(bucket->handle);
|
161
|
+
}
|
162
|
+
exc = ctx->exception;
|
163
|
+
xfree(ctx);
|
164
|
+
if (exc != Qnil) {
|
165
|
+
rb_exc_raise(cb_gc_unprotect(bucket, exc));
|
166
|
+
}
|
167
|
+
if (bucket->exception != Qnil) {
|
168
|
+
rb_exc_raise(bucket->exception);
|
169
|
+
}
|
170
|
+
if (params.cmd.unlock.num > 1) {
|
171
|
+
return rv; /* return as a hash {key => true, ...} */
|
172
|
+
} else {
|
173
|
+
VALUE vv = Qnil;
|
174
|
+
rb_hash_foreach(rv, cb_first_value_i, (VALUE)&vv);
|
175
|
+
return vv;
|
176
|
+
}
|
177
|
+
}
|
178
|
+
}
|
179
|
+
|
180
|
+
|