cache_bar 0.0.1 → 0.0.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/cache_bar.gemspec +1 -1
- data/init.rb +1 -10
- data/lib/{acts_as_cached.rb → cache_bar/acts_as_cached.rb} +4 -0
- data/lib/cache_bar/cache_store.rb +3 -1
- data/lib/cache_bar/ruby_client.rb +281 -0
- data/lib/cache_bar.rb +3 -281
- metadata +4 -2
data/cache_bar.gemspec
CHANGED
data/init.rb
CHANGED
|
@@ -1,10 +1 @@
|
|
|
1
|
-
require 'cache_bar'
|
|
2
|
-
require 'cache_bar/cache_store'
|
|
3
|
-
require 'acts_as_cached'
|
|
4
|
-
|
|
5
|
-
config.cache_store = CacheBar::CacheStore.new
|
|
6
|
-
config.after_initialize do
|
|
7
|
-
ActionView::Base.send(:acts_as_cached)
|
|
8
|
-
ActionController::Base.send(:acts_as_cached)
|
|
9
|
-
#ActiveRecord::Base.send(:acts_as_cached)
|
|
10
|
-
end
|
|
1
|
+
require 'cache_bar'
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
require 'digest/md5'
|
|
2
|
+
require 'socket'
|
|
3
|
+
require 'zlib'
|
|
4
|
+
require 'timeout'
|
|
5
|
+
|
|
6
|
+
class CacheBar
|
|
7
|
+
class NotFound < StandardError; end
|
|
8
|
+
class NotStored < StandardError; end
|
|
9
|
+
class ConnectionError < StandardError; end
|
|
10
|
+
|
|
11
|
+
def self.pool(name=nil)
|
|
12
|
+
name = name ? name.to_sym : :default
|
|
13
|
+
(@pools ||= {})[name] ||= begin
|
|
14
|
+
@config ||= begin
|
|
15
|
+
yaml = YAML.load(File.read(File.join(RAILS_ROOT, 'config', 'memcached.yml')))
|
|
16
|
+
(yaml['defaults'] || {}).merge(yaml[RAILS_ENV] || {})
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
if name == :default
|
|
20
|
+
new(@config.symbolize_keys)
|
|
21
|
+
else
|
|
22
|
+
raise "CacheBar Pool not found (#{name})" unless @config.has_key?(name.to_s) && @config[name.to_s].is_a?(Hash)
|
|
23
|
+
(@pools ||= {})[name.to_sym] ||= new(@config[name.to_s].symbolize_keys)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def initialize(options={})
|
|
29
|
+
@options = {
|
|
30
|
+
:namespace => 'default',
|
|
31
|
+
:gzip => false,
|
|
32
|
+
:ttl => 0,
|
|
33
|
+
:servers => '0.0.0.0'
|
|
34
|
+
}.merge(options)
|
|
35
|
+
@options[:servers] = [@options[:servers]].flatten
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def set(key, value, options={})
|
|
39
|
+
generic_set(:set, key, value, options)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def add(key, value, options={})
|
|
43
|
+
generic_set(:add, key, value, options)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def replace(key, value, options={})
|
|
47
|
+
generic_set(:replace, key, value, options)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def append(key, value, options={})
|
|
51
|
+
generic_set(:append, key, value, options)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def prepend(key, value, options={})
|
|
55
|
+
generic_set(:prepend, key, value, options)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def cas(key, value, cas, options={})
|
|
59
|
+
generic_set(:cas, key, value, options.merge(:cas => cas))
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def incr(key, value=1, options={})
|
|
63
|
+
generic_incr_or_decr(:incr, key, value, options)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def decr(key, value=1, options={})
|
|
67
|
+
generic_incr_or_decr(:decr, key, value, options)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def get(key, options={})
|
|
71
|
+
generic_get(:get, [key], options).first[:value]
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def gets(key, options={})
|
|
75
|
+
results = generic_get(:gets, [key], options)
|
|
76
|
+
[results.first[:value], results.first[:cas]]
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def get_multi(*keys)
|
|
80
|
+
options = keys.last.is_a?(Hash) ? keys.pop : {}
|
|
81
|
+
generic_get(:get, keys, options).inject({}) { |hash,results| hash[results[:key]] = results[:value]; hash }
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def gets_multi(*keys)
|
|
85
|
+
options = keys.last.is_a?(Hash) ? keys.pop : {}
|
|
86
|
+
generic_get(:gets, keys, options).inject({}) { |hash,results| hash[results[:key]] = [results[:value], results[:cas]]; hash }
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def delete(key, options={})
|
|
90
|
+
generic_key_command(:delete, key, nil, options)
|
|
91
|
+
true
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def flush_all
|
|
95
|
+
all_servers.inject({}) do |hash,(server,server_id)|
|
|
96
|
+
socket_for_server_id(server_id) do |socket|
|
|
97
|
+
socket.write("flush_all\r\n")
|
|
98
|
+
hash[server] = (socket.gets.strip == 'OK')
|
|
99
|
+
end
|
|
100
|
+
hash
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def stats
|
|
105
|
+
all_servers.inject({}) do |hash,(server,server_id)|
|
|
106
|
+
socket_for_server_id(server_id) do |socket|
|
|
107
|
+
socket.write("stats\r\n")
|
|
108
|
+
while true do
|
|
109
|
+
result, key, value = socket.gets.split(' ')
|
|
110
|
+
break if result == 'END'
|
|
111
|
+
(hash[server] ||= {})[key.to_sym] = value
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
hash
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def [](key)
|
|
119
|
+
get(key)
|
|
120
|
+
rescue NotFound
|
|
121
|
+
nil
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def []=(key, value)
|
|
125
|
+
set(key, value)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# get or set... a = cache.gos('a') { expensive_function_here() }
|
|
129
|
+
def gos(key, options={}, &block)
|
|
130
|
+
get(key, options)
|
|
131
|
+
rescue NotFound
|
|
132
|
+
set(key, block.call, options)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def with_options(options={})
|
|
136
|
+
(proxy = Object.new).instance_eval %(
|
|
137
|
+
def [](key)
|
|
138
|
+
@cache.get(key, @options)
|
|
139
|
+
rescue NotFound
|
|
140
|
+
nil
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def []=(key, value)
|
|
144
|
+
@cache.set(key, value, @options)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def method_missing(method, *params, &block)
|
|
148
|
+
params.push({}) unless params.last.is_a?(Hash)
|
|
149
|
+
params.last.merge!(@options)
|
|
150
|
+
@cache.send(method, *params, &block)
|
|
151
|
+
end
|
|
152
|
+
)
|
|
153
|
+
proxy.instance_variable_set('@cache', self)
|
|
154
|
+
proxy.instance_variable_set('@options', options)
|
|
155
|
+
proxy
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
protected
|
|
159
|
+
|
|
160
|
+
FLAG_INTEGER = 0x001
|
|
161
|
+
FLAG_MARSHAL = 0x010
|
|
162
|
+
FLAG_GZIP = 0x100
|
|
163
|
+
|
|
164
|
+
def namespace(key, options)
|
|
165
|
+
Digest::MD5.hexdigest("#{@options[:namespace]}:#{"#{options[:namespace]}:" if options[:namespace]}#{key}")
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def generic_incr_or_decr(command, key, value, options)
|
|
169
|
+
generic_key_command(command, key, value, options).to_i
|
|
170
|
+
rescue NotFound
|
|
171
|
+
begin
|
|
172
|
+
add(key, 0, options)
|
|
173
|
+
rescue NotStored
|
|
174
|
+
# this is fine... race condition, somebody else added the key already
|
|
175
|
+
end
|
|
176
|
+
generic_key_command(command, key, value, options).to_i
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def generic_key_command(command, key, data, options)
|
|
180
|
+
key = namespace(key, options)
|
|
181
|
+
results = socket_for_server_id(server_id_for_key(key)) do |socket|
|
|
182
|
+
socket.write("#{command} #{key} #{data}\r\n")
|
|
183
|
+
socket.gets.strip
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
case results
|
|
187
|
+
when "NOT_STORED", "EXISTS" then raise NotStored
|
|
188
|
+
when "NOT_FOUND" then raise NotFound
|
|
189
|
+
else results
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def generic_set(command, key, value, options)
|
|
194
|
+
flags = 0
|
|
195
|
+
if value.is_a?(Integer)
|
|
196
|
+
data = value.to_s
|
|
197
|
+
flags |= FLAG_INTEGER
|
|
198
|
+
else
|
|
199
|
+
if value.is_a?(String)
|
|
200
|
+
data = value
|
|
201
|
+
else
|
|
202
|
+
data = Marshal.dump(value)
|
|
203
|
+
flags |= FLAG_MARSHAL
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
if (options.has_key?(:gzip) && options[:gzip]) || @options[:gzip]
|
|
207
|
+
data = Zlib::Deflate.deflate(data)
|
|
208
|
+
flags |= FLAG_GZIP
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
generic_key_command(command, key, "#{flags} #{options[:ttl] || @options[:ttl]} #{data.length} #{options[:cas]}\r\n#{data}", options)
|
|
213
|
+
value
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def generic_get(command, keys, options)
|
|
217
|
+
md5_keys = keys.inject({}) { |hash, key| hash[namespace(key, options)] = key; hash }
|
|
218
|
+
server_keys = md5_keys.keys.inject({}) { |hash,key| (hash[server_id_for_key(key)] ||= []) << key; hash }
|
|
219
|
+
results = server_keys.inject([]) do |array, (server_id, keys)|
|
|
220
|
+
socket_for_server_id(server_id) do |socket|
|
|
221
|
+
socket.write("#{command} #{keys.join(' ')}\r\n")
|
|
222
|
+
|
|
223
|
+
while true
|
|
224
|
+
raise ConnectionError unless line = socket.gets
|
|
225
|
+
result, key, flag, length, cas = line.split(' ')
|
|
226
|
+
break if result == 'END'
|
|
227
|
+
|
|
228
|
+
raise ConnectionError unless value = socket.read(length.to_i)
|
|
229
|
+
raise ConnectionError unless socket.read(2)
|
|
230
|
+
|
|
231
|
+
value = value.to_i if flag.to_i & FLAG_INTEGER > 0
|
|
232
|
+
value = Zlib::Inflate.inflate(value) if flag.to_i & FLAG_GZIP > 0
|
|
233
|
+
begin
|
|
234
|
+
value = Marshal.load(value) if flag.to_i & FLAG_MARSHAL > 0
|
|
235
|
+
rescue ArgumentError => e
|
|
236
|
+
if e.message.match("undefined class/module (.*)")
|
|
237
|
+
$1.split('::').reject { |n| n.empty? }.inject(Object) do |constant,name|
|
|
238
|
+
constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
|
|
239
|
+
end
|
|
240
|
+
retry
|
|
241
|
+
else
|
|
242
|
+
raise
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
array << { :value => value, :key => md5_keys[key], :cas => cas.to_i }
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
array
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
if keys.length == 1 && results.length == 0
|
|
253
|
+
raise NotFound
|
|
254
|
+
else
|
|
255
|
+
results
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def server_id_for_key(key)
|
|
260
|
+
key.hex % all_servers.length
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def socket_for_server_id(server_id, &block)
|
|
264
|
+
timeout(1) do
|
|
265
|
+
(@sockets ||= {})[server_id] ||= begin
|
|
266
|
+
host, port = @options[:servers][server_id].split(':')
|
|
267
|
+
TCPSocket.new(host, port ? port.to_i : 11211)
|
|
268
|
+
end
|
|
269
|
+
block.call(@sockets[server_id])
|
|
270
|
+
end
|
|
271
|
+
rescue ConnectionError, Errno::EACCES, Errno::ECONNRESET, Errno::EPIPE, Errno::ECONNREFUSED, Errno::ETIMEDOUT, Timeout::Error
|
|
272
|
+
@sockets.delete(server_id)
|
|
273
|
+
raise ConnectionError
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
def all_servers
|
|
277
|
+
@options[:servers].inject([{},0]) { |(hash,index),server| hash[server] = index; [hash, index+1] }.first
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
end
|
|
281
|
+
|
data/lib/cache_bar.rb
CHANGED
|
@@ -1,281 +1,3 @@
|
|
|
1
|
-
require '
|
|
2
|
-
require '
|
|
3
|
-
require '
|
|
4
|
-
require 'timeout'
|
|
5
|
-
|
|
6
|
-
class CacheBar
|
|
7
|
-
class NotFound < StandardError; end
|
|
8
|
-
class NotStored < StandardError; end
|
|
9
|
-
class ConnectionError < StandardError; end
|
|
10
|
-
|
|
11
|
-
def self.pool(name=nil)
|
|
12
|
-
name = name ? name.to_sym : :default
|
|
13
|
-
(@pools ||= {})[name] ||= begin
|
|
14
|
-
@config ||= begin
|
|
15
|
-
yaml = YAML.load(File.read(File.join(RAILS_ROOT, 'config', 'memcached.yml')))
|
|
16
|
-
(yaml['defaults'] || {}).merge(yaml[RAILS_ENV] || {})
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
if name == :default
|
|
20
|
-
new(@config.symbolize_keys)
|
|
21
|
-
else
|
|
22
|
-
raise "CacheBar Pool not found (#{name})" unless @config.has_key?(name.to_s) && @config[name.to_s].is_a?(Hash)
|
|
23
|
-
(@pools ||= {})[name.to_sym] ||= new(@config[name.to_s].symbolize_keys)
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def initialize(options={})
|
|
29
|
-
@options = {
|
|
30
|
-
:namespace => 'default',
|
|
31
|
-
:gzip => false,
|
|
32
|
-
:ttl => 0,
|
|
33
|
-
:servers => '0.0.0.0'
|
|
34
|
-
}.merge(options)
|
|
35
|
-
@options[:servers] = [@options[:servers]].flatten
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def set(key, value, options={})
|
|
39
|
-
generic_set(:set, key, value, options)
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
def add(key, value, options={})
|
|
43
|
-
generic_set(:add, key, value, options)
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def replace(key, value, options={})
|
|
47
|
-
generic_set(:replace, key, value, options)
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def append(key, value, options={})
|
|
51
|
-
generic_set(:append, key, value, options)
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def prepend(key, value, options={})
|
|
55
|
-
generic_set(:prepend, key, value, options)
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
def cas(key, value, cas, options={})
|
|
59
|
-
generic_set(:cas, key, value, options.merge(:cas => cas))
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
def incr(key, value=1, options={})
|
|
63
|
-
generic_incr_or_decr(:incr, key, value, options)
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
def decr(key, value=1, options={})
|
|
67
|
-
generic_incr_or_decr(:decr, key, value, options)
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
def get(key, options={})
|
|
71
|
-
generic_get(:get, [key], options).first[:value]
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
def gets(key, options={})
|
|
75
|
-
results = generic_get(:gets, [key], options)
|
|
76
|
-
[results.first[:value], results.first[:cas]]
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
def get_multi(*keys)
|
|
80
|
-
options = keys.last.is_a?(Hash) ? keys.pop : {}
|
|
81
|
-
generic_get(:get, keys, options).inject({}) { |hash,results| hash[results[:key]] = results[:value]; hash }
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
def gets_multi(*keys)
|
|
85
|
-
options = keys.last.is_a?(Hash) ? keys.pop : {}
|
|
86
|
-
generic_get(:gets, keys, options).inject({}) { |hash,results| hash[results[:key]] = [results[:value], results[:cas]]; hash }
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
def delete(key, options={})
|
|
90
|
-
generic_key_command(:delete, key, nil, options)
|
|
91
|
-
true
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
def flush_all
|
|
95
|
-
all_servers.inject({}) do |hash,(server,server_id)|
|
|
96
|
-
socket_for_server_id(server_id) do |socket|
|
|
97
|
-
socket.write("flush_all\r\n")
|
|
98
|
-
hash[server] = (socket.gets.strip == 'OK')
|
|
99
|
-
end
|
|
100
|
-
hash
|
|
101
|
-
end
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
def stats
|
|
105
|
-
all_servers.inject({}) do |hash,(server,server_id)|
|
|
106
|
-
socket_for_server_id(server_id) do |socket|
|
|
107
|
-
socket.write("stats\r\n")
|
|
108
|
-
while true do
|
|
109
|
-
result, key, value = socket.gets.split(' ')
|
|
110
|
-
break if result == 'END'
|
|
111
|
-
(hash[server] ||= {})[key.to_sym] = value
|
|
112
|
-
end
|
|
113
|
-
end
|
|
114
|
-
hash
|
|
115
|
-
end
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
def [](key)
|
|
119
|
-
get(key)
|
|
120
|
-
rescue NotFound
|
|
121
|
-
nil
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
def []=(key, value)
|
|
125
|
-
set(key, value)
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
# get or set... a = cache.gos('a') { expensive_function_here() }
|
|
129
|
-
def gos(key, options={}, &block)
|
|
130
|
-
get(key, options)
|
|
131
|
-
rescue NotFound
|
|
132
|
-
set(key, block.call, options)
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
def with_options(options={})
|
|
136
|
-
(proxy = Object.new).instance_eval %(
|
|
137
|
-
def [](key)
|
|
138
|
-
@cache.get(key, @options)
|
|
139
|
-
rescue NotFound
|
|
140
|
-
nil
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
def []=(key, value)
|
|
144
|
-
@cache.set(key, value, @options)
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
def method_missing(method, *params, &block)
|
|
148
|
-
params.push({}) unless params.last.is_a?(Hash)
|
|
149
|
-
params.last.merge!(@options)
|
|
150
|
-
@cache.send(method, *params, &block)
|
|
151
|
-
end
|
|
152
|
-
)
|
|
153
|
-
proxy.instance_variable_set('@cache', self)
|
|
154
|
-
proxy.instance_variable_set('@options', options)
|
|
155
|
-
proxy
|
|
156
|
-
end
|
|
157
|
-
|
|
158
|
-
protected
|
|
159
|
-
|
|
160
|
-
FLAG_INTEGER = 0x001
|
|
161
|
-
FLAG_MARSHAL = 0x010
|
|
162
|
-
FLAG_GZIP = 0x100
|
|
163
|
-
|
|
164
|
-
def namespace(key, options)
|
|
165
|
-
Digest::MD5.hexdigest("#{@options[:namespace]}:#{"#{options[:namespace]}:" if options[:namespace]}#{key}")
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
def generic_incr_or_decr(command, key, value, options)
|
|
169
|
-
generic_key_command(command, key, value, options).to_i
|
|
170
|
-
rescue NotFound
|
|
171
|
-
begin
|
|
172
|
-
add(key, 0, options)
|
|
173
|
-
rescue NotStored
|
|
174
|
-
# this is fine... race condition, somebody else added the key already
|
|
175
|
-
end
|
|
176
|
-
generic_key_command(command, key, value, options).to_i
|
|
177
|
-
end
|
|
178
|
-
|
|
179
|
-
def generic_key_command(command, key, data, options)
|
|
180
|
-
key = namespace(key, options)
|
|
181
|
-
results = socket_for_server_id(server_id_for_key(key)) do |socket|
|
|
182
|
-
socket.write("#{command} #{key} #{data}\r\n")
|
|
183
|
-
socket.gets.strip
|
|
184
|
-
end
|
|
185
|
-
|
|
186
|
-
case results
|
|
187
|
-
when "NOT_STORED", "EXISTS" then raise NotStored
|
|
188
|
-
when "NOT_FOUND" then raise NotFound
|
|
189
|
-
else results
|
|
190
|
-
end
|
|
191
|
-
end
|
|
192
|
-
|
|
193
|
-
def generic_set(command, key, value, options)
|
|
194
|
-
flags = 0
|
|
195
|
-
if value.is_a?(Integer)
|
|
196
|
-
data = value.to_s
|
|
197
|
-
flags |= FLAG_INTEGER
|
|
198
|
-
else
|
|
199
|
-
if value.is_a?(String)
|
|
200
|
-
data = value
|
|
201
|
-
else
|
|
202
|
-
data = Marshal.dump(value)
|
|
203
|
-
flags |= FLAG_MARSHAL
|
|
204
|
-
end
|
|
205
|
-
|
|
206
|
-
if (options.has_key?(:gzip) && options[:gzip]) || @options[:gzip]
|
|
207
|
-
data = Zlib::Deflate.deflate(data)
|
|
208
|
-
flags |= FLAG_GZIP
|
|
209
|
-
end
|
|
210
|
-
end
|
|
211
|
-
|
|
212
|
-
generic_key_command(command, key, "#{flags} #{options[:ttl] || @options[:ttl]} #{data.length} #{options[:cas]}\r\n#{data}", options)
|
|
213
|
-
value
|
|
214
|
-
end
|
|
215
|
-
|
|
216
|
-
def generic_get(command, keys, options)
|
|
217
|
-
md5_keys = keys.inject({}) { |hash, key| hash[namespace(key, options)] = key; hash }
|
|
218
|
-
server_keys = md5_keys.keys.inject({}) { |hash,key| (hash[server_id_for_key(key)] ||= []) << key; hash }
|
|
219
|
-
results = server_keys.inject([]) do |array, (server_id, keys)|
|
|
220
|
-
socket_for_server_id(server_id) do |socket|
|
|
221
|
-
socket.write("#{command} #{keys.join(' ')}\r\n")
|
|
222
|
-
|
|
223
|
-
while true
|
|
224
|
-
raise ConnectionError unless line = socket.gets
|
|
225
|
-
result, key, flag, length, cas = line.split(' ')
|
|
226
|
-
break if result == 'END'
|
|
227
|
-
|
|
228
|
-
raise ConnectionError unless value = socket.read(length.to_i)
|
|
229
|
-
raise ConnectionError unless socket.read(2)
|
|
230
|
-
|
|
231
|
-
value = value.to_i if flag.to_i & FLAG_INTEGER > 0
|
|
232
|
-
value = Zlib::Inflate.inflate(value) if flag.to_i & FLAG_GZIP > 0
|
|
233
|
-
begin
|
|
234
|
-
value = Marshal.load(value) if flag.to_i & FLAG_MARSHAL > 0
|
|
235
|
-
rescue ArgumentError => e
|
|
236
|
-
if e.message.match("undefined class/module (.*)")
|
|
237
|
-
$1.split('::').reject { |n| n.empty? }.inject(Object) do |constant,name|
|
|
238
|
-
constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
|
|
239
|
-
end
|
|
240
|
-
retry
|
|
241
|
-
else
|
|
242
|
-
raise
|
|
243
|
-
end
|
|
244
|
-
end
|
|
245
|
-
|
|
246
|
-
array << { :value => value, :key => md5_keys[key], :cas => cas.to_i }
|
|
247
|
-
end
|
|
248
|
-
end
|
|
249
|
-
array
|
|
250
|
-
end
|
|
251
|
-
|
|
252
|
-
if keys.length == 1 && results.length == 0
|
|
253
|
-
raise NotFound
|
|
254
|
-
else
|
|
255
|
-
results
|
|
256
|
-
end
|
|
257
|
-
end
|
|
258
|
-
|
|
259
|
-
def server_id_for_key(key)
|
|
260
|
-
key.hex % all_servers.length
|
|
261
|
-
end
|
|
262
|
-
|
|
263
|
-
def socket_for_server_id(server_id, &block)
|
|
264
|
-
timeout(1) do
|
|
265
|
-
(@sockets ||= {})[server_id] ||= begin
|
|
266
|
-
host, port = @options[:servers][server_id].split(':')
|
|
267
|
-
TCPSocket.new(host, port ? port.to_i : 11211)
|
|
268
|
-
end
|
|
269
|
-
block.call(@sockets[server_id])
|
|
270
|
-
end
|
|
271
|
-
rescue ConnectionError, Errno::EACCES, Errno::ECONNRESET, Errno::EPIPE, Errno::ECONNREFUSED, Errno::ETIMEDOUT, Timeout::Error
|
|
272
|
-
@sockets.delete(server_id)
|
|
273
|
-
raise ConnectionError
|
|
274
|
-
end
|
|
275
|
-
|
|
276
|
-
def all_servers
|
|
277
|
-
@options[:servers].inject([{},0]) { |(hash,index),server| hash[server] = index; [hash, index+1] }.first
|
|
278
|
-
end
|
|
279
|
-
|
|
280
|
-
end
|
|
281
|
-
|
|
1
|
+
require 'cache_bar/ruby_client'
|
|
2
|
+
require 'cache_bar/acts_as_cached'
|
|
3
|
+
require 'cache_bar/cache_store'
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: cache_bar
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Warren Konkel
|
|
@@ -22,10 +22,12 @@ extensions: []
|
|
|
22
22
|
extra_rdoc_files: []
|
|
23
23
|
|
|
24
24
|
files:
|
|
25
|
+
- cache_bar-0.0.2.gem
|
|
25
26
|
- cache_bar.gemspec
|
|
26
27
|
- init.rb
|
|
27
|
-
- lib/acts_as_cached.rb
|
|
28
|
+
- lib/cache_bar/acts_as_cached.rb
|
|
28
29
|
- lib/cache_bar/cache_store.rb
|
|
30
|
+
- lib/cache_bar/ruby_client.rb
|
|
29
31
|
- lib/cache_bar.rb
|
|
30
32
|
- memcached.yml.sample
|
|
31
33
|
- MIT-LICENSE
|