redis 3.3.5 → 4.0.3
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.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/.travis/Gemfile +8 -1
- data/.travis.yml +34 -62
- data/CHANGELOG.md +45 -2
- data/Gemfile +5 -1
- data/README.md +32 -76
- data/benchmarking/logging.rb +1 -1
- data/bin/build +71 -0
- data/bors.toml +14 -0
- data/lib/redis/client.rb +38 -20
- data/lib/redis/cluster/command.rb +81 -0
- data/lib/redis/cluster/command_loader.rb +32 -0
- data/lib/redis/cluster/key_slot_converter.rb +72 -0
- data/lib/redis/cluster/node.rb +104 -0
- data/lib/redis/cluster/node_key.rb +35 -0
- data/lib/redis/cluster/node_loader.rb +35 -0
- data/lib/redis/cluster/option.rb +76 -0
- data/lib/redis/cluster/slot.rb +69 -0
- data/lib/redis/cluster/slot_loader.rb +47 -0
- data/lib/redis/cluster.rb +285 -0
- data/lib/redis/connection/command_helper.rb +2 -8
- data/lib/redis/connection/hiredis.rb +2 -2
- data/lib/redis/connection/ruby.rb +13 -30
- data/lib/redis/connection/synchrony.rb +12 -4
- data/lib/redis/connection.rb +2 -2
- data/lib/redis/distributed.rb +29 -8
- data/lib/redis/errors.rb +46 -0
- data/lib/redis/hash_ring.rb +20 -64
- data/lib/redis/pipeline.rb +9 -7
- data/lib/redis/version.rb +1 -1
- data/lib/redis.rb +287 -52
- data/makefile +74 -0
- data/redis.gemspec +9 -10
- data/test/bitpos_test.rb +13 -19
- data/test/blocking_commands_test.rb +3 -5
- data/test/client_test.rb +18 -1
- data/test/cluster_abnormal_state_test.rb +38 -0
- data/test/cluster_blocking_commands_test.rb +15 -0
- data/test/cluster_client_internals_test.rb +77 -0
- data/test/cluster_client_key_hash_tags_test.rb +88 -0
- data/test/cluster_client_options_test.rb +147 -0
- data/test/cluster_client_pipelining_test.rb +59 -0
- data/test/cluster_client_replicas_test.rb +36 -0
- data/test/cluster_client_slots_test.rb +94 -0
- data/test/cluster_client_transactions_test.rb +71 -0
- data/test/cluster_commands_on_cluster_test.rb +165 -0
- data/test/cluster_commands_on_connection_test.rb +40 -0
- data/test/cluster_commands_on_geo_test.rb +74 -0
- data/test/cluster_commands_on_hashes_test.rb +11 -0
- data/test/cluster_commands_on_hyper_log_log_test.rb +17 -0
- data/test/cluster_commands_on_keys_test.rb +134 -0
- data/test/cluster_commands_on_lists_test.rb +15 -0
- data/test/cluster_commands_on_pub_sub_test.rb +101 -0
- data/test/cluster_commands_on_scripting_test.rb +56 -0
- data/test/cluster_commands_on_server_test.rb +221 -0
- data/test/cluster_commands_on_sets_test.rb +39 -0
- data/test/cluster_commands_on_sorted_sets_test.rb +35 -0
- data/test/cluster_commands_on_streams_test.rb +196 -0
- data/test/cluster_commands_on_strings_test.rb +15 -0
- data/test/cluster_commands_on_transactions_test.rb +41 -0
- data/test/cluster_commands_on_value_types_test.rb +14 -0
- data/test/command_map_test.rb +3 -5
- data/test/commands_on_geo_test.rb +116 -0
- data/test/commands_on_hashes_test.rb +2 -16
- data/test/commands_on_hyper_log_log_test.rb +3 -17
- data/test/commands_on_lists_test.rb +2 -15
- data/test/commands_on_sets_test.rb +2 -72
- data/test/commands_on_sorted_sets_test.rb +2 -132
- data/test/commands_on_strings_test.rb +2 -96
- data/test/commands_on_value_types_test.rb +80 -6
- data/test/connection_handling_test.rb +5 -7
- data/test/distributed_blocking_commands_test.rb +10 -4
- data/test/distributed_commands_on_hashes_test.rb +16 -5
- data/test/distributed_commands_on_hyper_log_log_test.rb +8 -15
- data/test/distributed_commands_on_lists_test.rb +4 -7
- data/test/distributed_commands_on_sets_test.rb +58 -36
- data/test/distributed_commands_on_sorted_sets_test.rb +51 -10
- data/test/distributed_commands_on_strings_test.rb +30 -10
- data/test/distributed_commands_on_value_types_test.rb +38 -4
- data/test/distributed_commands_requiring_clustering_test.rb +1 -3
- data/test/distributed_connection_handling_test.rb +1 -3
- data/test/distributed_internals_test.rb +8 -19
- data/test/distributed_key_tags_test.rb +4 -6
- data/test/distributed_persistence_control_commands_test.rb +1 -3
- data/test/distributed_publish_subscribe_test.rb +1 -3
- data/test/distributed_remote_server_control_commands_test.rb +1 -3
- data/test/distributed_scripting_test.rb +1 -3
- data/test/distributed_sorting_test.rb +1 -3
- data/test/distributed_test.rb +12 -14
- data/test/distributed_transactions_test.rb +1 -3
- data/test/encoding_test.rb +4 -8
- data/test/error_replies_test.rb +2 -4
- data/test/fork_safety_test.rb +1 -6
- data/test/helper.rb +179 -66
- data/test/helper_test.rb +1 -3
- data/test/internals_test.rb +47 -56
- data/test/lint/blocking_commands.rb +40 -16
- data/test/lint/hashes.rb +41 -0
- data/test/lint/hyper_log_log.rb +15 -1
- data/test/lint/lists.rb +16 -0
- data/test/lint/sets.rb +142 -0
- data/test/lint/sorted_sets.rb +183 -2
- data/test/lint/strings.rb +108 -20
- data/test/lint/value_types.rb +8 -0
- data/test/persistence_control_commands_test.rb +1 -3
- data/test/pipelining_commands_test.rb +12 -8
- data/test/publish_subscribe_test.rb +1 -3
- data/test/remote_server_control_commands_test.rb +60 -3
- data/test/scanning_test.rb +1 -7
- data/test/scripting_test.rb +1 -3
- data/test/sentinel_command_test.rb +1 -3
- data/test/sentinel_test.rb +1 -3
- data/test/sorting_test.rb +1 -3
- data/test/ssl_test.rb +45 -49
- data/test/support/cluster/orchestrator.rb +199 -0
- data/test/support/connection/hiredis.rb +1 -1
- data/test/support/connection/ruby.rb +1 -1
- data/test/support/connection/synchrony.rb +1 -1
- data/test/support/redis_mock.rb +1 -1
- data/test/synchrony_driver.rb +6 -9
- data/test/thread_safety_test.rb +1 -3
- data/test/transactions_test.rb +11 -3
- data/test/unknown_commands_test.rb +1 -3
- data/test/url_param_test.rb +44 -46
- metadata +109 -16
- data/Rakefile +0 -87
@@ -1,6 +1,4 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require File.expand_path("helper", File.dirname(__FILE__))
|
1
|
+
require_relative "helper"
|
4
2
|
|
5
3
|
class TestRemoteServerControlCommands < Test::Unit::TestCase
|
6
4
|
|
@@ -115,4 +113,63 @@ class TestRemoteServerControlCommands < Test::Unit::TestCase
|
|
115
113
|
result = r.slowlog(:len)
|
116
114
|
assert_equal 0, result
|
117
115
|
end
|
116
|
+
|
117
|
+
def test_client
|
118
|
+
assert_equal r.instance_variable_get(:@client), r._client
|
119
|
+
end
|
120
|
+
|
121
|
+
def test_client_list
|
122
|
+
return if version < "2.4.0"
|
123
|
+
|
124
|
+
keys = [
|
125
|
+
"addr",
|
126
|
+
"fd",
|
127
|
+
"name",
|
128
|
+
"age",
|
129
|
+
"idle",
|
130
|
+
"flags",
|
131
|
+
"db",
|
132
|
+
"sub",
|
133
|
+
"psub",
|
134
|
+
"multi",
|
135
|
+
"qbuf",
|
136
|
+
"qbuf-free",
|
137
|
+
"obl",
|
138
|
+
"oll",
|
139
|
+
"omem",
|
140
|
+
"events",
|
141
|
+
"cmd"
|
142
|
+
]
|
143
|
+
|
144
|
+
clients = r.client(:list)
|
145
|
+
clients.each do |client|
|
146
|
+
keys.each do |k|
|
147
|
+
msg = "expected #client(:list) to include #{k}"
|
148
|
+
assert client.keys.include?(k), msg
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def test_client_kill
|
154
|
+
return if version < "2.6.9"
|
155
|
+
|
156
|
+
r.client(:setname, 'redis-rb')
|
157
|
+
clients = r.client(:list)
|
158
|
+
i = clients.index {|client| client['name'] == 'redis-rb'}
|
159
|
+
assert_equal "OK", r.client(:kill, clients[i]["addr"])
|
160
|
+
|
161
|
+
clients = r.client(:list)
|
162
|
+
i = clients.index {|client| client['name'] == 'redis-rb'}
|
163
|
+
assert_equal nil, i
|
164
|
+
end
|
165
|
+
|
166
|
+
def test_client_getname_and_setname
|
167
|
+
return if version < "2.6.9"
|
168
|
+
|
169
|
+
assert_equal nil, r.client(:getname)
|
170
|
+
|
171
|
+
r.client(:setname, 'redis-rb')
|
172
|
+
name = r.client(:getname)
|
173
|
+
assert_equal 'redis-rb', name
|
174
|
+
end
|
118
175
|
end
|
data/test/scanning_test.rb
CHANGED
data/test/scripting_test.rb
CHANGED
data/test/sentinel_test.rb
CHANGED
data/test/sorting_test.rb
CHANGED
data/test/ssl_test.rb
CHANGED
@@ -1,73 +1,69 @@
|
|
1
|
-
|
1
|
+
require_relative "helper"
|
2
2
|
|
3
|
-
|
4
|
-
require File.expand_path("helper", File.dirname(__FILE__))
|
3
|
+
class SslTest < Test::Unit::TestCase
|
5
4
|
|
6
|
-
|
5
|
+
include Helper::Client
|
7
6
|
|
8
|
-
|
7
|
+
driver(:ruby) do
|
9
8
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
redis = Redis.new(:port => port, :ssl => true, :ssl_params => { :ca_file => ssl_ca_file })
|
15
|
-
assert_equal redis.ping, "PONG"
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
def test_unverified_ssl_connection
|
20
|
-
assert_raise(OpenSSL::SSL::SSLError) do
|
21
|
-
RedisMock.start({ :ping => proc { "+PONG" } }, ssl_server_opts("untrusted")) do |port|
|
22
|
-
redis = Redis.new(:port => port, :ssl => true, :ssl_params => { :ca_file => ssl_ca_file })
|
23
|
-
redis.ping
|
24
|
-
end
|
25
|
-
end
|
9
|
+
def test_verified_ssl_connection
|
10
|
+
RedisMock.start({ :ping => proc { "+PONG" } }, ssl_server_opts("trusted")) do |port|
|
11
|
+
redis = Redis.new(:port => port, :ssl => true, :ssl_params => { :ca_file => ssl_ca_file })
|
12
|
+
assert_equal redis.ping, "PONG"
|
26
13
|
end
|
14
|
+
end
|
27
15
|
|
28
|
-
|
29
|
-
|
16
|
+
def test_unverified_ssl_connection
|
17
|
+
assert_raise(OpenSSL::SSL::SSLError) do
|
18
|
+
RedisMock.start({ :ping => proc { "+PONG" } }, ssl_server_opts("untrusted")) do |port|
|
30
19
|
redis = Redis.new(:port => port, :ssl => true, :ssl_params => { :ca_file => ssl_ca_file })
|
31
|
-
|
20
|
+
redis.ping
|
32
21
|
end
|
33
22
|
end
|
23
|
+
end
|
34
24
|
|
25
|
+
def test_ssl_blocking
|
26
|
+
RedisMock.start({}, ssl_server_opts("trusted")) do |port|
|
27
|
+
redis = Redis.new(:port => port, :ssl => true, :ssl_params => { :ca_file => ssl_ca_file })
|
28
|
+
assert_equal redis.set("boom", "a" * 10_000_000), "OK"
|
29
|
+
end
|
35
30
|
end
|
36
31
|
|
37
|
-
|
32
|
+
end
|
33
|
+
|
34
|
+
driver(:hiredis, :synchrony) do
|
38
35
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
end
|
36
|
+
def test_ssl_not_implemented_exception
|
37
|
+
assert_raise(NotImplementedError) do
|
38
|
+
RedisMock.start({ :ping => proc { "+PONG" } }, ssl_server_opts("trusted")) do |port|
|
39
|
+
redis = Redis.new(:port => port, :ssl => true, :ssl_params => { :ca_file => ssl_ca_file })
|
40
|
+
redis.ping
|
45
41
|
end
|
46
42
|
end
|
47
|
-
|
48
43
|
end
|
49
44
|
|
50
|
-
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
51
48
|
|
52
|
-
|
53
|
-
|
54
|
-
|
49
|
+
def ssl_server_opts(prefix)
|
50
|
+
ssl_cert = File.join(cert_path, "#{prefix}-cert.crt")
|
51
|
+
ssl_key = File.join(cert_path, "#{prefix}-cert.key")
|
55
52
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
}
|
53
|
+
{
|
54
|
+
:ssl => true,
|
55
|
+
:ssl_params => {
|
56
|
+
:cert => OpenSSL::X509::Certificate.new(File.read(ssl_cert)),
|
57
|
+
:key => OpenSSL::PKey::RSA.new(File.read(ssl_key))
|
62
58
|
}
|
63
|
-
|
59
|
+
}
|
60
|
+
end
|
64
61
|
|
65
|
-
|
66
|
-
|
67
|
-
|
62
|
+
def ssl_ca_file
|
63
|
+
File.join(cert_path, "trusted-ca.crt")
|
64
|
+
end
|
68
65
|
|
69
|
-
|
70
|
-
|
71
|
-
end
|
66
|
+
def cert_path
|
67
|
+
File.expand_path("../support/ssl/", __FILE__)
|
72
68
|
end
|
73
69
|
end
|
@@ -0,0 +1,199 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../../lib/redis'
|
4
|
+
|
5
|
+
class ClusterOrchestrator
|
6
|
+
SLOT_SIZE = 16384
|
7
|
+
|
8
|
+
def initialize(node_addrs)
|
9
|
+
raise 'Redis Cluster requires at least 3 master nodes.' if node_addrs.size < 3
|
10
|
+
@clients = node_addrs.map { |addr| Redis.new(url: addr) }
|
11
|
+
end
|
12
|
+
|
13
|
+
def rebuild
|
14
|
+
flush_all_data(@clients)
|
15
|
+
reset_cluster(@clients)
|
16
|
+
assign_slots(@clients)
|
17
|
+
save_config_epoch(@clients)
|
18
|
+
meet_each_other(@clients)
|
19
|
+
wait_meeting(@clients)
|
20
|
+
replicate(@clients)
|
21
|
+
save_config(@clients)
|
22
|
+
wait_cluster_building(@clients)
|
23
|
+
sleep 3
|
24
|
+
end
|
25
|
+
|
26
|
+
def down
|
27
|
+
flush_all_data(@clients)
|
28
|
+
reset_cluster(@clients)
|
29
|
+
end
|
30
|
+
|
31
|
+
def failover
|
32
|
+
take_slaves(@clients).last.cluster(:failover, :takeover)
|
33
|
+
sleep 3
|
34
|
+
end
|
35
|
+
|
36
|
+
def start_resharding(slot, src_node_key, dest_node_key)
|
37
|
+
node_map = hashify_node_map(@clients.first)
|
38
|
+
src_node_id = node_map.fetch(src_node_key)
|
39
|
+
src_client = find_client(@clients, src_node_key)
|
40
|
+
dest_node_id = node_map.fetch(dest_node_key)
|
41
|
+
dest_client = find_client(@clients, dest_node_key)
|
42
|
+
dest_host, dest_port = dest_node_key.split(':')
|
43
|
+
|
44
|
+
dest_client.cluster(:setslot, slot, 'IMPORTING', src_node_id)
|
45
|
+
src_client.cluster(:setslot, slot, 'MIGRATING', dest_node_id)
|
46
|
+
|
47
|
+
loop do
|
48
|
+
keys = src_client.cluster(:getkeysinslot, slot, 100)
|
49
|
+
break if keys.empty?
|
50
|
+
keys.each { |k| src_client.migrate(k, host: dest_host, port: dest_port) }
|
51
|
+
sleep 0.1
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def finish_resharding(slot, dest_node_key)
|
56
|
+
node_map = hashify_node_map(@clients.first)
|
57
|
+
@clients.first.cluster(:setslot, slot, 'NODE', node_map.fetch(dest_node_key))
|
58
|
+
end
|
59
|
+
|
60
|
+
def close
|
61
|
+
@clients.each(&:quit)
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def flush_all_data(clients)
|
67
|
+
clients.each do |c|
|
68
|
+
begin
|
69
|
+
c.flushall
|
70
|
+
rescue Redis::CommandError
|
71
|
+
# READONLY You can't write against a read only slave.
|
72
|
+
nil
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def reset_cluster(clients)
|
78
|
+
clients.each { |c| c.cluster(:reset) }
|
79
|
+
end
|
80
|
+
|
81
|
+
def assign_slots(clients)
|
82
|
+
masters = take_masters(clients)
|
83
|
+
slot_slice = SLOT_SIZE / masters.size
|
84
|
+
mod = SLOT_SIZE % masters.size
|
85
|
+
slot_sizes = Array.new(masters.size, slot_slice)
|
86
|
+
mod.downto(1) { |i| slot_sizes[i] += 1 }
|
87
|
+
|
88
|
+
slot_idx = 0
|
89
|
+
masters.zip(slot_sizes).each do |c, s|
|
90
|
+
slot_range = slot_idx..slot_idx + s - 1
|
91
|
+
c.cluster(:addslots, *slot_range.to_a)
|
92
|
+
slot_idx += s
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def save_config_epoch(clients)
|
97
|
+
clients.each_with_index do |c, i|
|
98
|
+
begin
|
99
|
+
c.cluster('set-config-epoch', i + 1)
|
100
|
+
rescue Redis::CommandError
|
101
|
+
# ERR Node config epoch is already non-zero
|
102
|
+
nil
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def meet_each_other(clients)
|
108
|
+
first_cliient = clients.first
|
109
|
+
target_info = first_cliient.connection
|
110
|
+
target_host = target_info.fetch(:host)
|
111
|
+
target_port = target_info.fetch(:port)
|
112
|
+
|
113
|
+
clients.each do |client|
|
114
|
+
next if first_cliient.id == client.id
|
115
|
+
client.cluster(:meet, target_host, target_port)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def wait_meeting(clients)
|
120
|
+
first_cliient = clients.first
|
121
|
+
size = clients.size
|
122
|
+
|
123
|
+
loop do
|
124
|
+
info = hashify_cluster_info(first_cliient)
|
125
|
+
break if info['cluster_known_nodes'].to_i == size
|
126
|
+
sleep 0.1
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def replicate(clients)
|
131
|
+
node_map = hashify_node_map(clients.first)
|
132
|
+
masters = take_masters(clients)
|
133
|
+
|
134
|
+
take_slaves(clients).each_with_index do |slave, i|
|
135
|
+
master_info = masters[i].connection
|
136
|
+
master_host = master_info.fetch(:host)
|
137
|
+
master_port = master_info.fetch(:port)
|
138
|
+
|
139
|
+
loop do
|
140
|
+
begin
|
141
|
+
master_node_id = node_map.fetch("#{master_host}:#{master_port}")
|
142
|
+
slave.cluster(:replicate, master_node_id)
|
143
|
+
rescue Redis::CommandError
|
144
|
+
# ERR Unknown node [key]
|
145
|
+
sleep 0.1
|
146
|
+
node_map = hashify_node_map(clients.first)
|
147
|
+
next
|
148
|
+
end
|
149
|
+
|
150
|
+
break
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def save_config(clients)
|
156
|
+
clients.each { |c| c.cluster(:saveconfig) }
|
157
|
+
end
|
158
|
+
|
159
|
+
def wait_cluster_building(clients)
|
160
|
+
first_cliient = clients.first
|
161
|
+
|
162
|
+
loop do
|
163
|
+
info = hashify_cluster_info(first_cliient)
|
164
|
+
break if info['cluster_state'] == 'ok'
|
165
|
+
sleep 0.1
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def hashify_cluster_info(client)
|
170
|
+
client.cluster(:info).split("\r\n").map { |str| str.split(':') }.to_h
|
171
|
+
end
|
172
|
+
|
173
|
+
def hashify_node_map(client)
|
174
|
+
client.cluster(:nodes)
|
175
|
+
.split("\n")
|
176
|
+
.map { |str| str.split(' ') }
|
177
|
+
.map { |arr| [arr[1].split('@').first, arr[0]] }
|
178
|
+
.to_h
|
179
|
+
end
|
180
|
+
|
181
|
+
def take_masters(clients)
|
182
|
+
size = clients.size / 2
|
183
|
+
return clients if size < 3
|
184
|
+
clients.take(size)
|
185
|
+
end
|
186
|
+
|
187
|
+
def take_slaves(clients)
|
188
|
+
size = clients.size / 2
|
189
|
+
return [] if size < 3
|
190
|
+
clients[size..size * 2]
|
191
|
+
end
|
192
|
+
|
193
|
+
def find_client(clients, node_key)
|
194
|
+
clients.find do |cli|
|
195
|
+
con = cli.connection
|
196
|
+
node_key == "#{con.fetch(:host)}:#{con.fetch(:port)}"
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
require_relative "../wire/thread"
|
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
require_relative "../wire/thread"
|
data/test/support/redis_mock.rb
CHANGED
data/test/synchrony_driver.rb
CHANGED
@@ -1,13 +1,10 @@
|
|
1
|
-
|
1
|
+
require "em-synchrony"
|
2
|
+
require "em-synchrony/connection_pool"
|
2
3
|
|
3
|
-
|
4
|
-
|
4
|
+
require_relative "../lib/redis"
|
5
|
+
require_relative "../lib/redis/connection/synchrony"
|
5
6
|
|
6
|
-
|
7
|
-
require 'redis/connection/synchrony'
|
8
|
-
|
9
|
-
|
10
|
-
require File.expand_path("./helper", File.dirname(__FILE__))
|
7
|
+
require_relative "helper"
|
11
8
|
|
12
9
|
PORT = 6381
|
13
10
|
OPTIONS = {:port => PORT, :db => 15}
|
@@ -55,7 +52,7 @@ EM.synchrony do
|
|
55
52
|
assert_equal "s2", r.lpop("foo")
|
56
53
|
assert_equal "s1", r.lpop("foo")
|
57
54
|
|
58
|
-
assert_equal "OK", r.
|
55
|
+
assert_equal "OK", r._client.call(:quit)
|
59
56
|
assert_equal "PONG", r.ping
|
60
57
|
|
61
58
|
|
data/test/thread_safety_test.rb
CHANGED
data/test/transactions_test.rb
CHANGED
@@ -1,6 +1,4 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require File.expand_path("helper", File.dirname(__FILE__))
|
1
|
+
require_relative "helper"
|
4
2
|
|
5
3
|
class TestTransactions < Test::Unit::TestCase
|
6
4
|
|
@@ -116,6 +114,16 @@ class TestTransactions < Test::Unit::TestCase
|
|
116
114
|
assert_equal "s1", r.get("foo")
|
117
115
|
end
|
118
116
|
|
117
|
+
def test_empty_multi_exec
|
118
|
+
result = nil
|
119
|
+
|
120
|
+
redis_mock(:exec => lambda { |*_| "-ERROR" }) do |redis|
|
121
|
+
result = redis.multi {}
|
122
|
+
end
|
123
|
+
|
124
|
+
assert_equal [], result
|
125
|
+
end
|
126
|
+
|
119
127
|
def test_raise_command_errors_when_accessing_futures_after_multi_exec
|
120
128
|
begin
|
121
129
|
r.multi do |m|
|