dalli 2.7.3 → 3.2.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/{History.md → CHANGELOG.md} +211 -0
- data/Gemfile +3 -6
- data/LICENSE +1 -1
- data/README.md +30 -208
- data/lib/dalli/cas/client.rb +2 -57
- data/lib/dalli/client.rb +254 -253
- data/lib/dalli/compressor.rb +13 -2
- data/lib/dalli/key_manager.rb +121 -0
- data/lib/dalli/options.rb +7 -7
- data/lib/dalli/pipelined_getter.rb +177 -0
- data/lib/dalli/protocol/base.rb +241 -0
- data/lib/dalli/protocol/binary/request_formatter.rb +117 -0
- data/lib/dalli/protocol/binary/response_header.rb +36 -0
- data/lib/dalli/protocol/binary/response_processor.rb +239 -0
- data/lib/dalli/protocol/binary/sasl_authentication.rb +60 -0
- data/lib/dalli/protocol/binary.rb +173 -0
- data/lib/dalli/protocol/connection_manager.rb +252 -0
- data/lib/dalli/protocol/meta/key_regularizer.rb +31 -0
- data/lib/dalli/protocol/meta/request_formatter.rb +121 -0
- data/lib/dalli/protocol/meta/response_processor.rb +211 -0
- data/lib/dalli/protocol/meta.rb +178 -0
- data/lib/dalli/protocol/response_buffer.rb +54 -0
- data/lib/dalli/protocol/server_config_parser.rb +86 -0
- data/lib/dalli/protocol/ttl_sanitizer.rb +45 -0
- data/lib/dalli/protocol/value_compressor.rb +85 -0
- data/lib/dalli/protocol/value_marshaller.rb +59 -0
- data/lib/dalli/protocol/value_serializer.rb +91 -0
- data/lib/dalli/protocol.rb +8 -0
- data/lib/dalli/ring.rb +97 -86
- data/lib/dalli/server.rb +4 -719
- data/lib/dalli/servers_arg_normalizer.rb +54 -0
- data/lib/dalli/socket.rb +123 -115
- data/lib/dalli/version.rb +5 -1
- data/lib/dalli.rb +45 -14
- data/lib/rack/session/dalli.rb +162 -42
- metadata +136 -63
- data/Performance.md +0 -42
- data/Rakefile +0 -43
- data/dalli.gemspec +0 -29
- data/lib/action_dispatch/middleware/session/dalli_store.rb +0 -81
- data/lib/active_support/cache/dalli_store.rb +0 -372
- data/lib/dalli/railtie.rb +0 -7
- data/test/benchmark_test.rb +0 -243
- data/test/helper.rb +0 -56
- data/test/memcached_mock.rb +0 -201
- data/test/sasl/memcached.conf +0 -1
- data/test/sasl/sasldb +0 -1
- data/test/test_active_support.rb +0 -541
- data/test/test_cas_client.rb +0 -107
- data/test/test_compressor.rb +0 -52
- data/test/test_dalli.rb +0 -682
- data/test/test_encoding.rb +0 -32
- data/test/test_failover.rb +0 -137
- data/test/test_network.rb +0 -64
- data/test/test_rack_session.rb +0 -341
- data/test/test_ring.rb +0 -85
- data/test/test_sasl.rb +0 -105
- data/test/test_serializer.rb +0 -29
- data/test/test_server.rb +0 -110
data/lib/dalli/ring.rb
CHANGED
@@ -1,59 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'digest/sha1'
|
2
4
|
require 'zlib'
|
3
5
|
|
4
6
|
module Dalli
|
7
|
+
##
|
8
|
+
# An implementation of a consistent hash ring, designed to minimize
|
9
|
+
# the cache miss impact of adding or removing servers from the ring.
|
10
|
+
# That is, adding or removing a server from the ring should impact
|
11
|
+
# the key -> server mapping of ~ 1/N of the stored keys where N is the
|
12
|
+
# number of servers in the ring. This is done by creating a large
|
13
|
+
# number of "points" per server, distributed over the space
|
14
|
+
# 0x00000000 - 0xFFFFFFFF. For a given key, we calculate the CRC32
|
15
|
+
# hash, and find the nearest "point" that is less than or equal to the
|
16
|
+
# the key's hash. In this implemetation, each "point" is represented
|
17
|
+
# by a Dalli::Ring::Entry.
|
18
|
+
##
|
5
19
|
class Ring
|
20
|
+
# The number of entries on the continuum created per server
|
21
|
+
# in an equally weighted scenario.
|
6
22
|
POINTS_PER_SERVER = 160 # this is the default in libmemcached
|
7
23
|
|
8
24
|
attr_accessor :servers, :continuum
|
9
25
|
|
10
|
-
def initialize(
|
11
|
-
@servers =
|
12
|
-
|
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.name}:#{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 }
|
26
|
+
def initialize(servers_arg, protocol_implementation, options)
|
27
|
+
@servers = servers_arg.map do |s|
|
28
|
+
protocol_implementation.new(s, options)
|
24
29
|
end
|
30
|
+
@continuum = nil
|
31
|
+
@continuum = build_continuum(servers) if servers.size > 1
|
25
32
|
|
26
33
|
threadsafe! unless options[:threadsafe] == false
|
27
34
|
@failover = options[:failover] != false
|
28
35
|
end
|
29
36
|
|
30
37
|
def server_for_key(key)
|
31
|
-
if @continuum
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
38
|
+
server = if @continuum
|
39
|
+
server_from_continuum(key)
|
40
|
+
else
|
41
|
+
@servers.first
|
42
|
+
end
|
43
|
+
|
44
|
+
# Note that the call to alive? has the side effect of initializing
|
45
|
+
# the socket
|
46
|
+
return server if server&.alive?
|
47
|
+
|
48
|
+
raise Dalli::RingError, 'No server available'
|
49
|
+
end
|
50
|
+
|
51
|
+
def server_from_continuum(key)
|
52
|
+
hkey = hash_for(key)
|
53
|
+
20.times do |try|
|
54
|
+
server = server_for_hash_key(hkey)
|
55
|
+
|
56
|
+
# Note that the call to alive? has the side effect of initializing
|
57
|
+
# the socket
|
58
|
+
return server if server.alive?
|
59
|
+
break unless @failover
|
60
|
+
|
61
|
+
hkey = hash_for("#{try}#{key}")
|
43
62
|
end
|
63
|
+
nil
|
64
|
+
end
|
44
65
|
|
45
|
-
|
66
|
+
def keys_grouped_by_server(key_arr)
|
67
|
+
key_arr.group_by do |key|
|
68
|
+
server_for_key(key)
|
69
|
+
rescue Dalli::RingError
|
70
|
+
Dalli.logger.debug { "unable to get key #{key}" }
|
71
|
+
nil
|
72
|
+
end
|
46
73
|
end
|
47
74
|
|
48
75
|
def lock
|
49
|
-
@servers.each
|
76
|
+
@servers.each(&:lock!)
|
50
77
|
begin
|
51
|
-
|
78
|
+
yield
|
52
79
|
ensure
|
53
|
-
@servers.each
|
80
|
+
@servers.each(&:unlock!)
|
54
81
|
end
|
55
82
|
end
|
56
83
|
|
84
|
+
def pipeline_consume_and_ignore_responses
|
85
|
+
@servers.each do |s|
|
86
|
+
s.request(:noop)
|
87
|
+
rescue Dalli::NetworkError
|
88
|
+
# Ignore this error, as it indicates the socket is unavailable
|
89
|
+
# and there's no need to flush
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def socket_timeout
|
94
|
+
@servers.first.socket_timeout
|
95
|
+
end
|
96
|
+
|
97
|
+
def close
|
98
|
+
@servers.each(&:close)
|
99
|
+
end
|
100
|
+
|
57
101
|
private
|
58
102
|
|
59
103
|
def threadsafe!
|
@@ -70,73 +114,40 @@ module Dalli
|
|
70
114
|
((total_servers * POINTS_PER_SERVER * server.weight) / Float(total_weight)).floor
|
71
115
|
end
|
72
116
|
|
73
|
-
|
74
|
-
# space. Fallback to a pure Ruby version if the compilation doesn't work.
|
75
|
-
# optional for performance and only necessary if you are using multiple
|
76
|
-
# memcached servers.
|
77
|
-
begin
|
78
|
-
require 'inline'
|
79
|
-
inline do |builder|
|
80
|
-
builder.c <<-EOM
|
81
|
-
int binary_search(VALUE ary, unsigned int r) {
|
82
|
-
long upper = RARRAY_LEN(ary) - 1;
|
83
|
-
long lower = 0;
|
84
|
-
long idx = 0;
|
85
|
-
ID value = rb_intern("value");
|
86
|
-
VALUE continuumValue;
|
87
|
-
unsigned int l;
|
88
|
-
|
89
|
-
while (lower <= upper) {
|
90
|
-
idx = (lower + upper) / 2;
|
91
|
-
|
92
|
-
continuumValue = rb_funcall(RARRAY_PTR(ary)[idx], value, 0);
|
93
|
-
l = NUM2UINT(continuumValue);
|
94
|
-
if (l == r) {
|
95
|
-
return idx;
|
96
|
-
}
|
97
|
-
else if (l > r) {
|
98
|
-
upper = idx - 1;
|
99
|
-
}
|
100
|
-
else {
|
101
|
-
lower = idx + 1;
|
102
|
-
}
|
103
|
-
}
|
104
|
-
return upper;
|
105
|
-
}
|
106
|
-
EOM
|
107
|
-
end
|
108
|
-
rescue LoadError
|
117
|
+
def server_for_hash_key(hash_key)
|
109
118
|
# Find the closest index in the Ring with value <= the given value
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
119
|
+
entryidx = @continuum.bsearch_index { |entry| entry.value > hash_key }
|
120
|
+
if entryidx.nil?
|
121
|
+
entryidx = @continuum.size - 1
|
122
|
+
else
|
123
|
+
entryidx -= 1
|
124
|
+
end
|
125
|
+
@continuum[entryidx].server
|
126
|
+
end
|
127
|
+
|
128
|
+
def build_continuum(servers)
|
129
|
+
continuum = []
|
130
|
+
total_weight = servers.inject(0) { |memo, srv| memo + srv.weight }
|
131
|
+
servers.each do |server|
|
132
|
+
entry_count_for(server, servers.size, total_weight).times do |idx|
|
133
|
+
hash = Digest::SHA1.hexdigest("#{server.name}:#{idx}")
|
134
|
+
value = Integer("0x#{hash[0..7]}")
|
135
|
+
continuum << Dalli::Ring::Entry.new(value, server)
|
126
136
|
end
|
127
|
-
return upper
|
128
137
|
end
|
138
|
+
continuum.sort_by(&:value)
|
129
139
|
end
|
130
140
|
|
141
|
+
##
|
142
|
+
# Represents a point in the consistent hash ring implementation.
|
143
|
+
##
|
131
144
|
class Entry
|
132
|
-
attr_reader :value
|
133
|
-
attr_reader :server
|
145
|
+
attr_reader :value, :server
|
134
146
|
|
135
147
|
def initialize(val, srv)
|
136
148
|
@value = val
|
137
149
|
@server = srv
|
138
150
|
end
|
139
151
|
end
|
140
|
-
|
141
152
|
end
|
142
153
|
end
|