redis 3.3.5 → 4.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +161 -2
  3. data/README.md +144 -79
  4. data/lib/redis/client.rb +166 -90
  5. data/lib/redis/cluster/command.rb +81 -0
  6. data/lib/redis/cluster/command_loader.rb +33 -0
  7. data/lib/redis/cluster/key_slot_converter.rb +72 -0
  8. data/lib/redis/cluster/node.rb +108 -0
  9. data/lib/redis/cluster/node_key.rb +31 -0
  10. data/lib/redis/cluster/node_loader.rb +37 -0
  11. data/lib/redis/cluster/option.rb +93 -0
  12. data/lib/redis/cluster/slot.rb +86 -0
  13. data/lib/redis/cluster/slot_loader.rb +49 -0
  14. data/lib/redis/cluster.rb +291 -0
  15. data/lib/redis/connection/command_helper.rb +7 -10
  16. data/lib/redis/connection/hiredis.rb +6 -5
  17. data/lib/redis/connection/registry.rb +2 -1
  18. data/lib/redis/connection/ruby.rb +128 -129
  19. data/lib/redis/connection/synchrony.rb +21 -8
  20. data/lib/redis/connection.rb +4 -2
  21. data/lib/redis/distributed.rb +194 -72
  22. data/lib/redis/errors.rb +48 -0
  23. data/lib/redis/hash_ring.rb +30 -73
  24. data/lib/redis/pipeline.rb +55 -15
  25. data/lib/redis/subscribe.rb +11 -12
  26. data/lib/redis/version.rb +3 -1
  27. data/lib/redis.rb +1451 -403
  28. metadata +49 -202
  29. data/.gitignore +0 -16
  30. data/.travis/Gemfile +0 -11
  31. data/.travis.yml +0 -89
  32. data/.yardopts +0 -3
  33. data/Gemfile +0 -4
  34. data/Rakefile +0 -87
  35. data/benchmarking/logging.rb +0 -71
  36. data/benchmarking/pipeline.rb +0 -51
  37. data/benchmarking/speed.rb +0 -21
  38. data/benchmarking/suite.rb +0 -24
  39. data/benchmarking/worker.rb +0 -71
  40. data/examples/basic.rb +0 -15
  41. data/examples/consistency.rb +0 -114
  42. data/examples/dist_redis.rb +0 -43
  43. data/examples/incr-decr.rb +0 -17
  44. data/examples/list.rb +0 -26
  45. data/examples/pubsub.rb +0 -37
  46. data/examples/sentinel/sentinel.conf +0 -9
  47. data/examples/sentinel/start +0 -49
  48. data/examples/sentinel.rb +0 -41
  49. data/examples/sets.rb +0 -36
  50. data/examples/unicorn/config.ru +0 -3
  51. data/examples/unicorn/unicorn.rb +0 -20
  52. data/redis.gemspec +0 -44
  53. data/test/bitpos_test.rb +0 -69
  54. data/test/blocking_commands_test.rb +0 -42
  55. data/test/client_test.rb +0 -59
  56. data/test/command_map_test.rb +0 -30
  57. data/test/commands_on_hashes_test.rb +0 -21
  58. data/test/commands_on_hyper_log_log_test.rb +0 -21
  59. data/test/commands_on_lists_test.rb +0 -20
  60. data/test/commands_on_sets_test.rb +0 -77
  61. data/test/commands_on_sorted_sets_test.rb +0 -137
  62. data/test/commands_on_strings_test.rb +0 -101
  63. data/test/commands_on_value_types_test.rb +0 -133
  64. data/test/connection_handling_test.rb +0 -277
  65. data/test/connection_test.rb +0 -57
  66. data/test/db/.gitkeep +0 -0
  67. data/test/distributed_blocking_commands_test.rb +0 -46
  68. data/test/distributed_commands_on_hashes_test.rb +0 -10
  69. data/test/distributed_commands_on_hyper_log_log_test.rb +0 -33
  70. data/test/distributed_commands_on_lists_test.rb +0 -22
  71. data/test/distributed_commands_on_sets_test.rb +0 -83
  72. data/test/distributed_commands_on_sorted_sets_test.rb +0 -18
  73. data/test/distributed_commands_on_strings_test.rb +0 -59
  74. data/test/distributed_commands_on_value_types_test.rb +0 -95
  75. data/test/distributed_commands_requiring_clustering_test.rb +0 -164
  76. data/test/distributed_connection_handling_test.rb +0 -23
  77. data/test/distributed_internals_test.rb +0 -79
  78. data/test/distributed_key_tags_test.rb +0 -52
  79. data/test/distributed_persistence_control_commands_test.rb +0 -26
  80. data/test/distributed_publish_subscribe_test.rb +0 -92
  81. data/test/distributed_remote_server_control_commands_test.rb +0 -66
  82. data/test/distributed_scripting_test.rb +0 -102
  83. data/test/distributed_sorting_test.rb +0 -20
  84. data/test/distributed_test.rb +0 -58
  85. data/test/distributed_transactions_test.rb +0 -32
  86. data/test/encoding_test.rb +0 -18
  87. data/test/error_replies_test.rb +0 -59
  88. data/test/fork_safety_test.rb +0 -65
  89. data/test/helper.rb +0 -232
  90. data/test/helper_test.rb +0 -24
  91. data/test/internals_test.rb +0 -417
  92. data/test/lint/blocking_commands.rb +0 -150
  93. data/test/lint/hashes.rb +0 -162
  94. data/test/lint/hyper_log_log.rb +0 -60
  95. data/test/lint/lists.rb +0 -143
  96. data/test/lint/sets.rb +0 -140
  97. data/test/lint/sorted_sets.rb +0 -316
  98. data/test/lint/strings.rb +0 -260
  99. data/test/lint/value_types.rb +0 -122
  100. data/test/persistence_control_commands_test.rb +0 -26
  101. data/test/pipelining_commands_test.rb +0 -242
  102. data/test/publish_subscribe_test.rb +0 -282
  103. data/test/remote_server_control_commands_test.rb +0 -118
  104. data/test/scanning_test.rb +0 -413
  105. data/test/scripting_test.rb +0 -78
  106. data/test/sentinel_command_test.rb +0 -80
  107. data/test/sentinel_test.rb +0 -255
  108. data/test/sorting_test.rb +0 -59
  109. data/test/ssl_test.rb +0 -73
  110. data/test/support/connection/hiredis.rb +0 -1
  111. data/test/support/connection/ruby.rb +0 -1
  112. data/test/support/connection/synchrony.rb +0 -17
  113. data/test/support/redis_mock.rb +0 -130
  114. data/test/support/ssl/gen_certs.sh +0 -31
  115. data/test/support/ssl/trusted-ca.crt +0 -25
  116. data/test/support/ssl/trusted-ca.key +0 -27
  117. data/test/support/ssl/trusted-cert.crt +0 -81
  118. data/test/support/ssl/trusted-cert.key +0 -28
  119. data/test/support/ssl/untrusted-ca.crt +0 -26
  120. data/test/support/ssl/untrusted-ca.key +0 -27
  121. data/test/support/ssl/untrusted-cert.crt +0 -82
  122. data/test/support/ssl/untrusted-cert.key +0 -28
  123. data/test/support/wire/synchrony.rb +0 -24
  124. data/test/support/wire/thread.rb +0 -5
  125. data/test/synchrony_driver.rb +0 -88
  126. data/test/test.conf.erb +0 -9
  127. data/test/thread_safety_test.rb +0 -62
  128. data/test/transactions_test.rb +0 -264
  129. data/test/unknown_commands_test.rb +0 -14
  130. data/test/url_param_test.rb +0 -138
