couchbase 1.3.4-x64-mingw32
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.travis.yml +22 -0
- data/.yardopts +5 -0
- data/CONTRIBUTING.markdown +75 -0
- data/Gemfile +4 -0
- data/LICENSE +201 -0
- data/Makefile +3 -0
- data/README.markdown +649 -0
- data/RELEASE_NOTES.markdown +796 -0
- data/Rakefile +20 -0
- data/couchbase.gemspec +49 -0
- data/examples/chat-em/Gemfile +7 -0
- data/examples/chat-em/README.markdown +45 -0
- data/examples/chat-em/server.rb +82 -0
- data/examples/chat-goliath-grape/Gemfile +5 -0
- data/examples/chat-goliath-grape/README.markdown +50 -0
- data/examples/chat-goliath-grape/app.rb +67 -0
- data/examples/chat-goliath-grape/config/app.rb +20 -0
- data/examples/transcoders/Gemfile +3 -0
- data/examples/transcoders/README.markdown +59 -0
- data/examples/transcoders/cb-zcat +40 -0
- data/examples/transcoders/cb-zcp +45 -0
- data/examples/transcoders/gzip_transcoder.rb +49 -0
- data/examples/transcoders/options.rb +54 -0
- data/ext/couchbase_ext/.gitignore +4 -0
- data/ext/couchbase_ext/arguments.c +956 -0
- data/ext/couchbase_ext/arithmetic.c +307 -0
- data/ext/couchbase_ext/bucket.c +1370 -0
- data/ext/couchbase_ext/context.c +65 -0
- data/ext/couchbase_ext/couchbase_ext.c +1364 -0
- data/ext/couchbase_ext/couchbase_ext.h +644 -0
- data/ext/couchbase_ext/delete.c +163 -0
- data/ext/couchbase_ext/eventmachine_plugin.c +452 -0
- data/ext/couchbase_ext/extconf.rb +168 -0
- data/ext/couchbase_ext/get.c +316 -0
- data/ext/couchbase_ext/gethrtime.c +129 -0
- data/ext/couchbase_ext/http.c +432 -0
- data/ext/couchbase_ext/multithread_plugin.c +1090 -0
- data/ext/couchbase_ext/observe.c +171 -0
- data/ext/couchbase_ext/plugin_common.c +171 -0
- data/ext/couchbase_ext/result.c +129 -0
- data/ext/couchbase_ext/stats.c +163 -0
- data/ext/couchbase_ext/store.c +542 -0
- data/ext/couchbase_ext/timer.c +192 -0
- data/ext/couchbase_ext/touch.c +186 -0
- data/ext/couchbase_ext/unlock.c +176 -0
- data/ext/couchbase_ext/utils.c +551 -0
- data/ext/couchbase_ext/version.c +142 -0
- data/lib/action_dispatch/middleware/session/couchbase_store.rb +38 -0
- data/lib/active_support/cache/couchbase_store.rb +430 -0
- data/lib/couchbase.rb +155 -0
- data/lib/couchbase/bucket.rb +457 -0
- data/lib/couchbase/cluster.rb +119 -0
- data/lib/couchbase/connection_pool.rb +58 -0
- data/lib/couchbase/constants.rb +12 -0
- data/lib/couchbase/result.rb +26 -0
- data/lib/couchbase/transcoder.rb +120 -0
- data/lib/couchbase/utils.rb +62 -0
- data/lib/couchbase/version.rb +21 -0
- data/lib/couchbase/view.rb +506 -0
- data/lib/couchbase/view_row.rb +272 -0
- data/lib/ext/multi_json_fix.rb +56 -0
- data/lib/rack/session/couchbase.rb +108 -0
- data/tasks/benchmark.rake +6 -0
- data/tasks/compile.rake +158 -0
- data/tasks/test.rake +100 -0
- data/tasks/util.rake +21 -0
- data/test/profile/.gitignore +1 -0
- data/test/profile/Gemfile +6 -0
- data/test/profile/benchmark.rb +195 -0
- data/test/setup.rb +178 -0
- data/test/test_arithmetic.rb +185 -0
- data/test/test_async.rb +316 -0
- data/test/test_bucket.rb +250 -0
- data/test/test_cas.rb +235 -0
- data/test/test_couchbase.rb +77 -0
- data/test/test_couchbase_connection_pool.rb +77 -0
- data/test/test_couchbase_rails_cache_store.rb +361 -0
- data/test/test_delete.rb +120 -0
- data/test/test_errors.rb +82 -0
- data/test/test_eventmachine.rb +70 -0
- data/test/test_format.rb +164 -0
- data/test/test_get.rb +407 -0
- data/test/test_stats.rb +57 -0
- data/test/test_store.rb +216 -0
- data/test/test_timer.rb +42 -0
- data/test/test_touch.rb +97 -0
- data/test/test_unlock.rb +119 -0
- data/test/test_utils.rb +58 -0
- data/test/test_version.rb +52 -0
- metadata +336 -0
@@ -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 cb_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 cb_timer_st *timer;
|
40
|
+
|
41
|
+
/* allocate new bucket struct and set it to zero */
|
42
|
+
obj = Data_Make_Struct(klass, struct cb_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 cb_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 cb_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 cb_timer_st *tm = DATA_PTR(timer);
|
109
|
+
return cb_proc_call(tm->bucket, 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 cb_timer_st *tm = (struct cb_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 cb_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) != cb_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, cb_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,186 @@
|
|
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_touch_callback(lcb_t handle, const void *cookie, lcb_error_t error, const lcb_touch_resp_t *resp)
|
22
|
+
{
|
23
|
+
struct cb_context_st *ctx = (struct cb_context_st *)cookie;
|
24
|
+
struct cb_bucket_st *bucket = ctx->bucket;
|
25
|
+
VALUE key, exc = Qnil, res;
|
26
|
+
|
27
|
+
ctx->nqueries--;
|
28
|
+
key = STR_NEW((const char*)resp->v.v0.key, resp->v.v0.nkey);
|
29
|
+
cb_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, cb_id_iv_operation, cb_sym_touch);
|
35
|
+
ctx->exception = exc;
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
if (bucket->async) { /* asynchronous */
|
40
|
+
if (ctx->proc != Qnil) {
|
41
|
+
res = rb_class_new_instance(0, NULL, cb_cResult);
|
42
|
+
rb_ivar_set(res, cb_id_iv_error, exc);
|
43
|
+
rb_ivar_set(res, cb_id_iv_operation, cb_sym_touch);
|
44
|
+
rb_ivar_set(res, cb_id_iv_key, key);
|
45
|
+
cb_proc_call(bucket, ctx->proc, 1, res);
|
46
|
+
}
|
47
|
+
} else { /* synchronous */
|
48
|
+
rb_hash_aset(ctx->rv, key, (error == LCB_SUCCESS) ? Qtrue : Qfalse);
|
49
|
+
}
|
50
|
+
if (ctx->nqueries == 0) {
|
51
|
+
ctx->proc = Qnil;
|
52
|
+
if (bucket->async) {
|
53
|
+
cb_context_free(ctx);
|
54
|
+
}
|
55
|
+
}
|
56
|
+
(void)handle;
|
57
|
+
}
|
58
|
+
|
59
|
+
/*
|
60
|
+
* Update the expiry time of an item
|
61
|
+
*
|
62
|
+
* @since 1.0.0
|
63
|
+
*
|
64
|
+
* The +touch+ method allow you to update the expiration time on a given
|
65
|
+
* key. This can be useful for situations where you want to prevent an item
|
66
|
+
* from expiring without resetting the associated value. For example, for a
|
67
|
+
* session database you might want to keep the session alive in the database
|
68
|
+
* each time the user accesses a web page without explicitly updating the
|
69
|
+
* session value, keeping the user's session active and available.
|
70
|
+
*
|
71
|
+
* @overload touch(key, options = {})
|
72
|
+
* @param key [String, Symbol] Key used to reference the value.
|
73
|
+
* @param options [Hash] Options for operation.
|
74
|
+
* @option options [Fixnum] :ttl (self.default_ttl) Expiry time for key.
|
75
|
+
* Values larger than 30*24*60*60 seconds (30 days) are interpreted as
|
76
|
+
* absolute times (from the epoch).
|
77
|
+
* @option options [true, false] :quiet (self.quiet) If set to +true+, the
|
78
|
+
* operation won't raise error for missing key, it will return +nil+.
|
79
|
+
*
|
80
|
+
* @yieldparam ret [Result] the result of operation in asynchronous mode
|
81
|
+
* (valid attributes: +error+, +operation+, +key+).
|
82
|
+
*
|
83
|
+
* @return [true, false] +true+ if the operation was successful and +false+
|
84
|
+
* otherwise.
|
85
|
+
*
|
86
|
+
* @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
|
87
|
+
*
|
88
|
+
* @raise [ArgumentError] when passing the block in synchronous mode
|
89
|
+
*
|
90
|
+
* @example Touch value using +default_ttl+
|
91
|
+
* c.touch("foo")
|
92
|
+
*
|
93
|
+
* @example Touch value using custom TTL (10 seconds)
|
94
|
+
* c.touch("foo", :ttl => 10)
|
95
|
+
*
|
96
|
+
* @overload touch(keys)
|
97
|
+
* @param keys [Hash] The Hash where keys represent the keys in the
|
98
|
+
* database, values -- the expiry times for corresponding key. See
|
99
|
+
* description of +:ttl+ argument above for more information about TTL
|
100
|
+
* values.
|
101
|
+
*
|
102
|
+
* @yieldparam ret [Result] the result of operation for each key in
|
103
|
+
* asynchronous mode (valid attributes: +error+, +operation+, +key+).
|
104
|
+
*
|
105
|
+
* @return [Hash] Mapping keys to result of touch operation (+true+ if the
|
106
|
+
* operation was successful and +false+ otherwise)
|
107
|
+
*
|
108
|
+
* @example Touch several values
|
109
|
+
* c.touch("foo" => 10, :bar => 20) #=> {"foo" => true, "bar" => true}
|
110
|
+
*
|
111
|
+
* @example Touch several values in async mode
|
112
|
+
* c.run do
|
113
|
+
* c.touch("foo" => 10, :bar => 20) do |ret|
|
114
|
+
* ret.operation #=> :touch
|
115
|
+
* ret.success? #=> true
|
116
|
+
* ret.key #=> "foo" and "bar" in separate calls
|
117
|
+
* end
|
118
|
+
* end
|
119
|
+
*
|
120
|
+
* @example Touch single value
|
121
|
+
* c.touch("foo" => 10) #=> true
|
122
|
+
*
|
123
|
+
*/
|
124
|
+
VALUE
|
125
|
+
cb_bucket_touch(int argc, VALUE *argv, VALUE self)
|
126
|
+
{
|
127
|
+
struct cb_bucket_st *bucket = DATA_PTR(self);
|
128
|
+
struct cb_context_st *ctx;
|
129
|
+
VALUE rv, proc, exc;
|
130
|
+
lcb_error_t err;
|
131
|
+
struct cb_params_st params;
|
132
|
+
|
133
|
+
if (!cb_bucket_connected_bang(bucket, cb_sym_touch)) {
|
134
|
+
return Qnil;
|
135
|
+
}
|
136
|
+
|
137
|
+
memset(¶ms, 0, sizeof(struct cb_params_st));
|
138
|
+
rb_scan_args(argc, argv, "0*&", ¶ms.args, &proc);
|
139
|
+
if (!bucket->async && proc != Qnil) {
|
140
|
+
rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
|
141
|
+
}
|
142
|
+
rb_funcall(params.args, cb_id_flatten_bang, 0);
|
143
|
+
params.type = cb_cmd_touch;
|
144
|
+
params.bucket = bucket;
|
145
|
+
cb_params_build(¶ms);
|
146
|
+
ctx = cb_context_alloc_common(bucket, proc, params.cmd.touch.num);
|
147
|
+
ctx->quiet = params.cmd.touch.quiet;
|
148
|
+
err = lcb_touch(bucket->handle, (const void *)ctx,
|
149
|
+
params.cmd.touch.num, params.cmd.touch.ptr);
|
150
|
+
cb_params_destroy(¶ms);
|
151
|
+
exc = cb_check_error(err, "failed to schedule touch request", Qnil);
|
152
|
+
if (exc != Qnil) {
|
153
|
+
cb_context_free(ctx);
|
154
|
+
rb_exc_raise(exc);
|
155
|
+
}
|
156
|
+
bucket->nbytes += params.npayload;
|
157
|
+
if (bucket->async) {
|
158
|
+
cb_maybe_do_loop(bucket);
|
159
|
+
return Qnil;
|
160
|
+
} else {
|
161
|
+
if (ctx->nqueries > 0) {
|
162
|
+
/* we have some operations pending */
|
163
|
+
lcb_wait(bucket->handle);
|
164
|
+
}
|
165
|
+
exc = ctx->exception;
|
166
|
+
rv = ctx->rv;
|
167
|
+
cb_context_free(ctx);
|
168
|
+
if (exc != Qnil) {
|
169
|
+
rb_exc_raise(exc);
|
170
|
+
}
|
171
|
+
exc = bucket->exception;
|
172
|
+
if (exc != Qnil) {
|
173
|
+
bucket->exception = Qnil;
|
174
|
+
rb_exc_raise(exc);
|
175
|
+
}
|
176
|
+
if (params.cmd.touch.num > 1) {
|
177
|
+
return rv; /* return as a hash {key => true, ...} */
|
178
|
+
} else {
|
179
|
+
VALUE vv = Qnil;
|
180
|
+
rb_hash_foreach(rv, cb_first_value_i, (VALUE)&vv);
|
181
|
+
return vv;
|
182
|
+
}
|
183
|
+
}
|
184
|
+
}
|
185
|
+
|
186
|
+
|
@@ -0,0 +1,176 @@
|
|
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_unlock_callback(lcb_t handle, const void *cookie, lcb_error_t error, const lcb_unlock_resp_t *resp)
|
22
|
+
{
|
23
|
+
struct cb_context_st *ctx = (struct cb_context_st *)cookie;
|
24
|
+
struct cb_bucket_st *bucket = ctx->bucket;
|
25
|
+
VALUE key, exc = Qnil, res;
|
26
|
+
|
27
|
+
ctx->nqueries--;
|
28
|
+
key = STR_NEW((const char*)resp->v.v0.key, resp->v.v0.nkey);
|
29
|
+
cb_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, cb_id_iv_operation, cb_sym_unlock);
|
35
|
+
ctx->exception = exc;
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
if (bucket->async) { /* asynchronous */
|
40
|
+
if (ctx->proc != Qnil) {
|
41
|
+
res = rb_class_new_instance(0, NULL, cb_cResult);
|
42
|
+
rb_ivar_set(res, cb_id_iv_error, exc);
|
43
|
+
rb_ivar_set(res, cb_id_iv_operation, cb_sym_unlock);
|
44
|
+
rb_ivar_set(res, cb_id_iv_key, key);
|
45
|
+
cb_proc_call(bucket, ctx->proc, 1, res);
|
46
|
+
}
|
47
|
+
} else { /* synchronous */
|
48
|
+
rb_hash_aset(ctx->rv, key, (error == LCB_SUCCESS) ? Qtrue : Qfalse);
|
49
|
+
}
|
50
|
+
if (ctx->nqueries == 0) {
|
51
|
+
ctx->proc = Qnil;
|
52
|
+
if (bucket->async) {
|
53
|
+
cb_context_free(ctx);
|
54
|
+
}
|
55
|
+
}
|
56
|
+
(void)handle;
|
57
|
+
}
|
58
|
+
|
59
|
+
/*
|
60
|
+
* Unlock key
|
61
|
+
*
|
62
|
+
* @since 1.2.0
|
63
|
+
*
|
64
|
+
* The +unlock+ method allow you to unlock key once locked by {Bucket#get}
|
65
|
+
* with +:lock+ option.
|
66
|
+
*
|
67
|
+
* @overload unlock(key, options = {})
|
68
|
+
* @param key [String, Symbol] Key used to reference the value.
|
69
|
+
* @param options [Hash] Options for operation.
|
70
|
+
* @option options [Fixnum] :cas The CAS value must match the current one
|
71
|
+
* from the storage.
|
72
|
+
* @option options [true, false] :quiet (self.quiet) If set to +true+, the
|
73
|
+
* operation won't raise error for missing key, it will return +nil+.
|
74
|
+
*
|
75
|
+
* @return [true, false] +true+ if the operation was successful and +false+
|
76
|
+
* otherwise.
|
77
|
+
*
|
78
|
+
* @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
|
79
|
+
*
|
80
|
+
* @raise [ArgumentError] when passing the block in synchronous mode
|
81
|
+
*
|
82
|
+
* @raise [Couchbase::Error::NotFound] if key(s) not found in the storage
|
83
|
+
*
|
84
|
+
* @raise [Couchbase::Error::TemporaryFail] if either the key wasn't
|
85
|
+
* locked or given CAS value doesn't match to actual in the storage
|
86
|
+
*
|
87
|
+
* @example Unlock the single key
|
88
|
+
* val, _, cas = c.get("foo", :lock => true, :extended => true)
|
89
|
+
* c.unlock("foo", :cas => cas)
|
90
|
+
*
|
91
|
+
* @overload unlock(keys)
|
92
|
+
* @param keys [Hash] The Hash where keys represent the keys in the
|
93
|
+
* database, values -- the CAS for corresponding key.
|
94
|
+
*
|
95
|
+
* @yieldparam ret [Result] the result of operation for each key in
|
96
|
+
* asynchronous mode (valid attributes: +error+, +operation+, +key+).
|
97
|
+
*
|
98
|
+
* @return [Hash] Mapping keys to result of unlock operation (+true+ if the
|
99
|
+
* operation was successful and +false+ otherwise)
|
100
|
+
*
|
101
|
+
* @example Unlock several keys
|
102
|
+
* c.unlock("foo" => cas1, :bar => cas2) #=> {"foo" => true, "bar" => true}
|
103
|
+
*
|
104
|
+
* @example Unlock several values in async mode
|
105
|
+
* c.run do
|
106
|
+
* c.unlock("foo" => 10, :bar => 20) do |ret|
|
107
|
+
* ret.operation #=> :unlock
|
108
|
+
* ret.success? #=> true
|
109
|
+
* ret.key #=> "foo" and "bar" in separate calls
|
110
|
+
* end
|
111
|
+
* end
|
112
|
+
*
|
113
|
+
*/
|
114
|
+
VALUE
|
115
|
+
cb_bucket_unlock(int argc, VALUE *argv, VALUE self)
|
116
|
+
{
|
117
|
+
struct cb_bucket_st *bucket = DATA_PTR(self);
|
118
|
+
struct cb_context_st *ctx;
|
119
|
+
VALUE rv, proc, exc;
|
120
|
+
lcb_error_t err;
|
121
|
+
struct cb_params_st params;
|
122
|
+
|
123
|
+
if (!cb_bucket_connected_bang(bucket, cb_sym_unlock)) {
|
124
|
+
return Qnil;
|
125
|
+
}
|
126
|
+
|
127
|
+
memset(¶ms, 0, sizeof(struct cb_params_st));
|
128
|
+
rb_scan_args(argc, argv, "0*&", ¶ms.args, &proc);
|
129
|
+
if (!bucket->async && proc != Qnil) {
|
130
|
+
rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
|
131
|
+
}
|
132
|
+
rb_funcall(params.args, cb_id_flatten_bang, 0);
|
133
|
+
params.type = cb_cmd_unlock;
|
134
|
+
params.bucket = bucket;
|
135
|
+
cb_params_build(¶ms);
|
136
|
+
ctx = cb_context_alloc_common(bucket, proc, params.cmd.unlock.num);
|
137
|
+
ctx->quiet = params.cmd.unlock.quiet;
|
138
|
+
err = lcb_unlock(bucket->handle, (const void *)ctx,
|
139
|
+
params.cmd.unlock.num, params.cmd.unlock.ptr);
|
140
|
+
cb_params_destroy(¶ms);
|
141
|
+
exc = cb_check_error(err, "failed to schedule unlock request", Qnil);
|
142
|
+
if (exc != Qnil) {
|
143
|
+
cb_context_free(ctx);
|
144
|
+
rb_exc_raise(exc);
|
145
|
+
}
|
146
|
+
bucket->nbytes += params.npayload;
|
147
|
+
if (bucket->async) {
|
148
|
+
cb_maybe_do_loop(bucket);
|
149
|
+
return Qnil;
|
150
|
+
} else {
|
151
|
+
if (ctx->nqueries > 0) {
|
152
|
+
/* we have some operations pending */
|
153
|
+
lcb_wait(bucket->handle);
|
154
|
+
}
|
155
|
+
exc = ctx->exception;
|
156
|
+
rv = ctx->rv;
|
157
|
+
cb_context_free(ctx);
|
158
|
+
if (exc != Qnil) {
|
159
|
+
rb_exc_raise(exc);
|
160
|
+
}
|
161
|
+
exc = bucket->exception;
|
162
|
+
if (exc != Qnil) {
|
163
|
+
bucket->exception = Qnil;
|
164
|
+
rb_exc_raise(exc);
|
165
|
+
}
|
166
|
+
if (params.cmd.unlock.num > 1) {
|
167
|
+
return rv; /* return as a hash {key => true, ...} */
|
168
|
+
} else {
|
169
|
+
VALUE vv = Qnil;
|
170
|
+
rb_hash_foreach(rv, cb_first_value_i, (VALUE)&vv);
|
171
|
+
return vv;
|
172
|
+
}
|
173
|
+
}
|
174
|
+
}
|
175
|
+
|
176
|
+
|