couchbase 1.1.5-x86-mingw32 → 1.2.0.beta-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/.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 +102 -12
@@ -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
|
+
|