redis 4.0.2 → 4.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/.travis.yml +2 -2
- data/CHANGELOG.md +6 -0
- data/lib/redis.rb +97 -11
- data/lib/redis/client.rb +19 -11
- data/lib/redis/cluster.rb +285 -0
- 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/errors.rb +46 -0
- data/lib/redis/version.rb +1 -1
- data/makefile +54 -16
- data/redis.gemspec +2 -1
- data/test/client_test.rb +17 -0
- 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/commands_on_hashes_test.rb +2 -14
- data/test/commands_on_hyper_log_log_test.rb +2 -14
- data/test/commands_on_lists_test.rb +2 -13
- data/test/commands_on_sets_test.rb +2 -70
- data/test/commands_on_sorted_sets_test.rb +2 -145
- data/test/commands_on_strings_test.rb +2 -94
- data/test/distributed_blocking_commands_test.rb +8 -0
- data/test/distributed_commands_on_hashes_test.rb +16 -3
- data/test/distributed_commands_on_hyper_log_log_test.rb +8 -13
- data/test/distributed_commands_on_lists_test.rb +4 -5
- data/test/distributed_commands_on_sets_test.rb +45 -46
- data/test/distributed_commands_on_sorted_sets_test.rb +51 -8
- data/test/distributed_commands_on_strings_test.rb +10 -0
- data/test/helper.rb +176 -32
- data/test/internals_test.rb +13 -0
- data/test/lint/blocking_commands.rb +40 -16
- data/test/lint/hashes.rb +26 -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 +102 -0
- data/test/support/cluster/orchestrator.rb +199 -0
- metadata +79 -4
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../errors'
|
4
|
+
require_relative 'node_key'
|
5
|
+
|
6
|
+
class Redis
|
7
|
+
class Cluster
|
8
|
+
# Load and hashify slot info for Redis Cluster Client
|
9
|
+
module SlotLoader
|
10
|
+
module_function
|
11
|
+
|
12
|
+
def load(nodes)
|
13
|
+
info = {}
|
14
|
+
|
15
|
+
nodes.each do |node|
|
16
|
+
info = Hash[*fetch_slot_info(node)]
|
17
|
+
info.empty? ? next : break
|
18
|
+
end
|
19
|
+
|
20
|
+
return info unless info.empty?
|
21
|
+
|
22
|
+
raise CannotConnectError, 'Redis client could not connect to any cluster nodes'
|
23
|
+
end
|
24
|
+
|
25
|
+
def fetch_slot_info(node)
|
26
|
+
node.call(%i[cluster slots])
|
27
|
+
.map { |arr| parse_slot_info(arr, default_ip: node.host) }
|
28
|
+
.flatten
|
29
|
+
rescue CannotConnectError, ConnectionError, CommandError
|
30
|
+
{} # can retry on another node
|
31
|
+
end
|
32
|
+
|
33
|
+
def parse_slot_info(arr, default_ip:)
|
34
|
+
first_slot, last_slot = arr[0..1]
|
35
|
+
slot_range = (first_slot..last_slot).freeze
|
36
|
+
arr[2..-1].map { |addr| [stringify_node_key(addr, default_ip), slot_range] }
|
37
|
+
.flatten
|
38
|
+
end
|
39
|
+
|
40
|
+
def stringify_node_key(arr, default_ip)
|
41
|
+
ip, port = arr
|
42
|
+
ip = default_ip if ip.empty? # When cluster is down
|
43
|
+
NodeKey.build_from_host_port(ip, port)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/redis/errors.rb
CHANGED
@@ -37,4 +37,50 @@ class Redis
|
|
37
37
|
# Raised when the connection was inherited by a child process.
|
38
38
|
class InheritedError < BaseConnectionError
|
39
39
|
end
|
40
|
+
|
41
|
+
# Raised when client options are invalid.
|
42
|
+
class InvalidClientOptionError < BaseError
|
43
|
+
end
|
44
|
+
|
45
|
+
class Cluster
|
46
|
+
# Raised when client connected to redis as cluster mode
|
47
|
+
# and some cluster subcommands were called.
|
48
|
+
class OrchestrationCommandNotSupported < BaseError
|
49
|
+
def initialize(command, subcommand = '')
|
50
|
+
str = [command, subcommand].map(&:to_s).reject(&:empty?).join(' ').upcase
|
51
|
+
msg = "#{str} command should be used with care "\
|
52
|
+
'only by applications orchestrating Redis Cluster, like redis-trib, '\
|
53
|
+
'and the command if used out of the right context can leave the cluster '\
|
54
|
+
'in a wrong state or cause data loss.'
|
55
|
+
super(msg)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Raised when error occurs on any node of cluster.
|
60
|
+
class CommandErrorCollection < BaseError
|
61
|
+
attr_reader :errors
|
62
|
+
|
63
|
+
# @param errors [Hash{String => Redis::CommandError}]
|
64
|
+
# @param error_message [String]
|
65
|
+
def initialize(errors, error_message = 'Command errors were replied on any node')
|
66
|
+
@errors = errors
|
67
|
+
super(error_message)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Raised when cluster client can't select node.
|
72
|
+
class AmbiguousNodeError < BaseError
|
73
|
+
def initialize(command)
|
74
|
+
super("Cluster client doesn't know which node the #{command} command should be sent to.")
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Raised when commands in pipelining include cross slot keys.
|
79
|
+
class CrossSlotPipeliningError < BaseError
|
80
|
+
def initialize(keys)
|
81
|
+
super("Cluster client couldn't send pipelining to single node. "\
|
82
|
+
"The commands include cross slot keys. #{keys}")
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
40
86
|
end
|
data/lib/redis/version.rb
CHANGED
data/makefile
CHANGED
@@ -1,27 +1,43 @@
|
|
1
|
-
TEST_FILES
|
2
|
-
REDIS_BRANCH
|
3
|
-
TMP
|
4
|
-
BUILD_DIR
|
5
|
-
TARBALL
|
6
|
-
BINARY
|
7
|
-
|
8
|
-
|
9
|
-
|
1
|
+
TEST_FILES := $(shell find ./test -name *_test.rb -type f)
|
2
|
+
REDIS_BRANCH ?= unstable
|
3
|
+
TMP := tmp
|
4
|
+
BUILD_DIR := ${TMP}/cache/redis-${REDIS_BRANCH}
|
5
|
+
TARBALL := ${TMP}/redis-${REDIS_BRANCH}.tar.gz
|
6
|
+
BINARY := ${BUILD_DIR}/src/redis-server
|
7
|
+
REDIS_CLIENT := ${BUILD_DIR}/src/redis-cli
|
8
|
+
REDIS_TRIB := ${BUILD_DIR}/src/redis-trib.rb
|
9
|
+
PID_PATH := ${BUILD_DIR}/redis.pid
|
10
|
+
SOCKET_PATH := ${BUILD_DIR}/redis.sock
|
11
|
+
PORT := 6381
|
12
|
+
CLUSTER_PORTS := 7000 7001 7002 7003 7004 7005
|
13
|
+
CLUSTER_PID_PATHS := $(addprefix ${TMP}/redis,$(addsuffix .pid,${CLUSTER_PORTS}))
|
14
|
+
CLUSTER_CONF_PATHS := $(addprefix ${TMP}/nodes,$(addsuffix .conf,${CLUSTER_PORTS}))
|
15
|
+
CLUSTER_ADDRS := $(addprefix 127.0.0.1:,${CLUSTER_PORTS})
|
10
16
|
|
11
|
-
|
17
|
+
define kill-redis
|
18
|
+
(ls $1 2> /dev/null && kill $$(cat $1) && rm -f $1) || true
|
19
|
+
endef
|
20
|
+
|
21
|
+
all:
|
12
22
|
make start
|
13
|
-
|
14
|
-
|
23
|
+
make start_cluster
|
24
|
+
make create_cluster
|
25
|
+
make test
|
15
26
|
make stop
|
27
|
+
make stop_cluster
|
16
28
|
|
17
29
|
${TMP}:
|
18
|
-
mkdir $@
|
30
|
+
mkdir -p $@
|
19
31
|
|
20
32
|
${BINARY}: ${TMP}
|
21
|
-
bin/build ${REDIS_BRANCH}
|
33
|
+
bin/build ${REDIS_BRANCH} $<
|
34
|
+
|
35
|
+
test: ${TEST_FILES}
|
36
|
+
env SOCKET_PATH=${SOCKET_PATH} \
|
37
|
+
bundle exec ruby -v -e 'ARGV.each { |test_file| require test_file }' ${TEST_FILES}
|
22
38
|
|
23
39
|
stop:
|
24
|
-
(
|
40
|
+
$(call kill-redis,${PID_PATH})
|
25
41
|
|
26
42
|
start: ${BINARY}
|
27
43
|
${BINARY} \
|
@@ -30,7 +46,29 @@ start: ${BINARY}
|
|
30
46
|
--port ${PORT} \
|
31
47
|
--unixsocket ${SOCKET_PATH}
|
32
48
|
|
49
|
+
stop_cluster:
|
50
|
+
$(call kill-redis,${CLUSTER_PID_PATHS})
|
51
|
+
rm -f appendonly.aof || true
|
52
|
+
rm -f ${CLUSTER_CONF_PATHS} || true
|
53
|
+
|
54
|
+
start_cluster: ${BINARY}
|
55
|
+
for port in ${CLUSTER_PORTS}; do \
|
56
|
+
${BINARY} \
|
57
|
+
--daemonize yes \
|
58
|
+
--appendonly yes \
|
59
|
+
--cluster-enabled yes \
|
60
|
+
--cluster-config-file ${TMP}/nodes$$port.conf \
|
61
|
+
--cluster-node-timeout 5000 \
|
62
|
+
--pidfile ${TMP}/redis$$port.pid \
|
63
|
+
--port $$port \
|
64
|
+
--unixsocket ${TMP}/redis$$port.sock; \
|
65
|
+
done
|
66
|
+
|
67
|
+
create_cluster:
|
68
|
+
yes yes | ((bundle exec ruby ${REDIS_TRIB} create --replicas 1 ${CLUSTER_ADDRS}) || \
|
69
|
+
(${REDIS_CLIENT} --cluster create ${CLUSTER_ADDRS} --cluster-replicas 1))
|
70
|
+
|
33
71
|
clean:
|
34
72
|
(test -d ${BUILD_DIR} && cd ${BUILD_DIR}/src && make clean distclean) || true
|
35
73
|
|
36
|
-
.PHONY: test start
|
74
|
+
.PHONY: all test stop start stop_cluster start_cluster create_cluster clean
|
data/redis.gemspec
CHANGED
@@ -32,11 +32,12 @@ Gem::Specification.new do |s|
|
|
32
32
|
|
33
33
|
s.files = `git ls-files`.split("\n")
|
34
34
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
35
|
-
s.executables = `git ls-files --
|
35
|
+
s.executables = `git ls-files -- exe/*`.split("\n").map{ |f| File.basename(f) }
|
36
36
|
|
37
37
|
s.required_ruby_version = '>= 2.2.2'
|
38
38
|
|
39
39
|
s.add_development_dependency("test-unit", ">= 3.1.5")
|
40
|
+
s.add_development_dependency("mocha")
|
40
41
|
s.add_development_dependency("hiredis")
|
41
42
|
s.add_development_dependency("em-synchrony")
|
42
43
|
end
|
data/test/client_test.rb
CHANGED
@@ -56,4 +56,21 @@ class TestClient < Test::Unit::TestCase
|
|
56
56
|
|
57
57
|
assert_equal result, ["OK", 1]
|
58
58
|
end
|
59
|
+
|
60
|
+
def test_client_with_custom_connector
|
61
|
+
custom_connector = Class.new(Redis::Client::Connector) do
|
62
|
+
def resolve
|
63
|
+
@options[:host] = '127.0.0.5'
|
64
|
+
@options[:port] = '999'
|
65
|
+
@options
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
assert_raise_message(
|
70
|
+
'Error connecting to Redis on 127.0.0.5:999 (Errno::ECONNREFUSED)'
|
71
|
+
) do
|
72
|
+
new_redis = _new_client(connector: custom_connector)
|
73
|
+
new_redis.ping
|
74
|
+
end
|
75
|
+
end
|
59
76
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'helper'
|
4
|
+
|
5
|
+
# ruby -w -Itest test/cluster_abnormal_state_test.rb
|
6
|
+
class TestClusterAbnormalState < Test::Unit::TestCase
|
7
|
+
include Helper::Cluster
|
8
|
+
|
9
|
+
def test_the_state_of_cluster_down
|
10
|
+
redis_cluster_down do
|
11
|
+
assert_raise(Redis::CommandError, 'CLUSTERDOWN Hash slot not served') do
|
12
|
+
redis.set('key1', 1)
|
13
|
+
end
|
14
|
+
|
15
|
+
assert_equal 'fail', redis.cluster(:info).fetch('cluster_state')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_the_state_of_cluster_failover
|
20
|
+
redis_cluster_failover do
|
21
|
+
100.times do |i|
|
22
|
+
assert_equal 'OK', r.set("key#{i}", i)
|
23
|
+
end
|
24
|
+
|
25
|
+
100.times do |i|
|
26
|
+
assert_equal i.to_s, r.get("key#{i}")
|
27
|
+
end
|
28
|
+
|
29
|
+
assert_equal 'ok', redis.cluster(:info).fetch('cluster_state')
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_raising_error_when_nodes_are_not_cluster_mode
|
34
|
+
assert_raise(Redis::CannotConnectError, 'Redis client could not connect to any cluster nodes') do
|
35
|
+
build_another_client(cluster: %W[redis://127.0.0.1:#{PORT}])
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'helper'
|
4
|
+
require_relative 'lint/blocking_commands'
|
5
|
+
|
6
|
+
# ruby -w -Itest test/cluster_blocking_commands_test.rb
|
7
|
+
class TestClusterBlockingCommands < Test::Unit::TestCase
|
8
|
+
include Helper::Cluster
|
9
|
+
include Lint::BlockingCommands
|
10
|
+
|
11
|
+
def mock(options = {}, &blk)
|
12
|
+
commands = build_mock_commands(options)
|
13
|
+
redis_cluster_mock(commands, &blk)
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'helper'
|
4
|
+
|
5
|
+
# ruby -w -Itest test/cluster_client_internals_test.rb
|
6
|
+
class TestClusterClientInternals < Test::Unit::TestCase
|
7
|
+
include Helper::Cluster
|
8
|
+
|
9
|
+
def test_handle_multiple_servers
|
10
|
+
100.times { |i| redis.set(i.to_s, "hogehoge#{i}") }
|
11
|
+
100.times { |i| assert_equal "hogehoge#{i}", redis.get(i.to_s) }
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_info_of_cluster_mode_is_enabled
|
15
|
+
assert_equal '1', redis.info['cluster_enabled']
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_unknown_commands_does_not_work_by_default
|
19
|
+
assert_raise(Redis::CommandError) do
|
20
|
+
redis.not_yet_implemented_command('boo', 'foo')
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_with_reconnect
|
25
|
+
assert_equal('Hello World', redis.with_reconnect { 'Hello World' })
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_without_reconnect
|
29
|
+
assert_equal('Hello World', redis.without_reconnect { 'Hello World' })
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_connected?
|
33
|
+
assert_equal true, redis.connected?
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_close
|
37
|
+
assert_equal true, redis.close
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_disconnect!
|
41
|
+
assert_equal true, redis.disconnect!
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_asking
|
45
|
+
assert_equal 'OK', redis.asking
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_id
|
49
|
+
expected = 'redis://127.0.0.1:7000/0 '\
|
50
|
+
'redis://127.0.0.1:7001/0 '\
|
51
|
+
'redis://127.0.0.1:7002/0'
|
52
|
+
assert_equal expected, redis.id
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_inspect
|
56
|
+
expected = "#<Redis client v#{Redis::VERSION} for "\
|
57
|
+
'redis://127.0.0.1:7000/0 '\
|
58
|
+
'redis://127.0.0.1:7001/0 '\
|
59
|
+
'redis://127.0.0.1:7002/0>'
|
60
|
+
|
61
|
+
assert_equal expected, redis.inspect
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_dup
|
65
|
+
assert_instance_of Redis, redis.dup
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_connection
|
69
|
+
expected = [
|
70
|
+
{ host: '127.0.0.1', port: 7000, db: 0, id: 'redis://127.0.0.1:7000/0', location: '127.0.0.1:7000' },
|
71
|
+
{ host: '127.0.0.1', port: 7001, db: 0, id: 'redis://127.0.0.1:7001/0', location: '127.0.0.1:7001' },
|
72
|
+
{ host: '127.0.0.1', port: 7002, db: 0, id: 'redis://127.0.0.1:7002/0', location: '127.0.0.1:7002' }
|
73
|
+
]
|
74
|
+
|
75
|
+
assert_equal expected, redis.connection
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'helper'
|
4
|
+
|
5
|
+
# ruby -w -Itest test/cluster_client_key_hash_tags_test.rb
|
6
|
+
class TestClusterClientKeyHashTags < Test::Unit::TestCase
|
7
|
+
include Helper::Cluster
|
8
|
+
|
9
|
+
def build_described_class
|
10
|
+
option = Redis::Cluster::Option.new(cluster: ['redis://127.0.0.1:7000'])
|
11
|
+
node = Redis::Cluster::Node.new(option.per_node_key)
|
12
|
+
details = Redis::Cluster::CommandLoader.load(node)
|
13
|
+
Redis::Cluster::Command.new(details)
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_key_extraction
|
17
|
+
described_class = build_described_class
|
18
|
+
|
19
|
+
assert_equal 'dogs:1', described_class.extract_first_key(%w[get dogs:1])
|
20
|
+
assert_equal 'user1000', described_class.extract_first_key(%w[get {user1000}.following])
|
21
|
+
assert_equal 'user1000', described_class.extract_first_key(%w[get {user1000}.followers])
|
22
|
+
assert_equal 'foo{}{bar}', described_class.extract_first_key(%w[get foo{}{bar}])
|
23
|
+
assert_equal '{bar', described_class.extract_first_key(%w[get foo{{bar}}zap])
|
24
|
+
assert_equal 'bar', described_class.extract_first_key(%w[get foo{bar}{zap}])
|
25
|
+
|
26
|
+
assert_equal '', described_class.extract_first_key([:get, ''])
|
27
|
+
assert_equal '', described_class.extract_first_key([:get, nil])
|
28
|
+
assert_equal '', described_class.extract_first_key([:get])
|
29
|
+
|
30
|
+
assert_equal '', described_class.extract_first_key([:set, '', 1])
|
31
|
+
assert_equal '', described_class.extract_first_key([:set, nil, 1])
|
32
|
+
assert_equal '', described_class.extract_first_key([:set])
|
33
|
+
|
34
|
+
# Keyless commands
|
35
|
+
assert_equal '', described_class.extract_first_key([:auth, 'password'])
|
36
|
+
assert_equal '', described_class.extract_first_key(%i[client kill])
|
37
|
+
assert_equal '', described_class.extract_first_key(%i[cluster addslots])
|
38
|
+
assert_equal '', described_class.extract_first_key(%i[command])
|
39
|
+
assert_equal '', described_class.extract_first_key(%i[command count])
|
40
|
+
assert_equal '', described_class.extract_first_key(%i[config get])
|
41
|
+
assert_equal '', described_class.extract_first_key(%i[debug segfault])
|
42
|
+
assert_equal '', described_class.extract_first_key([:echo, 'Hello World'])
|
43
|
+
assert_equal '', described_class.extract_first_key([:flushall, 'ASYNC'])
|
44
|
+
assert_equal '', described_class.extract_first_key([:flushdb, 'ASYNC'])
|
45
|
+
assert_equal '', described_class.extract_first_key([:info, 'cluster'])
|
46
|
+
assert_equal '', described_class.extract_first_key(%i[memory doctor])
|
47
|
+
assert_equal '', described_class.extract_first_key([:ping, 'Hi'])
|
48
|
+
assert_equal '', described_class.extract_first_key([:psubscribe, 'channel'])
|
49
|
+
assert_equal '', described_class.extract_first_key([:pubsub, 'channels', '*'])
|
50
|
+
assert_equal '', described_class.extract_first_key([:publish, 'channel', 'Hi'])
|
51
|
+
assert_equal '', described_class.extract_first_key([:punsubscribe, 'channel'])
|
52
|
+
assert_equal '', described_class.extract_first_key([:subscribe, 'channel'])
|
53
|
+
assert_equal '', described_class.extract_first_key([:unsubscribe, 'channel'])
|
54
|
+
assert_equal '', described_class.extract_first_key(%w[script exists sha1 sha1])
|
55
|
+
assert_equal '', described_class.extract_first_key([:select, 1])
|
56
|
+
assert_equal '', described_class.extract_first_key([:shutdown, 'SAVE'])
|
57
|
+
assert_equal '', described_class.extract_first_key([:slaveof, '127.0.0.1', 6379])
|
58
|
+
assert_equal '', described_class.extract_first_key([:slowlog, 'get', 2])
|
59
|
+
assert_equal '', described_class.extract_first_key([:swapdb, 0, 1])
|
60
|
+
assert_equal '', described_class.extract_first_key([:wait, 1, 0])
|
61
|
+
|
62
|
+
# 2nd argument is not a key
|
63
|
+
assert_equal 'key1', described_class.extract_first_key([:eval, 'script', 2, 'key1', 'key2', 'first', 'second'])
|
64
|
+
assert_equal '', described_class.extract_first_key([:eval, 'return 0', 0])
|
65
|
+
assert_equal 'key1', described_class.extract_first_key([:evalsha, 'sha1', 2, 'key1', 'key2', 'first', 'second'])
|
66
|
+
assert_equal '', described_class.extract_first_key([:evalsha, 'return 0', 0])
|
67
|
+
assert_equal 'key1', described_class.extract_first_key([:migrate, '127.0.0.1', 6379, 'key1', 0, 5000])
|
68
|
+
assert_equal 'key1', described_class.extract_first_key([:memory, :usage, 'key1'])
|
69
|
+
assert_equal 'key1', described_class.extract_first_key([:object, 'refcount', 'key1'])
|
70
|
+
assert_equal 'mystream', described_class.extract_first_key([:xread, 'COUNT', 2, 'STREAMS', 'mystream', 0])
|
71
|
+
assert_equal 'mystream', described_class.extract_first_key([:xreadgroup, 'GROUP', 'mygroup', 'Bob', 'COUNT', 2, 'STREAMS', 'mystream', '>'])
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_whether_the_command_effect_is_readonly_or_not
|
75
|
+
described_class = build_described_class
|
76
|
+
|
77
|
+
assert_equal true, described_class.should_send_to_master?([:set])
|
78
|
+
assert_equal false, described_class.should_send_to_slave?([:set])
|
79
|
+
|
80
|
+
assert_equal false, described_class.should_send_to_master?([:get])
|
81
|
+
assert_equal true, described_class.should_send_to_slave?([:get])
|
82
|
+
|
83
|
+
target_version('3.2.0') do
|
84
|
+
assert_equal false, described_class.should_send_to_master?([:info])
|
85
|
+
assert_equal false, described_class.should_send_to_slave?([:info])
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|