ninjudd-ninjudd-memcache-client 1.5.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +85 -0
- data/README.txt +54 -0
- data/Rakefile +24 -0
- data/ext/crc32/crc32.c +28 -0
- data/ext/crc32/extconf.rb +5 -0
- data/lib/memcache.rb +791 -0
- data/lib/memcache_extended.rb +112 -0
- data/lib/memcache_mock.rb +126 -0
- data/lib/memcache_util.rb +90 -0
- data/test/test_mem_cache.rb +744 -0
- data/test/test_memcache_extended.rb +27 -0
- data/test/test_memcache_mock.rb +94 -0
- metadata +66 -0
@@ -0,0 +1,112 @@
|
|
1
|
+
if not defined?(MemCache)
|
2
|
+
require File.dirname(__FILE__) + '/memcache'
|
3
|
+
end
|
4
|
+
|
5
|
+
module MemCacheExtensions
|
6
|
+
LOCK_TIMEOUT = 5 if not defined? LOCK_TIMEOUT
|
7
|
+
WRITE_LOCK_WAIT = 0.001 if not defined? WRITE_LOCK_WAIT
|
8
|
+
|
9
|
+
def get_some(keys, disable = false)
|
10
|
+
keys = keys.collect {|key| key.to_s}
|
11
|
+
|
12
|
+
records = {}
|
13
|
+
records = self.get_multi(keys) unless disable
|
14
|
+
keys_to_fetch = keys - records.keys
|
15
|
+
|
16
|
+
if keys_to_fetch.any?
|
17
|
+
yield(keys_to_fetch).each do |key, data_item|
|
18
|
+
self.set(key, data_item) unless disable
|
19
|
+
records[key] = data_item
|
20
|
+
end
|
21
|
+
end
|
22
|
+
records
|
23
|
+
end
|
24
|
+
|
25
|
+
def in_namespace(namespace)
|
26
|
+
begin
|
27
|
+
# Temporarily change the namespace for convenience.
|
28
|
+
ns = self.namespace
|
29
|
+
self.instance_variable_set(:@namespace, "#{ns}#{namespace}")
|
30
|
+
yield
|
31
|
+
ensure
|
32
|
+
self.instance_variable_set(:@namespace, ns)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def get_or_set(key)
|
37
|
+
get(key) || begin
|
38
|
+
value = yield
|
39
|
+
set(key, value)
|
40
|
+
value
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def get_reset_expiry(key, expiry)
|
45
|
+
result = get(key)
|
46
|
+
set(key, result, expiry) if result
|
47
|
+
result
|
48
|
+
end
|
49
|
+
|
50
|
+
def lock(key)
|
51
|
+
# Returns true if the lock already exists.
|
52
|
+
response = add(lock_key(key), true, LOCK_TIMEOUT)
|
53
|
+
response.index('STORED') != 0
|
54
|
+
end
|
55
|
+
|
56
|
+
def unlock(key)
|
57
|
+
response = delete(lock_key(key))
|
58
|
+
response.index('DELETED') == 0
|
59
|
+
end
|
60
|
+
|
61
|
+
def with_lock(key, flag = nil)
|
62
|
+
while lock(key) do
|
63
|
+
return if flag == :ignore
|
64
|
+
sleep(WRITE_LOCK_WAIT) # just wait
|
65
|
+
end
|
66
|
+
yield
|
67
|
+
unlock(key) unless flag == :keep
|
68
|
+
end
|
69
|
+
|
70
|
+
def lock_key(key)
|
71
|
+
"lock:#{key}"
|
72
|
+
end
|
73
|
+
|
74
|
+
def locked?(key)
|
75
|
+
not get(lock_key(key)).nil?
|
76
|
+
end
|
77
|
+
|
78
|
+
def set_with_lock(*args)
|
79
|
+
with_lock(args.first, :ignore) do
|
80
|
+
set(*args)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def add_with_lock(*args)
|
85
|
+
with_lock(args.first, :ignore) do
|
86
|
+
add(*args)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def delete_with_lock(*args)
|
91
|
+
# leave a :delete lock around to prevent someone from
|
92
|
+
# adding stale data for a little while
|
93
|
+
with_lock(args.first, :keep) do
|
94
|
+
delete(*args)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
### To support using memcache in testing.
|
99
|
+
def clear; end
|
100
|
+
def empty?; false; end
|
101
|
+
###
|
102
|
+
end
|
103
|
+
|
104
|
+
class MemCache
|
105
|
+
include MemCacheExtensions
|
106
|
+
end
|
107
|
+
|
108
|
+
if defined?(MemCacheMock)
|
109
|
+
class MemCacheMock
|
110
|
+
include MemCacheExtensions
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
class MemCacheMock
|
2
|
+
attr_writer :namespace
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@data = {}
|
6
|
+
@expiry = {}
|
7
|
+
@auto_clear = false
|
8
|
+
end
|
9
|
+
|
10
|
+
def namespace
|
11
|
+
@namespace.to_s
|
12
|
+
end
|
13
|
+
|
14
|
+
def cache_key(key)
|
15
|
+
"#{namespace}:#{key}"
|
16
|
+
end
|
17
|
+
|
18
|
+
# Note: This doesn't work exactly like memcache's incr
|
19
|
+
# because MemCacheMock doesn't support raw storage.
|
20
|
+
# This version will work on marshalled data.
|
21
|
+
# This is also not atomic.
|
22
|
+
def incr(key, amount=1)
|
23
|
+
oldval = get(key).to_i or return nil
|
24
|
+
newval = oldval + amount
|
25
|
+
set(key, newval) # Note: Loses the expiry.
|
26
|
+
return newval
|
27
|
+
end
|
28
|
+
|
29
|
+
def decr(key, amount=1)
|
30
|
+
incr(key, amount * -1)
|
31
|
+
end
|
32
|
+
|
33
|
+
def set(*args)
|
34
|
+
do_set(*args)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Note: Raw not implemented.
|
38
|
+
def do_set(key, value, expiry = 0, raw=false)
|
39
|
+
return '' if @auto_clear
|
40
|
+
key = cache_key(key)
|
41
|
+
|
42
|
+
@data[key] = Marshal.dump(value)
|
43
|
+
@expiry[key] = Time.now + expiry if expiry != 0
|
44
|
+
'STORED'
|
45
|
+
end
|
46
|
+
|
47
|
+
def add(key, value, expiry = 0)
|
48
|
+
do_set(key, value, expiry) unless get(key)
|
49
|
+
end
|
50
|
+
|
51
|
+
def kind_of?(type)
|
52
|
+
(type == MemCache) || super
|
53
|
+
end
|
54
|
+
|
55
|
+
def delete(key)
|
56
|
+
key = cache_key(key)
|
57
|
+
@data.delete(key)
|
58
|
+
end
|
59
|
+
|
60
|
+
def clear
|
61
|
+
@data.clear
|
62
|
+
@expiry.clear
|
63
|
+
end
|
64
|
+
|
65
|
+
def get_multi(*keys)
|
66
|
+
hash = {}
|
67
|
+
keys.each do |key|
|
68
|
+
val = get(key)
|
69
|
+
key = cache_key(key).sub("#{namespace}:",'')
|
70
|
+
hash[key] = val if val
|
71
|
+
end
|
72
|
+
hash
|
73
|
+
end
|
74
|
+
|
75
|
+
# Note: Raw not implemented.
|
76
|
+
def get(key, raw=false)
|
77
|
+
key = cache_key(key)
|
78
|
+
clear if @auto_clear
|
79
|
+
if @expiry[key] and Time.now > @expiry[key]
|
80
|
+
@data[key] = nil
|
81
|
+
@expiry[key] = nil
|
82
|
+
end
|
83
|
+
return if not @data[key]
|
84
|
+
Marshal.load(@data[key])
|
85
|
+
end
|
86
|
+
|
87
|
+
def [](key)
|
88
|
+
get(key)
|
89
|
+
end
|
90
|
+
|
91
|
+
def []=(key, value)
|
92
|
+
set(key, value)
|
93
|
+
end
|
94
|
+
|
95
|
+
def empty?
|
96
|
+
@data.empty?
|
97
|
+
end
|
98
|
+
|
99
|
+
def keys
|
100
|
+
@data.keys
|
101
|
+
end
|
102
|
+
|
103
|
+
def auto_clear_on(&block)
|
104
|
+
if block_given?
|
105
|
+
auto_clear_block(true, &block)
|
106
|
+
else
|
107
|
+
@auto_clear = true
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def auto_clear_off(&block)
|
112
|
+
if block_given?
|
113
|
+
auto_clear_block(false, &block)
|
114
|
+
else
|
115
|
+
@auto_clear = false
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def auto_clear_block(value, &block)
|
120
|
+
old_auto_clear = @auto_clear
|
121
|
+
@auto_clear = value
|
122
|
+
block.call
|
123
|
+
@auto_clear = old_auto_clear
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
##
|
2
|
+
# A utility wrapper around the MemCache client to simplify cache access. All
|
3
|
+
# methods silently ignore MemCache errors.
|
4
|
+
|
5
|
+
module Cache
|
6
|
+
|
7
|
+
##
|
8
|
+
# Returns the object at +key+ from the cache if successful, or nil if either
|
9
|
+
# the object is not in the cache or if there was an error attermpting to
|
10
|
+
# access the cache.
|
11
|
+
#
|
12
|
+
# If there is a cache miss and a block is given the result of the block will
|
13
|
+
# be stored in the cache with optional +expiry+, using the +add+ method rather
|
14
|
+
# than +set+.
|
15
|
+
|
16
|
+
def self.get(key, expiry = 0)
|
17
|
+
start_time = Time.now
|
18
|
+
value = CACHE.get key
|
19
|
+
elapsed = Time.now - start_time
|
20
|
+
ActiveRecord::Base.logger.debug('MemCache Get (%0.6f) %s' % [elapsed, key])
|
21
|
+
if value.nil? and block_given? then
|
22
|
+
value = yield
|
23
|
+
add key, value, expiry
|
24
|
+
end
|
25
|
+
value
|
26
|
+
rescue MemCache::MemCacheError => err
|
27
|
+
ActiveRecord::Base.logger.debug "MemCache Error: #{err.message}"
|
28
|
+
if block_given? then
|
29
|
+
value = yield
|
30
|
+
put key, value, expiry
|
31
|
+
end
|
32
|
+
value
|
33
|
+
end
|
34
|
+
|
35
|
+
##
|
36
|
+
# Sets +value+ in the cache at +key+, with an optional +expiry+ time in
|
37
|
+
# seconds.
|
38
|
+
|
39
|
+
def self.put(key, value, expiry = 0)
|
40
|
+
start_time = Time.now
|
41
|
+
CACHE.set key, value, expiry
|
42
|
+
elapsed = Time.now - start_time
|
43
|
+
ActiveRecord::Base.logger.debug('MemCache Set (%0.6f) %s' % [elapsed, key])
|
44
|
+
value
|
45
|
+
rescue MemCache::MemCacheError => err
|
46
|
+
ActiveRecord::Base.logger.debug "MemCache Error: #{err.message}"
|
47
|
+
nil
|
48
|
+
end
|
49
|
+
|
50
|
+
##
|
51
|
+
# Sets +value+ in the cache at +key+, with an optional +expiry+ time in
|
52
|
+
# seconds. If +key+ already exists in cache, returns nil.
|
53
|
+
|
54
|
+
def self.add(key, value, expiry = 0)
|
55
|
+
start_time = Time.now
|
56
|
+
response = CACHE.add key, value, expiry
|
57
|
+
elapsed = Time.now - start_time
|
58
|
+
ActiveRecord::Base.logger.debug('MemCache Add (%0.6f) %s' % [elapsed, key])
|
59
|
+
(response == "STORED\r\n") ? value : nil
|
60
|
+
rescue MemCache::MemCacheError => err
|
61
|
+
ActiveRecord::Base.logger.debug "MemCache Error: #{err.message}"
|
62
|
+
nil
|
63
|
+
end
|
64
|
+
|
65
|
+
##
|
66
|
+
# Deletes +key+ from the cache in +delay+ seconds.
|
67
|
+
|
68
|
+
def self.delete(key, delay = nil)
|
69
|
+
start_time = Time.now
|
70
|
+
CACHE.delete key, delay
|
71
|
+
elapsed = Time.now - start_time
|
72
|
+
ActiveRecord::Base.logger.debug('MemCache Delete (%0.6f) %s' %
|
73
|
+
[elapsed, key])
|
74
|
+
nil
|
75
|
+
rescue MemCache::MemCacheError => err
|
76
|
+
ActiveRecord::Base.logger.debug "MemCache Error: #{err.message}"
|
77
|
+
nil
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
# Resets all connections to MemCache servers.
|
82
|
+
|
83
|
+
def self.reset
|
84
|
+
CACHE.reset
|
85
|
+
ActiveRecord::Base.logger.debug 'MemCache Connections Reset'
|
86
|
+
nil
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|