@@ -1,8 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'zlib'
2
4
 
3
5
  class Redis
4
6
  class HashRing
5
-
6
7
  POINTS_PER_SERVER = 160 # this is the default in libmemcached
7
8
 
8
9
  attr_reader :ring, :sorted_keys, :replicas, :nodes
@@ -10,7 +11,7 @@ class Redis
10
11
  # nodes is a list of objects that have a proper to_s representation.
11
12
  # replicas indicates how many virtual points should be used pr. node,
12
13
  # replicas are required to improve the distribution.
13
- def initialize(nodes=[], replicas=POINTS_PER_SERVER)
14
+ def initialize(nodes = [], replicas = POINTS_PER_SERVER)
14
15
  @replicas = replicas
15
16
  @ring = {}
16
17
  @nodes = []
@@ -25,7 +26,6 @@ class Redis
25
26
  @nodes << node
26
27
  @replicas.times do |i|
27
28
  key = Zlib.crc32("#{node.id}:#{i}")
28
- raise "Node ID collision" if @ring.has_key?(key)
29
29
  @ring[key] = node
30
30
  @sorted_keys << key
31
31
  end
@@ -33,11 +33,11 @@ class Redis
33
33
  end
34
34
 
35
35
  def remove_node(node)
36
- @nodes.reject!{|n| n.id == node.id}
36
+ @nodes.reject! { |n| n.id == node.id }
37
37
  @replicas.times do |i|
38
38
  key = Zlib.crc32("#{node.id}:#{i}")
39
39
  @ring.delete(key)
40
- @sorted_keys.reject! {|k| k == key}
40
+ @sorted_keys.reject! { |k| k == key }
41
41
  end
42
42
  end
43
43
 
@@ -47,86 +47,43 @@ class Redis
47
47
  end
48
48
 
49
49
  def get_node_pos(key)
