ngmoco-cache-money 0.2.23 → 0.2.24.2
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/README +30 -34
- data/README.markdown +30 -34
- data/config/environment.rb +0 -8
- data/config/memcached.yml +0 -2
- data/init.rb +1 -1
- data/lib/cache_money.rb +55 -36
- data/lib/cash/adapter/memcache_client.rb +36 -0
- data/lib/cash/adapter/memcached.rb +131 -0
- data/lib/cash/adapter/redis.rb +152 -0
- data/lib/cash/config.rb +7 -4
- data/lib/cash/finders.rb +15 -3
- data/lib/cash/index.rb +3 -6
- data/lib/cash/local.rb +1 -1
- data/lib/cash/lock.rb +2 -2
- data/lib/cash/mock.rb +16 -12
- data/lib/cash/query/abstract.rb +29 -3
- data/lib/cash/version.rb +3 -0
- data/lib/cash/write_through.rb +9 -5
- data/lib/mem_cached_support_store.rb +1 -1
- data/rails/init.rb +1 -40
- data/spec/cash/calculations_spec.rb +11 -0
- data/spec/cash/finders_spec.rb +4 -4
- data/spec/cash/lock_spec.rb +30 -24
- data/spec/cash/marshal_spec.rb +1 -1
- data/spec/cash/transactional_spec.rb +19 -17
- data/spec/cash/without_caching_spec.rb +32 -0
- data/spec/cash/write_through_spec.rb +7 -0
- data/spec/spec_helper.rb +32 -6
- metadata +136 -45
- data/lib/memcached_wrapper.rb +0 -263
- data/spec/memcached_wrapper_test.rb +0 -209
data/README
CHANGED
@@ -117,9 +117,7 @@ Nested transactions are fully supported, with partial rollback and (apparent) pa
|
|
117
117
|
|
118
118
|
### Mocks ###
|
119
119
|
|
120
|
-
For your unit tests, it is faster to use a Memcached mock than the real deal.
|
121
|
-
|
122
|
-
$memcache = Cash::Mock.new
|
120
|
+
For your unit tests, it is faster to use a Memcached mock than the real deal. In your test environment, initialize the repository with an instance of Cash::Mock.
|
123
121
|
|
124
122
|
### Locks ###
|
125
123
|
|
@@ -133,39 +131,41 @@ In most cases locks are unnecessary; the transactional Memcached client will tak
|
|
133
131
|
|
134
132
|
Sometimes your code will request the same cache key twice in one request. You can avoid a round trip to the Memcached server by using a local, per-request cache. Add this to your initializer:
|
135
133
|
|
136
|
-
|
137
|
-
|
138
|
-
|
134
|
+
$memcache = MemcachedWrapper.new(config[:servers].gsub(' ', '').split(','), config)
|
135
|
+
$local = Cash::Local.new($memcache)
|
136
|
+
$lock = Cash::Lock.new($memcache)
|
137
|
+
$cache = Cash::Transactional.new($local, $lock)
|
138
|
+
|
139
139
|
## Installation ##
|
140
140
|
|
141
|
-
#### Step 0: Install MemCached
|
142
|
-
|
143
141
|
#### Step 1: Get the GEM ####
|
144
142
|
|
145
|
-
% gem sources -a http://gems.github.com
|
146
143
|
% sudo gem install ngmoco-cache-money
|
147
144
|
|
148
|
-
|
145
|
+
Add the gem you your Gemfile:
|
146
|
+
gem 'ngmoco-cache-money', :lib => 'cache_money'
|
147
|
+
|
148
|
+
#### Step 2: Configure cache client
|
149
|
+
|
150
|
+
In your environment, create a cache client instance configured for your cache servers.
|
151
|
+
|
152
|
+
$memcached = Memcached.new( ...servers..., ...options...)
|
149
153
|
|
150
|
-
|
154
|
+
Currently supported cache clients are: memcached, memcache-client
|
151
155
|
|
152
|
-
|
153
|
-
ttl: 604800
|
154
|
-
namespace: ...
|
155
|
-
sessions: false
|
156
|
-
debug: false
|
157
|
-
servers: localhost:11211
|
158
|
-
cache_money: true
|
156
|
+
#### Step 3: Configure Caching
|
159
157
|
|
160
|
-
|
161
|
-
....
|
158
|
+
Add the following to an initializer:
|
162
159
|
|
163
|
-
|
164
|
-
config.gem "ngmoco-cache-money",
|
165
|
-
:lib => "cache_money",
|
166
|
-
:source => 'http://gems.github.com',
|
167
|
-
:version => '0.2.9'
|
160
|
+
Cash.configure :repository => $memcached, :adapter => :memcached
|
168
161
|
|
162
|
+
Supported adapters are :memcache_client, :memcached. :memcached is assumed and is only compatible with Memcached clients.
|
163
|
+
Local or transactional semantics may be disabled by setting :local => false or :transactional => false.
|
164
|
+
|
165
|
+
Caching can be disabled on a per-environment basis in the environment's initializer:
|
166
|
+
|
167
|
+
Cash.enabled = false
|
168
|
+
|
169
169
|
#### Step 4: Add indices to your ActiveRecord models ####
|
170
170
|
|
171
171
|
Queries like `User.find(1)` will use the cache automatically. For more complex queries you must add indices on the attributes that you will query on. For example, a query like `User.find(:all, :conditions => {:name => 'bob'})` will require an index like:
|
@@ -187,24 +187,20 @@ There may be times where you only want to cache some of your models instead of e
|
|
187
187
|
In that case, you can omit the following from your `config/initializers/cache_money.rb`
|
188
188
|
|
189
189
|
class ActiveRecord::Base
|
190
|
-
is_cached
|
190
|
+
is_cached
|
191
191
|
end
|
192
192
|
|
193
193
|
After that is removed, you can simple put this at the top of your models you wish to cache:
|
194
194
|
|
195
|
-
is_cached
|
196
|
-
|
197
|
-
Just make sure that you put that line before any of your index directives.
|
198
|
-
|
199
|
-
## Version ##
|
195
|
+
is_cached
|
200
196
|
|
201
|
-
|
197
|
+
Just make sure that you put that line before any of your index directives. Note that all subclasses of a cached model are also cached.
|
202
198
|
|
203
199
|
## Acknowledgments ##
|
204
200
|
|
205
201
|
Thanks to
|
206
202
|
|
207
203
|
* Twitter for commissioning the development of this library and supporting the effort to open-source it.
|
208
|
-
* Sam Luckenbill for pairing with
|
204
|
+
* Sam Luckenbill for pairing with Nick on most of the hard stuff.
|
209
205
|
* Matthew and Chris for pairing a few days, offering useful feedback on the readability of the code, and the initial implementation of the Memcached mock.
|
210
|
-
* Evan Weaver for helping to reason-through software and testing strategies to deal with replication lag, and the initial implementation of the Memcached lock.
|
206
|
+
* Evan Weaver for helping to reason-through software and testing strategies to deal with replication lag, and the initial implementation of the Memcached lock.
|
data/README.markdown
CHANGED
@@ -117,9 +117,7 @@ Nested transactions are fully supported, with partial rollback and (apparent) pa
|
|
117
117
|
|
118
118
|
### Mocks ###
|
119
119
|
|
120
|
-
For your unit tests, it is faster to use a Memcached mock than the real deal.
|
121
|
-
|
122
|
-
$memcache = Cash::Mock.new
|
120
|
+
For your unit tests, it is faster to use a Memcached mock than the real deal. In your test environment, initialize the repository with an instance of Cash::Mock.
|
123
121
|
|
124
122
|
### Locks ###
|
125
123
|
|
@@ -133,39 +131,41 @@ In most cases locks are unnecessary; the transactional Memcached client will tak
|
|
133
131
|
|
134
132
|
Sometimes your code will request the same cache key twice in one request. You can avoid a round trip to the Memcached server by using a local, per-request cache. Add this to your initializer:
|
135
133
|
|
136
|
-
|
137
|
-
|
138
|
-
|
134
|
+
$memcache = MemcachedWrapper.new(config[:servers].gsub(' ', '').split(','), config)
|
135
|
+
$local = Cash::Local.new($memcache)
|
136
|
+
$lock = Cash::Lock.new($memcache)
|
137
|
+
$cache = Cash::Transactional.new($local, $lock)
|
138
|
+
|
139
139
|
## Installation ##
|
140
140
|
|
141
|
-
#### Step 0: Install MemCached
|
142
|
-
|
143
141
|
#### Step 1: Get the GEM ####
|
144
142
|
|
145
|
-
% gem sources -a http://gems.github.com
|
146
143
|
% sudo gem install ngmoco-cache-money
|
147
144
|
|
148
|
-
|
145
|
+
Add the gem you your Gemfile:
|
146
|
+
gem 'ngmoco-cache-money', :lib => 'cache_money'
|
147
|
+
|
148
|
+
#### Step 2: Configure cache client
|
149
|
+
|
150
|
+
In your environment, create a cache client instance configured for your cache servers.
|
151
|
+
|
152
|
+
$memcached = Memcached.new( ...servers..., ...options...)
|
149
153
|
|
150
|
-
|
154
|
+
Currently supported cache clients are: memcached, memcache-client
|
151
155
|
|
152
|
-
|
153
|
-
ttl: 604800
|
154
|
-
namespace: ...
|
155
|
-
sessions: false
|
156
|
-
debug: false
|
157
|
-
servers: localhost:11211
|
158
|
-
cache_money: true
|
156
|
+
#### Step 3: Configure Caching
|
159
157
|
|
160
|
-
|
161
|
-
....
|
158
|
+
Add the following to an initializer:
|
162
159
|
|
163
|
-
|
164
|
-
config.gem "ngmoco-cache-money",
|
165
|
-
:lib => "cache_money",
|
166
|
-
:source => 'http://gems.github.com',
|
167
|
-
:version => '0.2.9'
|
160
|
+
Cash.configure :repository => $memcached, :adapter => :memcached
|
168
161
|
|
162
|
+
Supported adapters are :memcache_client, :memcached. :memcached is assumed and is only compatible with Memcached clients.
|
163
|
+
Local or transactional semantics may be disabled by setting :local => false or :transactional => false.
|
164
|
+
|
165
|
+
Caching can be disabled on a per-environment basis in the environment's initializer:
|
166
|
+
|
167
|
+
Cash.enabled = false
|
168
|
+
|
169
169
|
#### Step 4: Add indices to your ActiveRecord models ####
|
170
170
|
|
171
171
|
Queries like `User.find(1)` will use the cache automatically. For more complex queries you must add indices on the attributes that you will query on. For example, a query like `User.find(:all, :conditions => {:name => 'bob'})` will require an index like:
|
@@ -187,24 +187,20 @@ There may be times where you only want to cache some of your models instead of e
|
|
187
187
|
In that case, you can omit the following from your `config/initializers/cache_money.rb`
|
188
188
|
|
189
189
|
class ActiveRecord::Base
|
190
|
-
is_cached
|
190
|
+
is_cached
|
191
191
|
end
|
192
192
|
|
193
193
|
After that is removed, you can simple put this at the top of your models you wish to cache:
|
194
194
|
|
195
|
-
is_cached
|
196
|
-
|
197
|
-
Just make sure that you put that line before any of your index directives.
|
198
|
-
|
199
|
-
## Version ##
|
195
|
+
is_cached
|
200
196
|
|
201
|
-
|
197
|
+
Just make sure that you put that line before any of your index directives. Note that all subclasses of a cached model are also cached.
|
202
198
|
|
203
199
|
## Acknowledgments ##
|
204
200
|
|
205
201
|
Thanks to
|
206
202
|
|
207
203
|
* Twitter for commissioning the development of this library and supporting the effort to open-source it.
|
208
|
-
* Sam Luckenbill for pairing with
|
204
|
+
* Sam Luckenbill for pairing with Nick on most of the hard stuff.
|
209
205
|
* Matthew and Chris for pairing a few days, offering useful feedback on the readability of the code, and the initial implementation of the Memcached mock.
|
210
|
-
* Evan Weaver for helping to reason-through software and testing strategies to deal with replication lag, and the initial implementation of the Memcached lock.
|
206
|
+
* Evan Weaver for helping to reason-through software and testing strategies to deal with replication lag, and the initial implementation of the Memcached lock.
|
data/config/environment.rb
CHANGED
@@ -1,14 +1,6 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
gem 'activesupport', '~> 2.3.0'
|
3
|
-
gem 'activerecord', '~> 2.3.0'
|
4
|
-
gem 'actionpack', '~> 2.3.0'
|
5
|
-
gem 'rspec', '>= 1.3.0'
|
6
|
-
gem 'jeweler', '~> 1.4.0'
|
7
|
-
|
8
1
|
require 'action_controller'
|
9
2
|
require 'active_record'
|
10
3
|
require 'active_record/session_store'
|
11
|
-
require 'jeweler'
|
12
4
|
|
13
5
|
ActiveRecord::Base.establish_connection(
|
14
6
|
:adapter => 'sqlite3',
|
data/config/memcached.yml
CHANGED
data/init.rb
CHANGED
@@ -1 +1 @@
|
|
1
|
-
require
|
1
|
+
require 'rails/init'
|
data/lib/cache_money.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'active_support'
|
2
2
|
require 'active_record'
|
3
3
|
|
4
|
+
require 'cash/version'
|
4
5
|
require 'cash/lock'
|
5
6
|
require 'cash/transactional'
|
6
7
|
require 'cash/write_through'
|
@@ -22,27 +23,31 @@ require 'cash/query/calculation'
|
|
22
23
|
require 'cash/util/array'
|
23
24
|
require 'cash/util/marshal'
|
24
25
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
if
|
38
|
-
|
39
|
-
|
40
|
-
|
26
|
+
module Cash
|
27
|
+
mattr_accessor :enabled
|
28
|
+
self.enabled = true
|
29
|
+
|
30
|
+
mattr_accessor :repository
|
31
|
+
|
32
|
+
def self.configure(options = {})
|
33
|
+
options.assert_valid_keys(:repository, :local, :transactional, :adapter, :default_ttl)
|
34
|
+
cache = options[:repository] || raise(":repository is a required option")
|
35
|
+
|
36
|
+
adapter = options.fetch(:adapter, :memcached)
|
37
|
+
|
38
|
+
if adapter
|
39
|
+
require "cash/adapter/#{adapter.to_s}"
|
40
|
+
klass = "Cash::Adapter::#{adapter.to_s.camelize}".constantize
|
41
|
+
cache = klass.new(cache, :logger => Rails.logger, :default_ttl => options.fetch(:default_ttl, 1.day.to_i))
|
41
42
|
end
|
43
|
+
|
44
|
+
lock = Cash::Lock.new(cache)
|
45
|
+
cache = Cash::Local.new(cache) if options.fetch(:local, true)
|
46
|
+
cache = Cash::Transactional.new(cache, lock) if options.fetch(:transactional, true)
|
47
|
+
|
48
|
+
self.repository = cache
|
42
49
|
end
|
43
|
-
|
44
|
-
|
45
|
-
module Cash
|
50
|
+
|
46
51
|
def self.included(active_record_class)
|
47
52
|
active_record_class.class_eval do
|
48
53
|
include Config, Accessor, WriteThrough, Finders
|
@@ -50,6 +55,12 @@ module Cash
|
|
50
55
|
end
|
51
56
|
end
|
52
57
|
|
58
|
+
private
|
59
|
+
|
60
|
+
def self.repository
|
61
|
+
@@repository || raise("Cash.configure must be called when Cash.enabled is true")
|
62
|
+
end
|
63
|
+
|
53
64
|
module ClassMethods
|
54
65
|
def self.extended(active_record_class)
|
55
66
|
class << active_record_class
|
@@ -57,30 +68,38 @@ module Cash
|
|
57
68
|
end
|
58
69
|
end
|
59
70
|
|
60
|
-
def transaction_with_cache_transaction(*args)
|
61
|
-
if
|
62
|
-
|
63
|
-
|
71
|
+
def transaction_with_cache_transaction(*args, &block)
|
72
|
+
if Cash.enabled
|
73
|
+
# Wrap both the db and cache transaction in another cache transaction so that the cache
|
74
|
+
# gets written only after the database commit but can still flush the inner cache
|
75
|
+
# transaction if an AR::Rollback is issued.
|
76
|
+
Cash.repository.transaction do
|
77
|
+
transaction_without_cache_transaction(*args) do
|
78
|
+
Cash.repository.transaction { block.call }
|
79
|
+
end
|
64
80
|
end
|
65
81
|
else
|
66
|
-
transaction_without_cache_transaction(*args)
|
82
|
+
transaction_without_cache_transaction(*args, &block)
|
67
83
|
end
|
68
84
|
end
|
69
|
-
|
70
|
-
def cacheable?(*args)
|
71
|
-
true
|
72
|
-
end
|
73
85
|
end
|
74
86
|
end
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
87
|
+
|
88
|
+
class ActiveRecord::Base
|
89
|
+
include Cash
|
90
|
+
|
91
|
+
def self.is_cached(options = {})
|
92
|
+
options.assert_valid_keys(:ttl, :repository, :version)
|
93
|
+
opts = options.dup
|
94
|
+
opts[:repository] = Cash.repository unless opts.has_key?(:repository)
|
95
|
+
Cash::Config.create(self, opts)
|
80
96
|
end
|
81
|
-
|
82
|
-
|
83
|
-
|
97
|
+
|
98
|
+
def <=>(other)
|
99
|
+
if self.id == other.id then
|
100
|
+
0
|
101
|
+
else
|
102
|
+
self.id < other.id ? -1 : 1
|
84
103
|
end
|
85
104
|
end
|
86
105
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'memcache'
|
2
|
+
|
3
|
+
module Cash
|
4
|
+
module Adapter
|
5
|
+
class MemcacheClient
|
6
|
+
def initialize(repository, options = {})
|
7
|
+
@repository = repository
|
8
|
+
@logger = options[:logger]
|
9
|
+
@default_ttl = options[:default_ttl] || raise(":default_ttl is a required option")
|
10
|
+
end
|
11
|
+
|
12
|
+
def add(key, value, ttl=nil, raw=false)
|
13
|
+
@repository.add(key, value, ttl || @default_ttl, raw)
|
14
|
+
end
|
15
|
+
|
16
|
+
def set(key, value, ttl=nil, raw=false)
|
17
|
+
@repository.set(key, value, ttl || @default_ttl, raw)
|
18
|
+
end
|
19
|
+
|
20
|
+
def exception_classes
|
21
|
+
MemCache::MemCacheError
|
22
|
+
end
|
23
|
+
|
24
|
+
def respond_to?(method)
|
25
|
+
super || @repository.respond_to?(method)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def method_missing(*args, &block)
|
31
|
+
@repository.send(*args, &block)
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'memcached'
|
2
|
+
|
3
|
+
# Maps memcached methods and semantics to those of memcache-client
|
4
|
+
module Cash
|
5
|
+
module Adapter
|
6
|
+
class Memcached
|
7
|
+
def initialize(repository, options = {})
|
8
|
+
@repository = repository
|
9
|
+
@logger = options[:logger]
|
10
|
+
@default_ttl = options[:default_ttl] || raise(":default_ttl is a required option")
|
11
|
+
end
|
12
|
+
|
13
|
+
def add(key, value, ttl=nil, raw=false)
|
14
|
+
wrap(key, not_stored) do
|
15
|
+
logger.debug("Memcached add: #{key.inspect}") if debug_logger?
|
16
|
+
@repository.add(key, raw ? value.to_s : value, ttl || @default_ttl, !raw)
|
17
|
+
logger.debug("Memcached hit: #{key.inspect}") if debug_logger?
|
18
|
+
stored
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Wraps Memcached#get so that it doesn't raise. This has the side-effect of preventing you from
|
23
|
+
# storing <tt>nil</tt> values.
|
24
|
+
def get(key, raw=false)
|
25
|
+
wrap(key) do
|
26
|
+
logger.debug("Memcached get: #{key.inspect}") if debug_logger?
|
27
|
+
value = wrap(key) { @repository.get(key, !raw) }
|
28
|
+
logger.debug("Memcached hit: #{key.inspect}") if debug_logger?
|
29
|
+
value
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def get_multi(*keys)
|
34
|
+
wrap(keys, {}) do
|
35
|
+
begin
|
36
|
+
keys.flatten!
|
37
|
+
logger.debug("Memcached get_multi: #{keys.inspect}") if debug_logger?
|
38
|
+
values = @repository.get(keys, true)
|
39
|
+
logger.debug("Memcached hit: #{keys.inspect}") if debug_logger?
|
40
|
+
values
|
41
|
+
rescue TypeError
|
42
|
+
log_error($!) if logger
|
43
|
+
keys.each { |key| delete(key) }
|
44
|
+
logger.debug("Memcached deleted: #{keys.inspect}") if debug_logger?
|
45
|
+
{}
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def set(key, value, ttl=nil, raw=false)
|
51
|
+
wrap(key, not_stored) do
|
52
|
+
logger.debug("Memcached set: #{key.inspect}") if debug_logger?
|
53
|
+
@repository.set(key, raw ? value.to_s : value, ttl || @default_ttl, !raw)
|
54
|
+
logger.debug("Memcached hit: #{key.inspect}") if debug_logger?
|
55
|
+
stored
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def delete(key)
|
60
|
+
wrap(key, not_found) do
|
61
|
+
logger.debug("Memcached delete: #{key.inspect}") if debug_logger?
|
62
|
+
@repository.delete(key)
|
63
|
+
logger.debug("Memcached hit: #{key.inspect}") if debug_logger?
|
64
|
+
deleted
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def get_server_for_key(key)
|
69
|
+
wrap(key) { @repository.server_by_key(key) }
|
70
|
+
end
|
71
|
+
|
72
|
+
def incr(key, value = 1)
|
73
|
+
wrap(key) { @repository.incr(key, value) }
|
74
|
+
end
|
75
|
+
|
76
|
+
def decr(key, value = 1)
|
77
|
+
wrap(key) { @repository.decr(key, value) }
|
78
|
+
end
|
79
|
+
|
80
|
+
def flush_all
|
81
|
+
@repository.flush
|
82
|
+
end
|
83
|
+
|
84
|
+
def exception_classes
|
85
|
+
::Memcached::Error
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def logger
|
91
|
+
@logger
|
92
|
+
end
|
93
|
+
|
94
|
+
def debug_logger?
|
95
|
+
logger && logger.respond_to?(:debug?) && logger.debug?
|
96
|
+
end
|
97
|
+
|
98
|
+
def wrap(key, error_value = nil, options = {})
|
99
|
+
yield
|
100
|
+
rescue ::Memcached::NotStored
|
101
|
+
logger.debug("Memcached miss: #{key.inspect}") if debug_logger?
|
102
|
+
error_value
|
103
|
+
rescue ::Memcached::Error
|
104
|
+
log_error($!) if logger
|
105
|
+
raise if options[:reraise_error]
|
106
|
+
error_value
|
107
|
+
end
|
108
|
+
|
109
|
+
def stored
|
110
|
+
"STORED\r\n"
|
111
|
+
end
|
112
|
+
|
113
|
+
def deleted
|
114
|
+
"DELETED\r\n"
|
115
|
+
end
|
116
|
+
|
117
|
+
def not_stored
|
118
|
+
"NOT_STORED\r\n"
|
119
|
+
end
|
120
|
+
|
121
|
+
def not_found
|
122
|
+
"NOT_FOUND\r\n"
|
123
|
+
end
|
124
|
+
|
125
|
+
def log_error(err)
|
126
|
+
#logger.error("#{err}: \n\t#{err.backtrace.join("\n\t")}") if logger
|
127
|
+
logger.error("Memcached ERROR, #{err.class}: #{err}") if logger
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|