couchbase 1.3.4-x64-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.
- 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,142 @@
|
|
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_version_callback(lcb_t handle, const void *cookie, lcb_error_t error, const lcb_server_version_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 node, val, exc, 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 get version", node);
|
29
|
+
if (exc != Qnil) {
|
30
|
+
rb_ivar_set(exc, cb_id_iv_operation, cb_sym_version);
|
31
|
+
ctx->exception = exc;
|
32
|
+
}
|
33
|
+
|
34
|
+
if (node != Qnil) {
|
35
|
+
val = STR_NEW((const char*)resp->v.v0.vstring, resp->v.v0.nvstring);
|
36
|
+
if (bucket->async) { /* asynchronous */
|
37
|
+
if (ctx->proc != Qnil) {
|
38
|
+
res = rb_class_new_instance(0, NULL, cb_cResult);
|
39
|
+
rb_ivar_set(res, cb_id_iv_error, exc);
|
40
|
+
rb_ivar_set(res, cb_id_iv_operation, cb_sym_version);
|
41
|
+
rb_ivar_set(res, cb_id_iv_node, node);
|
42
|
+
rb_ivar_set(res, cb_id_iv_value, val);
|
43
|
+
cb_proc_call(bucket, ctx->proc, 1, res);
|
44
|
+
}
|
45
|
+
} else { /* synchronous */
|
46
|
+
if (NIL_P(exc)) {
|
47
|
+
rb_hash_aset(ctx->rv, node, val);
|
48
|
+
}
|
49
|
+
}
|
50
|
+
} else {
|
51
|
+
ctx->nqueries--;
|
52
|
+
ctx->proc = Qnil;
|
53
|
+
if (bucket->async) {
|
54
|
+
cb_context_free(ctx);
|
55
|
+
}
|
56
|
+
}
|
57
|
+
|
58
|
+
(void)handle;
|
59
|
+
}
|
60
|
+
|
61
|
+
/*
|
62
|
+
* Returns versions of the server for each node in the cluster
|
63
|
+
*
|
64
|
+
* @since 1.1.0
|
65
|
+
*
|
66
|
+
* @overload version
|
67
|
+
* @yieldparam [Result] ret the object with +error+, +node+, +operation+
|
68
|
+
* and +value+ attributes.
|
69
|
+
*
|
70
|
+
* @return [Hash] node-version pairs
|
71
|
+
*
|
72
|
+
* @raise [Couchbase::Error::Connect] if connection closed (see {Bucket#reconnect})
|
73
|
+
* @raise [ArgumentError] when passing the block in synchronous mode
|
74
|
+
*
|
75
|
+
* @example Synchronous version request
|
76
|
+
* c.version #=> will render version
|
77
|
+
*
|
78
|
+
* @example Asynchronous version request
|
79
|
+
* c.run do
|
80
|
+
* c.version do |ret|
|
81
|
+
* ret.operation #=> :version
|
82
|
+
* ret.success? #=> true
|
83
|
+
* ret.node #=> "localhost:11211"
|
84
|
+
* ret.value #=> will render version
|
85
|
+
* end
|
86
|
+
* end
|
87
|
+
*/
|
88
|
+
VALUE
|
89
|
+
cb_bucket_version(int argc, VALUE *argv, VALUE self)
|
90
|
+
{
|
91
|
+
struct cb_bucket_st *bucket = DATA_PTR(self);
|
92
|
+
struct cb_context_st *ctx;
|
93
|
+
VALUE rv, exc, proc;
|
94
|
+
lcb_error_t err;
|
95
|
+
struct cb_params_st params;
|
96
|
+
|
97
|
+
if (!cb_bucket_connected_bang(bucket, cb_sym_version)) {
|
98
|
+
return Qnil;
|
99
|
+
}
|
100
|
+
|
101
|
+
memset(¶ms, 0, sizeof(struct cb_params_st));
|
102
|
+
rb_scan_args(argc, argv, "0*&", ¶ms.args, &proc);
|
103
|
+
if (!bucket->async && proc != Qnil) {
|
104
|
+
rb_raise(rb_eArgError, "synchronous mode doesn't support callbacks");
|
105
|
+
}
|
106
|
+
params.type = cb_cmd_version;
|
107
|
+
params.bucket = bucket;
|
108
|
+
cb_params_build(¶ms);
|
109
|
+
ctx = cb_context_alloc_common(bucket, proc, params.cmd.version.num);
|
110
|
+
err = lcb_server_versions(bucket->handle, (const void *)ctx,
|
111
|
+
params.cmd.version.num, params.cmd.version.ptr);
|
112
|
+
exc = cb_check_error(err, "failed to schedule version request", Qnil);
|
113
|
+
cb_params_destroy(¶ms);
|
114
|
+
if (exc != Qnil) {
|
115
|
+
cb_context_free(ctx);
|
116
|
+
rb_exc_raise(exc);
|
117
|
+
}
|
118
|
+
bucket->nbytes += params.npayload;
|
119
|
+
if (bucket->async) {
|
120
|
+
cb_maybe_do_loop(bucket);
|
121
|
+
return Qnil;
|
122
|
+
} else {
|
123
|
+
if (ctx->nqueries > 0) {
|
124
|
+
/* we have some operations pending */
|
125
|
+
lcb_wait(bucket->handle);
|
126
|
+
}
|
127
|
+
exc = ctx->exception;
|
128
|
+
rv = ctx->rv;
|
129
|
+
cb_context_free(ctx);
|
130
|
+
if (exc != Qnil) {
|
131
|
+
rb_exc_raise(exc);
|
132
|
+
}
|
133
|
+
exc = bucket->exception;
|
134
|
+
if (exc != Qnil) {
|
135
|
+
bucket->exception = Qnil;
|
136
|
+
rb_exc_raise(exc);
|
137
|
+
}
|
138
|
+
return rv;
|
139
|
+
}
|
140
|
+
}
|
141
|
+
|
142
|
+
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'active_support/cache'
|
2
|
+
require 'action_dispatch/middleware/session/abstract_store'
|
3
|
+
require 'rack/session/couchbase'
|
4
|
+
require 'couchbase'
|
5
|
+
|
6
|
+
module ActionDispatch
|
7
|
+
module Session
|
8
|
+
|
9
|
+
# This is Couchbase-powered session store for Rails applications
|
10
|
+
#
|
11
|
+
# To use it just update your `config/initializers/session_store.rb` file
|
12
|
+
#
|
13
|
+
# require 'action_dispatch/middleware/session/couchbase_store'
|
14
|
+
# AppName::Application.config.session_store :couchbase_store
|
15
|
+
#
|
16
|
+
# Or remove this file and add following line to your `config/application.rb`:
|
17
|
+
#
|
18
|
+
# require 'action_dispatch/middleware/session/couchbase_store'
|
19
|
+
# config.session_store :couchbase_store
|
20
|
+
#
|
21
|
+
# You can also pass additional options:
|
22
|
+
#
|
23
|
+
# require 'action_dispatch/middleware/session/couchbase_store'
|
24
|
+
# session_options = {
|
25
|
+
# :expire_after => 5.minutes,
|
26
|
+
# :couchbase => {:bucket => "sessions", :default_format => :marshal}
|
27
|
+
# }
|
28
|
+
# config.session_store :couchbase_store, session_options
|
29
|
+
#
|
30
|
+
# By default sessions will be serialized to JSON, to allow analyse them
|
31
|
+
# using Map/Reduce.
|
32
|
+
#
|
33
|
+
class CouchbaseStore < Rack::Session::Couchbase
|
34
|
+
include Compatibility
|
35
|
+
include StaleSessionCheck
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,430 @@
|
|
1
|
+
# Author:: Couchbase <info@couchbase.com>
|
2
|
+
# Copyright:: 2011, 2012 Couchbase, Inc.
|
3
|
+
# License:: Apache License, Version 2.0
|
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
|
+
require 'couchbase'
|
19
|
+
require 'securerandom'
|
20
|
+
require 'active_support/core_ext/array/extract_options'
|
21
|
+
require 'active_support/cache'
|
22
|
+
require 'monitor'
|
23
|
+
|
24
|
+
module ActiveSupport
|
25
|
+
module Cache
|
26
|
+
# This class implements Cache interface for Rails. To use it just
|
27
|
+
# put following line in your config/application.rb file:
|
28
|
+
#
|
29
|
+
# config.cache_store = :couchbase_store
|
30
|
+
#
|
31
|
+
# You can also pass additional connection options there
|
32
|
+
#
|
33
|
+
# cache_options = {
|
34
|
+
# :bucket => 'protected',
|
35
|
+
# :username => 'protected',
|
36
|
+
# :password => 'secret',
|
37
|
+
# :expires_in => 30.seconds
|
38
|
+
# }
|
39
|
+
# config.cache_store = :couchbase_store, cache_options
|
40
|
+
class CouchbaseStore < Store
|
41
|
+
|
42
|
+
# Creates a new CouchbaseStore object, with the given options. For
|
43
|
+
# more info see {{Couchbase::Bucket#initialize}}
|
44
|
+
#
|
45
|
+
# ActiveSupport::Cache::CouchbaseStore.new(:bucket => "cache")
|
46
|
+
#
|
47
|
+
# If no options are specified, then CouchbaseStore will connect to
|
48
|
+
# localhost port 8091 (default Couchbase Server port) and will use
|
49
|
+
# bucket named "default" which is always open for unauthorized access
|
50
|
+
# (if exists).
|
51
|
+
def initialize(*args)
|
52
|
+
args = [*(args.flatten)]
|
53
|
+
options = args.extract_options! || {}
|
54
|
+
@raise_errors = !options[:quiet] = !options.delete(:raise_errors)
|
55
|
+
options[:default_ttl] ||= options.delete(:expires_in)
|
56
|
+
options[:default_format] ||= :marshal
|
57
|
+
options[:key_prefix] ||= options.delete(:namespace)
|
58
|
+
@key_prefix = options[:key_prefix]
|
59
|
+
options[:connection_pool] ||= options.delete(:connection_pool)
|
60
|
+
args.push(options)
|
61
|
+
|
62
|
+
if options[:connection_pool]
|
63
|
+
if RUBY_VERSION.to_f < 1.9
|
64
|
+
warn "connection_pool gem doesn't support ruby < 1.9"
|
65
|
+
else
|
66
|
+
@data = ::Couchbase::ConnectionPool.new(options[:connection_pool], *args)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
unless @data
|
70
|
+
@data = ::Couchbase::Bucket.new(*args)
|
71
|
+
@data.extend(Threadsafe)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Fetches data from the cache, using the given key.
|
76
|
+
#
|
77
|
+
# @since 1.2.0.dp5
|
78
|
+
#
|
79
|
+
# If there is data in the cache with the given key, then that data is
|
80
|
+
# returned. If there is no such data in the cache (a cache miss),
|
81
|
+
# then nil will be returned. However, if a block has been passed, that
|
82
|
+
# block will be run in the event of a cache miss. The return value of
|
83
|
+
# the block will be written to the cache under the given cache key,
|
84
|
+
# and that return value will be returned.
|
85
|
+
#
|
86
|
+
# @param [String] name name for the key
|
87
|
+
# @param [Hash] options
|
88
|
+
# @option options [true, false] :force if this option is +true+ it
|
89
|
+
# will force cache miss.
|
90
|
+
# @option options [Fixnum] :expires_in the expiration time on the
|
91
|
+
# cache in seconds. Values larger than 30*24*60*60 seconds (30 days)
|
92
|
+
# are interpreted as absolute times (from the epoch).
|
93
|
+
# @option options [true, false] :unless_exists if this option is +true+
|
94
|
+
# it will write value only if the key doesn't exist in the database
|
95
|
+
# (it accepts +:unless_exist+ too).
|
96
|
+
#
|
97
|
+
# @return [Object]
|
98
|
+
def fetch(name, options = nil)
|
99
|
+
options ||= {}
|
100
|
+
name = expanded_key(name)
|
101
|
+
|
102
|
+
if block_given?
|
103
|
+
unless options[:force]
|
104
|
+
entry = instrument(:read, name, options) do |payload|
|
105
|
+
payload[:super_operation] = :fetch if payload
|
106
|
+
read_entry(name, options)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
if !entry.nil?
|
111
|
+
instrument(:fetch_hit, name, options) { |payload| }
|
112
|
+
entry
|
113
|
+
else
|
114
|
+
result = instrument(:generate, name, options) do |payload|
|
115
|
+
yield
|
116
|
+
end
|
117
|
+
write(name, result, options)
|
118
|
+
result
|
119
|
+
end
|
120
|
+
else
|
121
|
+
read(name, options)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# Writes the value to the cache, with the key
|
126
|
+
#
|
127
|
+
# @since 1.2.0.dp5
|
128
|
+
#
|
129
|
+
# @param [String] name name for the key
|
130
|
+
# @param [Object] value value of the key
|
131
|
+
# @param [Hash] options
|
132
|
+
# @option options [Fixnum] :expires_in the expiration time on the
|
133
|
+
# cache in seconds. Values larger than 30*24*60*60 seconds (30 days)
|
134
|
+
# are interpreted as absolute times (from the epoch).
|
135
|
+
#
|
136
|
+
# @return [Fixnum, false] false in case of failure and CAS value
|
137
|
+
# otherwise (it could be used as true value)
|
138
|
+
def write(name, value, options = nil)
|
139
|
+
options ||= {}
|
140
|
+
name = expanded_key name
|
141
|
+
if options.delete(:raw)
|
142
|
+
options[:format] = :plain
|
143
|
+
value = value.to_s
|
144
|
+
value.force_encoding(Encoding::BINARY) if defined?(Encoding)
|
145
|
+
end
|
146
|
+
|
147
|
+
instrument(:write, name, options) do |payload|
|
148
|
+
write_entry(name, value, options)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# Fetches data from the cache, using the given key.
|
153
|
+
#
|
154
|
+
# @since 1.2.0.dp5
|
155
|
+
#
|
156
|
+
# If there is data in the cache with the given key, then that data is
|
157
|
+
# returned. Otherwise, nil is returned.
|
158
|
+
#
|
159
|
+
# @param [String] name name for the key
|
160
|
+
# @param [Hash] options
|
161
|
+
# @option options [Fixnum] :expires_in the expiration time on the
|
162
|
+
# cache in seconds. Values larger than 30*24*60*60 seconds (30 days)
|
163
|
+
# are interpreted as absolute times (from the epoch).
|
164
|
+
# @option options [true, false] :raw do not marshal the value if this
|
165
|
+
# option is +true+
|
166
|
+
#
|
167
|
+
# @return [Object]
|
168
|
+
def read(name, options = nil)
|
169
|
+
options ||= {}
|
170
|
+
name = expanded_key name
|
171
|
+
if options.delete(:raw)
|
172
|
+
options[:format] = :plain
|
173
|
+
end
|
174
|
+
|
175
|
+
instrument(:read, name, options) do |payload|
|
176
|
+
entry = read_entry(name, options)
|
177
|
+
payload[:hit] = !!entry if payload
|
178
|
+
entry
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
# Read multiple values at once from the cache.
|
183
|
+
#
|
184
|
+
# @since 1.2.0.dp5
|
185
|
+
#
|
186
|
+
# Options can be passed in the last argument.
|
187
|
+
#
|
188
|
+
# Returns a hash mapping the names provided to the values found.
|
189
|
+
#
|
190
|
+
# @return [Hash] key-value pairs
|
191
|
+
def read_multi(*names)
|
192
|
+
options = names.extract_options!
|
193
|
+
names = names.flatten.map{|name| expanded_key(name)}
|
194
|
+
options[:assemble_hash] = true
|
195
|
+
if options.delete(:raw)
|
196
|
+
options[:format] = :plain
|
197
|
+
end
|
198
|
+
instrument(:read_multi, names, options) do
|
199
|
+
@data.get(names, options)
|
200
|
+
end
|
201
|
+
rescue ::Couchbase::Error::Base => e
|
202
|
+
logger.error("#{e.class}: #{e.message}") if logger
|
203
|
+
raise if @raise_errors
|
204
|
+
false
|
205
|
+
end
|
206
|
+
|
207
|
+
# Return true if the cache contains an entry for the given key.
|
208
|
+
#
|
209
|
+
# @since 1.2.0.dp5
|
210
|
+
#
|
211
|
+
# @return [true, false]
|
212
|
+
def exists?(name, options = nil)
|
213
|
+
options ||= {}
|
214
|
+
name = expanded_key name
|
215
|
+
|
216
|
+
instrument(:exists?, name) do
|
217
|
+
!read_entry(name, options).nil?
|
218
|
+
end
|
219
|
+
end
|
220
|
+
alias :exist? :exists?
|
221
|
+
|
222
|
+
# Deletes an entry in the cache.
|
223
|
+
#
|
224
|
+
# @since 1.2.0.dp5
|
225
|
+
#
|
226
|
+
# @return [true, false] true if an entry is deleted
|
227
|
+
def delete(name, options = nil)
|
228
|
+
options ||= {}
|
229
|
+
name = expanded_key name
|
230
|
+
|
231
|
+
instrument(:delete, name) do
|
232
|
+
delete_entry(name, options)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
# Increment an integer value in the cache.
|
237
|
+
#
|
238
|
+
# @since 1.2.0.dp5
|
239
|
+
#
|
240
|
+
# @param [String] name name for the key
|
241
|
+
# @param [Fixnum] amount (1) the delta value
|
242
|
+
# @param [Hash] options
|
243
|
+
# @option options [Fixnum] :expires_in the expiration time on the
|
244
|
+
# cache in seconds. Values larger than 30*24*60*60 seconds (30 days)
|
245
|
+
# are interpreted as absolute times (from the epoch).
|
246
|
+
# @option options [Fixnum] :initial (1) this option allows to initialize
|
247
|
+
# the value if the key is missing in the cache
|
248
|
+
#
|
249
|
+
# @return [Fixnum] new value
|
250
|
+
def increment(name, amount = 1, options = nil)
|
251
|
+
options ||= {}
|
252
|
+
name = expanded_key name
|
253
|
+
|
254
|
+
if ttl = options.delete(:expires_in)
|
255
|
+
options[:ttl] ||= ttl
|
256
|
+
end
|
257
|
+
options[:create] = true
|
258
|
+
instrument(:increment, name, options) do |payload|
|
259
|
+
payload[:amount] = amount if payload
|
260
|
+
@data.incr(name, amount, options)
|
261
|
+
end
|
262
|
+
rescue ::Couchbase::Error::Base => e
|
263
|
+
logger.error("#{e.class}: #{e.message}") if logger
|
264
|
+
raise if @raise_errors
|
265
|
+
false
|
266
|
+
end
|
267
|
+
|
268
|
+
# Decrement an integer value in the cache.
|
269
|
+
#
|
270
|
+
# @since 1.2.0.dp5
|
271
|
+
#
|
272
|
+
# @param [String] name name for the key
|
273
|
+
# @param [Fixnum] amount (1) the delta value
|
274
|
+
# @param [Hash] options
|
275
|
+
# @option options [Fixnum] :expires_in the expiration time on the
|
276
|
+
# cache in seconds. Values larger than 30*24*60*60 seconds (30 days)
|
277
|
+
# are interpreted as absolute times (from the epoch).
|
278
|
+
# @option options [Fixnum] :initial this option allows to initialize
|
279
|
+
# the value if the key is missing in the cache
|
280
|
+
#
|
281
|
+
# @return [Fixnum] new value
|
282
|
+
def decrement(name, amount = 1, options = nil)
|
283
|
+
options ||= {}
|
284
|
+
name = expanded_key name
|
285
|
+
|
286
|
+
if ttl = options.delete(:expires_in)
|
287
|
+
options[:ttl] ||= ttl
|
288
|
+
end
|
289
|
+
options[:create] = true
|
290
|
+
instrument(:decrement, name, options) do |payload|
|
291
|
+
payload[:amount] = amount if payload
|
292
|
+
@data.decr(name, amount, options)
|
293
|
+
end
|
294
|
+
rescue ::Couchbase::Error::Base => e
|
295
|
+
logger.error("#{e.class}: #{e.message}") if logger
|
296
|
+
raise if @raise_errors
|
297
|
+
false
|
298
|
+
end
|
299
|
+
|
300
|
+
# Get the statistics from the memcached servers.
|
301
|
+
#
|
302
|
+
# @since 1.2.0.dp5
|
303
|
+
#
|
304
|
+
# @return [Hash]
|
305
|
+
def stats(*arg)
|
306
|
+
@data.stats(*arg)
|
307
|
+
end
|
308
|
+
|
309
|
+
protected
|
310
|
+
|
311
|
+
# Read an entry from the cache.
|
312
|
+
def read_entry(key, options) # :nodoc:
|
313
|
+
@data.get(key, options)
|
314
|
+
rescue ::Couchbase::Error::Base => e
|
315
|
+
logger.error("#{e.class}: #{e.message}") if logger
|
316
|
+
raise if @raise_errors
|
317
|
+
nil
|
318
|
+
end
|
319
|
+
|
320
|
+
# Write an entry to the cache.
|
321
|
+
def write_entry(key, value, options) # :nodoc:
|
322
|
+
method = if options[:unless_exists] || options[:unless_exist]
|
323
|
+
:add
|
324
|
+
else
|
325
|
+
:set
|
326
|
+
end
|
327
|
+
if ttl = options.delete(:expires_in)
|
328
|
+
options[:ttl] ||= ttl
|
329
|
+
end
|
330
|
+
@data.send(method, key, value, options)
|
331
|
+
rescue ::Couchbase::Error::Base => e
|
332
|
+
logger.error("#{e.class}: #{e.message}") if logger
|
333
|
+
raise if @raise_errors
|
334
|
+
false
|
335
|
+
end
|
336
|
+
|
337
|
+
# Delete an entry from the cache.
|
338
|
+
def delete_entry(key, options) # :nodoc:
|
339
|
+
@data.delete(key, options)
|
340
|
+
rescue ::Couchbase::Error::Base => e
|
341
|
+
logger.error("#{e.class}: #{e.message}") if logger
|
342
|
+
raise if @raise_errors
|
343
|
+
false
|
344
|
+
end
|
345
|
+
|
346
|
+
private
|
347
|
+
|
348
|
+
# Expand key to be a consistent string value. Invoke +cache_key+ if
|
349
|
+
# object responds to +cache_key+. Otherwise, to_param method will be
|
350
|
+
# called. If the key is a Hash, then keys will be sorted alphabetically.
|
351
|
+
def expanded_key(key) # :nodoc:
|
352
|
+
return validate_key(key.cache_key.to_s) if key.respond_to?(:cache_key)
|
353
|
+
|
354
|
+
case key
|
355
|
+
when Array
|
356
|
+
if key.size > 1
|
357
|
+
key = key.collect{|element| expanded_key(element)}
|
358
|
+
else
|
359
|
+
key = key.first
|
360
|
+
end
|
361
|
+
when Hash
|
362
|
+
key = key.sort_by { |k,_| k.to_s }.collect{|k,v| "#{k}=#{v}"}
|
363
|
+
end
|
364
|
+
|
365
|
+
validate_key(key.respond_to?(:to_param) ? key.to_param : key)
|
366
|
+
end
|
367
|
+
|
368
|
+
def validate_key(key)
|
369
|
+
if key_with_prefix(key).length > 250
|
370
|
+
key = "#{key[0, max_length_before_prefix]}:md5:#{Digest::MD5.hexdigest(key)}"
|
371
|
+
end
|
372
|
+
return key
|
373
|
+
end
|
374
|
+
|
375
|
+
def key_with_prefix(key)
|
376
|
+
(ns = @key_prefix) ? "#{ns}#{key}" : key
|
377
|
+
end
|
378
|
+
|
379
|
+
def max_length_before_prefix
|
380
|
+
@max_length_before_prefix ||= 212 - (@key_prefix || '').size
|
381
|
+
end
|
382
|
+
|
383
|
+
module Threadsafe
|
384
|
+
def self.extended(obj)
|
385
|
+
obj.init_threadsafe
|
386
|
+
end
|
387
|
+
|
388
|
+
def get(*)
|
389
|
+
@lock.synchronize do
|
390
|
+
super
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
394
|
+
def send(*)
|
395
|
+
@lock.synchronize do
|
396
|
+
super
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
def delete(*)
|
401
|
+
@lock.synchronize do
|
402
|
+
super
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
def incr(*)
|
407
|
+
@lock.synchronize do
|
408
|
+
super
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
def decr(*)
|
413
|
+
@lock.synchronize do
|
414
|
+
super
|
415
|
+
end
|
416
|
+
end
|
417
|
+
|
418
|
+
def stats(*)
|
419
|
+
@lock.synchronize do
|
420
|
+
super
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
424
|
+
def init_threadsafe
|
425
|
+
@lock = Monitor.new
|
426
|
+
end
|
427
|
+
end
|
428
|
+
end
|
429
|
+
end
|
430
|
+
end
|