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.

Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +7 -6
  3. data/History.md +124 -0
  4. data/README.md +26 -200
  5. data/lib/dalli/cas/client.rb +1 -57
  6. data/lib/dalli/client.rb +230 -263
  7. data/lib/dalli/compressor.rb +12 -2
  8. data/lib/dalli/key_manager.rb +113 -0
  9. data/lib/dalli/options.rb +6 -7
  10. data/lib/dalli/pipelined_getter.rb +177 -0
  11. data/lib/dalli/protocol/base.rb +241 -0
  12. data/lib/dalli/protocol/binary/request_formatter.rb +117 -0
  13. data/lib/dalli/protocol/binary/response_header.rb +36 -0
  14. data/lib/dalli/protocol/binary/response_processor.rb +239 -0
  15. data/lib/dalli/protocol/binary/sasl_authentication.rb +60 -0
  16. data/lib/dalli/protocol/binary.rb +173 -0
  17. data/lib/dalli/protocol/connection_manager.rb +252 -0
  18. data/lib/dalli/protocol/meta/key_regularizer.rb +31 -0
  19. data/lib/dalli/protocol/meta/request_formatter.rb +108 -0
  20. data/lib/dalli/protocol/meta/response_processor.rb +211 -0
  21. data/lib/dalli/protocol/meta.rb +177 -0
  22. data/lib/dalli/protocol/response_buffer.rb +54 -0
  23. data/lib/dalli/protocol/server_config_parser.rb +84 -0
  24. data/lib/dalli/protocol/ttl_sanitizer.rb +45 -0
  25. data/lib/dalli/protocol/value_compressor.rb +85 -0
  26. data/lib/dalli/protocol/value_marshaller.rb +59 -0
  27. data/lib/dalli/protocol/value_serializer.rb +91 -0
  28. data/lib/dalli/protocol.rb +8 -0
  29. data/lib/dalli/ring.rb +90 -81
  30. data/lib/dalli/server.rb +3 -749
  31. data/lib/dalli/servers_arg_normalizer.rb +54 -0
  32. data/lib/dalli/socket.rb +117 -137
  33. data/lib/dalli/version.rb +4 -1
  34. data/lib/dalli.rb +42 -14
  35. data/lib/rack/session/dalli.rb +95 -95
  36. metadata +118 -10
  37. data/lib/action_dispatch/middleware/session/dalli_store.rb +0 -82
  38. data/lib/active_support/cache/dalli_store.rb +0 -441
  39. 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
- hkey = hash_for(key)
34
- 20.times do |try|
35
- entryidx = binary_search(@continuum, hkey)
36
- server = @continuum[entryidx].server
37
- return server if server.alive?
38
- break unless @failover
39
- hkey = hash_for("#{try}#{key}")
40
- end
41
- else
42
- server = @servers.first
43
- return server if server && server.alive?
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
- raise Dalli::RingError, "No server available"
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
- return yield
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
- # Native extension to perform the binary search within the continuum
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
- def binary_search(ary, value)
112
- upper = ary.size - 1
113
- lower = 0
114
-
115
- while (lower <= upper) do
116
- idx = (lower + upper) / 2
117
- comp = ary[idx].value <=> value
118
-
119
- if comp == 0
120
- return idx
121
- elsif comp > 0
122
- upper = idx - 1
123
- else
124
- lower = idx + 1
125
- end
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