50
- return [nil,nil] if @ring.size == 0
50
+ return [nil, nil] if @ring.empty?
51
+
51
52
  crc = Zlib.crc32(key)
52
53
  idx = HashRing.binary_search(@sorted_keys, crc)
53
- return [@ring[@sorted_keys[idx]], idx]
54
+ [@ring[@sorted_keys[idx]], idx]
54
55
  end
55
56
 
56
57
  def iter_nodes(key)
57
- return [nil,nil] if @ring.size == 0
58
+ return [nil, nil] if @ring.empty?
59
+
58
60
  _, pos = get_node_pos(key)
59
61
  @ring.size.times do |n|
60
- yield @ring[@sorted_keys[(pos+n) % @ring.size]]
62
+ yield @ring[@sorted_keys[(pos + n) % @ring.size]]
61
63
  end
62
64
  end
63
65
 
64
- class << self
65
-
66
- # gem install RubyInline to use this code
67
- # Native extension to perform the binary search within the hashring.
68
- # There's a pure ruby version below so this is purely optional
69
- # for performance. In testing 20k gets and sets, the native
70
- # binary search shaved about 12% off the runtime (9sec -> 8sec).
71
- begin
72
- require 'inline'
73
- inline do |builder|
74
- builder.c <<-EOM
75
- int binary_search(VALUE ary, unsigned int r) {
76
- int upper = RARRAY_LEN(ary) - 1;
77
- int lower = 0;
78
- int idx = 0;
79
-
80
- while (lower <= upper) {
81
- idx = (lower + upper) / 2;
82
-
83
- VALUE continuumValue = RARRAY_PTR(ary)[idx];
84
- unsigned int l = NUM2UINT(continuumValue);
85
- if (l == r) {
86
- return idx;
87
- }
88
- else if (l > r) {
89
- upper = idx - 1;
90
- }
91
- else {
92
- lower = idx + 1;
93
- }
94
- }
95
- if (upper < 0) {
96
- upper = RARRAY_LEN(ary) - 1;
97
- }
98
- return upper;
99
- }
100
- EOM
66
+ # Find the closest index in HashRing with value <= the given value
67
+ def self.binary_search(ary, value)
68
+ upper = ary.size - 1
69
+ lower = 0
70
+ idx = 0
71
+
72
+ while lower <= upper
73
+ idx = (lower + upper) / 2
74
+ comp = ary[idx] <=> value
75
+
76
+ if comp == 0
77
+ return idx
78
+ elsif comp > 0
79
+ upper = idx - 1
80
+ else
81
+ lower = idx + 1
101
82
  end
102
- rescue Exception
103
- # Find the closest index in HashRing with value <= the given value
104
- def binary_search(ary, value, &block)
105
- upper = ary.size - 1
106
- lower = 0
107
- idx = 0
108
-
109
- while(lower <= upper) do
110
- idx = (lower + upper) / 2
111
- comp = ary[idx] <=> value
112
-
113
- if comp == 0
114
- return idx
115
- elsif comp > 0
116
- upper = idx - 1
117
- else
118
- lower = idx + 1
119
- end
120
- end
121
-
122
- if upper < 0
123
- upper = ary.size - 1
124
- end
125
- return upper
126
- end
127
-
128
83
  end
129
- end
130
84
 
85
+ upper = ary.size - 1 if upper < 0
86
+ upper
87
+ end
131
88
  end
132
89
  end
@@ -1,21 +1,23 @@
1
- class Redis
2
- unless defined?(::BasicObject)
3
- class BasicObject
4
- instance_methods.each { |meth| undef_method(meth) unless meth =~ /\A(__|instance_eval)/ }
5
- end
6
- end
1
+ # frozen_string_literal: true
7
2
 
3
+ class Redis
8
4
  class Pipeline
9
5
  attr_accessor :db
6
+ attr_reader :client
10
7
 
11
8
  attr :futures
12
9
 
13
- def initialize
10
+ def initialize(client)
11
+ @client = client.is_a?(Pipeline) ? client.client : client
14
12
  @with_reconnect = true
15
13
  @shutdown = false
16
14
  @futures = []
17
15
  end
18
16
 
17
+ def timeout
18
+ client.timeout
19
+ end
20
+
19
21
  def with_reconnect?
20
22
  @with_reconnect
21
23
  end
@@ -28,15 +30,23 @@ class Redis
28
30
  @shutdown
29
31
  end
30
32
 
31
- def call(command, &block)
33
+ def empty?
34
+ @futures.empty?
35
+ end
36
+
37
+ def call(command, timeout: nil, &block)
32
38
  # A pipeline that contains a shutdown should not raise ECONNRESET when
33
39
  # the connection is gone.
34
40
  @shutdown = true if command.first == :shutdown
35
- future = Future.new(command, block)
41
+ future = Future.new(command, block, timeout)
36
42
  @futures << future
