dalli 2.7.11 → 3.2.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of dalli might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Gemfile +7 -6
- data/History.md +124 -0
- data/README.md +26 -200
- data/lib/dalli/cas/client.rb +1 -57
- data/lib/dalli/client.rb +230 -263
- data/lib/dalli/compressor.rb +12 -2
- data/lib/dalli/key_manager.rb +113 -0
- data/lib/dalli/options.rb +6 -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 +108 -0
- data/lib/dalli/protocol/meta/response_processor.rb +211 -0
- data/lib/dalli/protocol/meta.rb +177 -0
- data/lib/dalli/protocol/response_buffer.rb +54 -0
- data/lib/dalli/protocol/server_config_parser.rb +84 -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 +90 -81
- data/lib/dalli/server.rb +3 -749
- data/lib/dalli/servers_arg_normalizer.rb +54 -0
- data/lib/dalli/socket.rb +117 -137
- data/lib/dalli/version.rb +4 -1
- data/lib/dalli.rb +42 -14
- data/lib/rack/session/dalli.rb +95 -95
- metadata +118 -10
- data/lib/action_dispatch/middleware/session/dalli_store.rb +0 -82
- data/lib/active_support/cache/dalli_store.rb +0 -441
- data/lib/dalli/railtie.rb +0 -8
data/lib/dalli/ring.rb
CHANGED
@@ -1,9 +1,24 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
require 'digest/sha1'
|
3
4
|
require 'zlib'
|
4
5
|
|
5
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
|
+
##
|
6
19
|
class Ring
|
20
|
+
# The number of entries on the continuum created per server
|
21
|
+
# in an equally weighted scenario.
|
7
22
|
POINTS_PER_SERVER = 160 # this is the default in libmemcached
|
8
23
|
|
9
24
|
attr_accessor :servers, :continuum
|
@@ -11,50 +26,76 @@ module Dalli
|
|
11
26
|
def initialize(servers, options)
|
12
27
|
@servers = servers
|
13
28
|
@continuum = nil
|
14
|
-
if servers.size > 1
|
15
|
-
total_weight = servers.inject(0) { |memo, srv| memo + srv.weight }
|
16
|
-
continuum = []
|
17
|
-
servers.each do |server|
|
18
|
-
entry_count_for(server, servers.size, total_weight).times do |idx|
|
19
|
-
hash = Digest::SHA1.hexdigest("#{server.name}:#{idx}")
|
20
|
-
value = Integer("0x#{hash[0..7]}")
|
21
|
-
continuum << Dalli::Ring::Entry.new(value, server)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
@continuum = continuum.sort_by(&:value)
|
25
|
-
end
|
29
|
+
@continuum = build_continuum(servers) if servers.size > 1
|
26
30
|
|
27
31
|
threadsafe! unless options[:threadsafe] == false
|
28
32
|
@failover = options[:failover] != false
|
29
33
|
end
|
30
34
|
|
31
35
|
def server_for_key(key)
|
32
|
-
if @continuum
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
36
|
+
server = if @continuum
|
37
|
+
server_from_continuum(key)
|
38
|
+
else
|
39
|
+
@servers.first
|
40
|
+
end
|
41
|
+
|
42
|
+
# Note that the call to alive? has the side effect of initializing
|
43
|
+
# the socket
|
44
|
+
return server if server&.alive?
|
45
|
+
|
46
|
+
raise Dalli::RingError, 'No server available'
|
47
|
+
end
|
48
|
+
|
49
|
+
def server_from_continuum(key)
|
50
|
+
hkey = hash_for(key)
|
51
|
+
20.times do |try|
|
52
|
+
server = server_for_hash_key(hkey)
|
53
|
+
|
54
|
+
# Note that the call to alive? has the side effect of initializing
|
55
|
+
# the socket
|
56
|
+
return server if server.alive?
|
57
|
+
break unless @failover
|
58
|
+
|
59
|
+
hkey = hash_for("#{try}#{key}")
|
44
60
|
end
|
61
|
+
nil
|
62
|
+
end
|
45
63
|
|
46
|
-
|
64
|
+
def keys_grouped_by_server(key_arr)
|
65
|
+
key_arr.group_by do |key|
|
66
|
+
server_for_key(key)
|
67
|
+
rescue Dalli::RingError
|
68
|
+
Dalli.logger.debug { "unable to get key #{key}" }
|
69
|
+
nil
|
70
|
+
end
|
47
71
|
end
|
48
72
|
|
49
73
|
def lock
|
50
74
|
@servers.each(&:lock!)
|
51
75
|
begin
|
52
|
-
|
76
|
+
yield
|
53
77
|
ensure
|
54
78
|
@servers.each(&:unlock!)
|
55
79
|
end
|
56
80
|
end
|
57
81
|
|
82
|
+
def pipeline_consume_and_ignore_responses
|
83
|
+
@servers.each do |s|
|
84
|
+
s.request(:noop)
|
85
|
+
rescue Dalli::NetworkError
|
86
|
+
# Ignore this error, as it indicates the socket is unavailable
|
87
|
+
# and there's no need to flush
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def socket_timeout
|
92
|
+
@servers.first.socket_timeout
|
93
|
+
end
|
94
|
+
|
95
|
+
def close
|
96
|
+
@servers.each(&:close)
|
97
|
+
end
|
98
|
+
|
58
99
|
private
|
59
100
|
|
60
101
|
def threadsafe!
|
@@ -71,72 +112,40 @@ module Dalli
|
|
71
112
|
((total_servers * POINTS_PER_SERVER * server.weight) / Float(total_weight)).floor
|
72
113
|
end
|
73
114
|
|
74
|
-
|
75
|
-
# space. Fallback to a pure Ruby version if the compilation doesn't work.
|
76
|
-
# optional for performance and only necessary if you are using multiple
|
77
|
-
# memcached servers.
|
78
|
-
begin
|
79
|
-
require 'inline'
|
80
|
-
inline do |builder|
|
81
|
-
builder.c <<-EOM
|
82
|
-
int binary_search(VALUE ary, unsigned int r) {
|
83
|
-
long upper = RARRAY_LEN(ary) - 1;
|
84
|
-
long lower = 0;
|
85
|
-
long idx = 0;
|
86
|
-
ID value = rb_intern("value");
|
87
|
-
VALUE continuumValue;
|
88
|
-
unsigned int l;
|
89
|
-
|
90
|
-
while (lower <= upper) {
|
91
|
-
idx = (lower + upper) / 2;
|
92
|
-
|
93
|
-
continuumValue = rb_funcall(RARRAY_PTR(ary)[idx], value, 0);
|
94
|
-
l = NUM2UINT(continuumValue);
|
95
|
-
if (l == r) {
|
96
|
-
return idx;
|
97
|
-
}
|
98
|
-
else if (l > r) {
|
99
|
-
upper = idx - 1;
|
100
|
-
}
|
101
|
-
else {
|
102
|
-
lower = idx + 1;
|
103
|
-
}
|
104
|
-
}
|
105
|
-
return upper;
|
106
|
-
}
|
107
|
-
EOM
|
108
|
-
end
|
109
|
-
rescue LoadError
|
115
|
+
def server_for_hash_key(hash_key)
|
110
116
|
# Find the closest index in the Ring with value <= the given value
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
117
|
+
entryidx = @continuum.bsearch_index { |entry| entry.value > hash_key }
|
118
|
+
if entryidx.nil?
|
119
|
+
entryidx = @continuum.size - 1
|
120
|
+
else
|
121
|
+
entryidx -= 1
|
122
|
+
end
|
123
|
+
@continuum[entryidx].server
|
124
|
+
end
|
125
|
+
|
126
|
+
def build_continuum(servers)
|
127
|
+
continuum = []
|
128
|
+
total_weight = servers.inject(0) { |memo, srv| memo + srv.weight }
|
129
|
+
servers.each do |server|
|
130
|
+
entry_count_for(server, servers.size, total_weight).times do |idx|
|
131
|
+
hash = Digest::SHA1.hexdigest("#{server.name}:#{idx}")
|
132
|
+
value = Integer("0x#{hash[0..7]}")
|
133
|
+
continuum << Dalli::Ring::Entry.new(value, server)
|
126
134
|
end
|
127
|
-
upper
|
128
135
|
end
|
136
|
+
continuum.sort_by(&:value)
|
129
137
|
end
|
130
138
|
|
139
|
+
##
|
140
|
+
# Represents a point in the consistent hash ring implementation.
|
141
|
+
##
|
131
142
|
class Entry
|
132
|
-
attr_reader :value
|
133
|
-
attr_reader :server
|
143
|
+
attr_reader :value, :server
|
134
144
|
|
135
145
|
def initialize(val, srv)
|
136
146
|
@value = val
|
137
147
|
@server = srv
|
138
148
|
end
|
139
149
|
end
|
140
|
-
|
141
150
|
end
|
142
151
|
end
|