sayso-dalli 1.0.3.001
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +7 -0
- data/History.md +175 -0
- data/LICENSE +20 -0
- data/Performance.md +77 -0
- data/README.md +177 -0
- data/Rakefile +34 -0
- data/Upgrade.md +45 -0
- data/dalli.gemspec +33 -0
- data/lib/action_controller/session/dalli_store.rb +62 -0
- data/lib/action_dispatch/middleware/session/dalli_store.rb +67 -0
- data/lib/active_support/cache/dalli_store.rb +187 -0
- data/lib/active_support/cache/dalli_store23.rb +172 -0
- data/lib/dalli/client.rb +264 -0
- data/lib/dalli/compatibility.rb +52 -0
- data/lib/dalli/memcache-client.rb +1 -0
- data/lib/dalli/options.rb +44 -0
- data/lib/dalli/ring.rb +105 -0
- data/lib/dalli/server.rb +516 -0
- data/lib/dalli/socket.rb +117 -0
- data/lib/dalli/version.rb +3 -0
- data/lib/dalli.rb +43 -0
- data/test/abstract_unit.rb +284 -0
- data/test/benchmark_test.rb +170 -0
- data/test/helper.rb +54 -0
- data/test/memcached_mock.rb +106 -0
- data/test/test_active_support.rb +213 -0
- data/test/test_compatibility.rb +33 -0
- data/test/test_dalli.rb +398 -0
- data/test/test_encoding.rb +43 -0
- data/test/test_failover.rb +89 -0
- data/test/test_network.rb +54 -0
- data/test/test_ring.rb +89 -0
- data/test/test_sasl.rb +79 -0
- data/test/test_session_store.rb +230 -0
- metadata +144 -0
@@ -0,0 +1,52 @@
|
|
1
|
+
class Dalli::Client
|
2
|
+
|
3
|
+
module MemcacheClientCompatibility
|
4
|
+
|
5
|
+
def initialize(*args)
|
6
|
+
Dalli.logger.error("Starting Dalli in memcache-client compatibility mode")
|
7
|
+
super(*args)
|
8
|
+
end
|
9
|
+
|
10
|
+
def set(key, value, ttl = nil, options = nil)
|
11
|
+
if options == true || options == false
|
12
|
+
Dalli.logger.error("Dalli: please use set(key, value, ttl, :raw => boolean): #{caller[0]}")
|
13
|
+
options = { :raw => options }
|
14
|
+
end
|
15
|
+
super(key, value, ttl, options) ? "STORED\r\n" : "NOT_STORED\r\n"
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
def add(key, value, ttl = nil, options = nil)
|
20
|
+
if options == true || options == false
|
21
|
+
Dalli.logger.error("Dalli: please use add(key, value, ttl, :raw => boolean): #{caller[0]}")
|
22
|
+
options = { :raw => options }
|
23
|
+
end
|
24
|
+
super(key, value, ttl, options) ? "STORED\r\n" : "NOT_STORED\r\n"
|
25
|
+
end
|
26
|
+
|
27
|
+
def replace(key, value, ttl = nil, options = nil)
|
28
|
+
if options == true || options == false
|
29
|
+
Dalli.logger.error("Dalli: please use replace(key, value, ttl, :raw => boolean): #{caller[0]}")
|
30
|
+
options = { :raw => options }
|
31
|
+
end
|
32
|
+
super(key, value, ttl, options) ? "STORED\r\n" : "NOT_STORED\r\n"
|
33
|
+
end
|
34
|
+
|
35
|
+
# Dalli does not unmarshall data that does not have the marshalled flag set so we need
|
36
|
+
# to unmarshall manually any marshalled data originally put in memcached by memcache-client.
|
37
|
+
# Peek at the data and see if it looks marshalled.
|
38
|
+
def get(key, options = nil)
|
39
|
+
value = super(key, options)
|
40
|
+
if value && value.is_a?(String) && !options && value.size > 2 &&
|
41
|
+
bytes = value.unpack('cc') && bytes[0] == 4 && bytes[1] == 8
|
42
|
+
return Marshal.load(value) rescue value
|
43
|
+
end
|
44
|
+
value
|
45
|
+
end
|
46
|
+
|
47
|
+
def delete(key)
|
48
|
+
super(key) ? "DELETED\r\n" : "NOT_DELETED\r\n"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
Dalli::Client.compatibility_mode = true
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'thread'
|
2
|
+
require 'monitor'
|
3
|
+
|
4
|
+
module Dalli
|
5
|
+
|
6
|
+
# Make Dalli threadsafe by using a lock around all
|
7
|
+
# public server methods.
|
8
|
+
#
|
9
|
+
# Dalli::Server.extend(Dalli::Threadsafe)
|
10
|
+
#
|
11
|
+
module Threadsafe
|
12
|
+
def request(op, *args)
|
13
|
+
lock.synchronize do
|
14
|
+
super
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def alive?
|
19
|
+
lock.synchronize do
|
20
|
+
super
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def close
|
25
|
+
lock.synchronize do
|
26
|
+
super
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def lock!
|
31
|
+
lock.mon_enter
|
32
|
+
end
|
33
|
+
|
34
|
+
def unlock!
|
35
|
+
lock.mon_exit
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
def lock
|
40
|
+
@lock ||= Monitor.new
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
data/lib/dalli/ring.rb
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'digest/sha1'
|
2
|
+
require 'zlib'
|
3
|
+
|
4
|
+
module Dalli
|
5
|
+
class Ring
|
6
|
+
POINTS_PER_SERVER = 160 # this is the default in libmemcached
|
7
|
+
|
8
|
+
attr_accessor :servers, :continuum
|
9
|
+
|
10
|
+
def initialize(servers, options)
|
11
|
+
@servers = servers
|
12
|
+
@continuum = nil
|
13
|
+
if servers.size > 1
|
14
|
+
total_weight = servers.inject(0) { |memo, srv| memo + srv.weight }
|
15
|
+
continuum = []
|
16
|
+
servers.each do |server|
|
17
|
+
entry_count_for(server, servers.size, total_weight).times do |idx|
|
18
|
+
hash = Digest::SHA1.hexdigest("#{server.hostname}:#{server.port}:#{idx}")
|
19
|
+
value = Integer("0x#{hash[0..7]}")
|
20
|
+
continuum << Dalli::Ring::Entry.new(value, server)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
@continuum = continuum.sort { |a, b| a.value <=> b.value }
|
24
|
+
end
|
25
|
+
|
26
|
+
threadsafe! unless options[:threadsafe] == false
|
27
|
+
@failover = options[:failover] != false
|
28
|
+
end
|
29
|
+
|
30
|
+
def server_for_key(key)
|
31
|
+
if @continuum
|
32
|
+
hkey = hash_for(key)
|
33
|
+
20.times do |try|
|
34
|
+
entryidx = self.class.binary_search(@continuum, hkey)
|
35
|
+
server = @continuum[entryidx].server
|
36
|
+
return server if server.alive?
|
37
|
+
break unless @failover
|
38
|
+
hkey = hash_for("#{try}#{key}")
|
39
|
+
end
|
40
|
+
else
|
41
|
+
server = @servers.first
|
42
|
+
return server if server && server.alive?
|
43
|
+
end
|
44
|
+
|
45
|
+
raise Dalli::RingError, "No server available"
|
46
|
+
end
|
47
|
+
|
48
|
+
def lock
|
49
|
+
@servers.each { |s| s.lock! }
|
50
|
+
begin
|
51
|
+
return yield
|
52
|
+
ensure
|
53
|
+
@servers.each { |s| s.unlock! }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def threadsafe!
|
60
|
+
@servers.each do |s|
|
61
|
+
s.extend(Dalli::Threadsafe)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def hash_for(key)
|
66
|
+
Zlib.crc32(key)
|
67
|
+
end
|
68
|
+
|
69
|
+
def entry_count_for(server, total_servers, total_weight)
|
70
|
+
((total_servers * POINTS_PER_SERVER * server.weight) / Float(total_weight)).floor
|
71
|
+
end
|
72
|
+
|
73
|
+
# Find the closest index in the Ring with value <= the given value
|
74
|
+
def self.binary_search(ary, value)
|
75
|
+
upper = ary.size - 1
|
76
|
+
lower = 0
|
77
|
+
idx = 0
|
78
|
+
|
79
|
+
while (lower <= upper) do
|
80
|
+
idx = (lower + upper) / 2
|
81
|
+
comp = ary[idx].value <=> value
|
82
|
+
|
83
|
+
if comp == 0
|
84
|
+
return idx
|
85
|
+
elsif comp > 0
|
86
|
+
upper = idx - 1
|
87
|
+
else
|
88
|
+
lower = idx + 1
|
89
|
+
end
|
90
|
+
end
|
91
|
+
return upper
|
92
|
+
end
|
93
|
+
|
94
|
+
class Entry
|
95
|
+
attr_reader :value
|
96
|
+
attr_reader :server
|
97
|
+
|
98
|
+
def initialize(val, srv)
|
99
|
+
@value = val
|
100
|
+
@server = srv
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
end
|