37
43
  future
38
44
  end
39
45
 
46
+ def call_with_timeout(command, timeout, &block)
47
+ call(command, timeout: timeout, &block)
48
+ end
49
+
40
50
  def call_pipeline(pipeline)
41
51
  @shutdown = true if pipeline.shutdown?
42
52
  @futures.concat(pipeline.futures)
@@ -45,10 +55,14 @@ class Redis
45
55
  end
46
56
 
47
57
  def commands
48
- @futures.map { |f| f._command }
58
+ @futures.map(&:_command)
49
59
  end
50
60
 
51
- def with_reconnect(val=true)
61
+ def timeouts
62
+ @futures.map(&:timeout)
63
+ end
64
+
65
+ def with_reconnect(val = true)
52
66
  @with_reconnect = false unless val
53
67
  yield
54
68
  end
@@ -80,7 +94,8 @@ class Redis
80
94
 
81
95
  if exec.size < futures.size
82
96
  # Some command wasn't recognized by Redis.
83
- raise replies.detect { |r| r.is_a?(CommandError) }
97
+ command_error = replies.detect { |r| r.is_a?(CommandError) }
98
+ raise command_error
84
99
  end
85
100
 
86
101
  super(exec) do |reply|
@@ -91,8 +106,20 @@ class Redis
91
106
  end
92
107
  end
93
108
 
109
+ def timeouts
110
+ if empty?
111
+ []
112
+ else
113
+ [nil, *super, nil]
114
+ end
115
+ end
116
+
94
117
  def commands
95
- [[:multi]] + super + [[:exec]]
118
+ if empty?
119
+ []
120
+ else
121
+ [[:multi]] + super + [[:exec]]
122
+ end
96
123
  end
97
124
  end
98
125
  end
@@ -106,12 +133,25 @@ class Redis
106
133
  class Future < BasicObject
107
134
  FutureNotReady = ::Redis::FutureNotReady.new
108
135
 
109
- def initialize(command, transformation)
136
+ attr_reader :timeout
137
+
138
+ def initialize(command, transformation, timeout)
110
139
  @command = command
111
140
  @transformation = transformation
141
+ @timeout = timeout
112
142
  @object = FutureNotReady
113
143
  end
114
144
 
145
+ def ==(_other)
146
+ message = +"The methods == and != are deprecated for Redis::Future and will be removed in 4.2.0"
147
+ message << " - You probably meant to call .value == or .value !="
148
+ message << " (#{::Kernel.caller(1, 1).first})\n"
149
+
150
+ ::Kernel.warn(message)
151
+
152
+ super
153
+ end
154
+
115
155
  def inspect
116
156
  "<Redis::Future #{@command.inspect}>"
117
157
  end
@@ -126,7 +166,7 @@ class Redis
126
166
  end
127
167
 
128
168
  def value
129
- ::Kernel.raise(@object) if @object.kind_of?(::RuntimeError)
169
+ ::Kernel.raise(@object) if @object.is_a?(::RuntimeError)
130
170
  @object
131
171
  end
132
172
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Redis
2
4
  class SubscribedClient
3
5
  def initialize(client)
@@ -32,24 +34,21 @@ class Redis
32
34
  call([:punsubscribe, *channels])
33
35
  end
34
36
 
35
- protected
37
+ protected
36
38
 
37
39
  def subscription(start, stop, channels, block, timeout = 0)
38
40
  sub = Subscription.new(&block)
39
41
 
40
42
  unsubscribed = false
41
43
 
42
- begin
43
- @client.call_loop([start, *channels], timeout) do |line|
44
- type, *rest = line
45
- sub.callbacks[type].call(*rest)
46
- unsubscribed = type == stop && rest.last == 0
47
- break if unsubscribed
48
- end
49
- ensure
50
- # No need to unsubscribe here. The real client closes the connection
51
- # whenever an exception is raised (see #ensure_connected).
44
+ @client.call_loop([start, *channels], timeout) do |line|
45
+ type, *rest = line
46
+ sub.callbacks[type].call(*rest)
47
+ unsubscribed = type == stop && rest.last == 0
48
+ break if unsubscribed
52
49
  end
50
+ # No need to unsubscribe here. The real client closes the connection
51
+ # whenever an exception is raised (see #ensure_connected).
53
52
  end
54
53
  end
55
54
 
@@ -58,7 +57,7 @@ class Redis
58
57
 
59
58
  def initialize
60
59
  @callbacks = Hash.new do |hash, key|
61
- hash[key] = lambda { |*_| }
60
+ hash[key] = ->(*_) {}
62
61
  end
63
62
 
64
63
  yield(self)
data/lib/redis/version.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Redis
2
- VERSION = "3.3.5"
4
+ VERSION = '4.5.1'
3
5
  end