redis 3.3.5 → 4.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (127) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/.travis/Gemfile +8 -1
  4. data/.travis.yml +34 -62
  5. data/CHANGELOG.md +45 -2
  6. data/Gemfile +5 -1
  7. data/README.md +32 -76
  8. data/benchmarking/logging.rb +1 -1
  9. data/bin/build +71 -0
  10. data/bors.toml +14 -0
  11. data/lib/redis/client.rb +38 -20
  12. data/lib/redis/cluster/command.rb +81 -0
  13. data/lib/redis/cluster/command_loader.rb +32 -0
  14. data/lib/redis/cluster/key_slot_converter.rb +72 -0
  15. data/lib/redis/cluster/node.rb +104 -0
  16. data/lib/redis/cluster/node_key.rb +35 -0
  17. data/lib/redis/cluster/node_loader.rb +35 -0
  18. data/lib/redis/cluster/option.rb +76 -0
  19. data/lib/redis/cluster/slot.rb +69 -0
  20. data/lib/redis/cluster/slot_loader.rb +47 -0
  21. data/lib/redis/cluster.rb +285 -0
  22. data/lib/redis/connection/command_helper.rb +2 -8
  23. data/lib/redis/connection/hiredis.rb +2 -2
  24. data/lib/redis/connection/ruby.rb +13 -30
  25. data/lib/redis/connection/synchrony.rb +12 -4
  26. data/lib/redis/connection.rb +2 -2
  27. data/lib/redis/distributed.rb +29 -8
  28. data/lib/redis/errors.rb +46 -0
  29. data/lib/redis/hash_ring.rb +20 -64
  30. data/lib/redis/pipeline.rb +9 -7
  31. data/lib/redis/version.rb +1 -1
  32. data/lib/redis.rb +287 -52
  33. data/makefile +74 -0
  34. data/redis.gemspec +9 -10
  35. data/test/bitpos_test.rb +13 -19
  36. data/test/blocking_commands_test.rb +3 -5
  37. data/test/client_test.rb +18 -1
  38. data/test/cluster_abnormal_state_test.rb +38 -0
  39. data/test/cluster_blocking_commands_test.rb +15 -0
  40. data/test/cluster_client_internals_test.rb +77 -0
  41. data/test/cluster_client_key_hash_tags_test.rb +88 -0
  42. data/test/cluster_client_options_test.rb +147 -0
  43. data/test/cluster_client_pipelining_test.rb +59 -0
  44. data/test/cluster_client_replicas_test.rb +36 -0
  45. data/test/cluster_client_slots_test.rb +94 -0
  46. data/test/cluster_client_transactions_test.rb +71 -0
  47. data/test/cluster_commands_on_cluster_test.rb +165 -0
  48. data/test/cluster_commands_on_connection_test.rb +40 -0
  49. data/test/cluster_commands_on_geo_test.rb +74 -0
  50. data/test/cluster_commands_on_hashes_test.rb +11 -0
  51. data/test/cluster_commands_on_hyper_log_log_test.rb +17 -0
  52. data/test/cluster_commands_on_keys_test.rb +134 -0
  53. data/test/cluster_commands_on_lists_test.rb +15 -0
  54. data/test/cluster_commands_on_pub_sub_test.rb +101 -0
  55. data/test/cluster_commands_on_scripting_test.rb +56 -0
  56. data/test/cluster_commands_on_server_test.rb +221 -0
  57. data/test/cluster_commands_on_sets_test.rb +39 -0
  58. data/test/cluster_commands_on_sorted_sets_test.rb +35 -0
  59. data/test/cluster_commands_on_streams_test.rb +196 -0
  60. data/test/cluster_commands_on_strings_test.rb +15 -0
  61. data/test/cluster_commands_on_transactions_test.rb +41 -0
  62. data/test/cluster_commands_on_value_types_test.rb +14 -0
  63. data/test/command_map_test.rb +3 -5
  64. data/test/commands_on_geo_test.rb +116 -0
  65. data/test/commands_on_hashes_test.rb +2 -16
  66. data/test/commands_on_hyper_log_log_test.rb +3 -17
  67. data/test/commands_on_lists_test.rb +2 -15
  68. data/test/commands_on_sets_test.rb +2 -72
  69. data/test/commands_on_sorted_sets_test.rb +2 -132
  70. data/test/commands_on_strings_test.rb +2 -96
  71. data/test/commands_on_value_types_test.rb +80 -6
  72. data/test/connection_handling_test.rb +5 -7
  73. data/test/distributed_blocking_commands_test.rb +10 -4
  74. data/test/distributed_commands_on_hashes_test.rb +16 -5
  75. data/test/distributed_commands_on_hyper_log_log_test.rb +8 -15
  76. data/test/distributed_commands_on_lists_test.rb +4 -7
  77. data/test/distributed_commands_on_sets_test.rb +58 -36
  78. data/test/distributed_commands_on_sorted_sets_test.rb +51 -10
  79. data/test/distributed_commands_on_strings_test.rb +30 -10
  80. data/test/distributed_commands_on_value_types_test.rb +38 -4
  81. data/test/distributed_commands_requiring_clustering_test.rb +1 -3
  82. data/test/distributed_connection_handling_test.rb +1 -3
  83. data/test/distributed_internals_test.rb +8 -19
  84. data/test/distributed_key_tags_test.rb +4 -6
  85. data/test/distributed_persistence_control_commands_test.rb +1 -3
  86. data/test/distributed_publish_subscribe_test.rb +1 -3
  87. data/test/distributed_remote_server_control_commands_test.rb +1 -3
  88. data/test/distributed_scripting_test.rb +1 -3
  89. data/test/distributed_sorting_test.rb +1 -3
  90. data/test/distributed_test.rb +12 -14
  91. data/test/distributed_transactions_test.rb +1 -3
  92. data/test/encoding_test.rb +4 -8
  93. data/test/error_replies_test.rb +2 -4
  94. data/test/fork_safety_test.rb +1 -6
  95. data/test/helper.rb +179 -66
  96. data/test/helper_test.rb +1 -3
  97. data/test/internals_test.rb +47 -56
  98. data/test/lint/blocking_commands.rb +40 -16
  99. data/test/lint/hashes.rb +41 -0
  100. data/test/lint/hyper_log_log.rb +15 -1
  101. data/test/lint/lists.rb +16 -0
  102. data/test/lint/sets.rb +142 -0
  103. data/test/lint/sorted_sets.rb +183 -2
  104. data/test/lint/strings.rb +108 -20
  105. data/test/lint/value_types.rb +8 -0
  106. data/test/persistence_control_commands_test.rb +1 -3
  107. data/test/pipelining_commands_test.rb +12 -8
  108. data/test/publish_subscribe_test.rb +1 -3
  109. data/test/remote_server_control_commands_test.rb +60 -3
  110. data/test/scanning_test.rb +1 -7
  111. data/test/scripting_test.rb +1 -3
  112. data/test/sentinel_command_test.rb +1 -3
  113. data/test/sentinel_test.rb +1 -3
  114. data/test/sorting_test.rb +1 -3
  115. data/test/ssl_test.rb +45 -49
  116. data/test/support/cluster/orchestrator.rb +199 -0
  117. data/test/support/connection/hiredis.rb +1 -1
  118. data/test/support/connection/ruby.rb +1 -1
  119. data/test/support/connection/synchrony.rb +1 -1
  120. data/test/support/redis_mock.rb +1 -1
  121. data/test/synchrony_driver.rb +6 -9
  122. data/test/thread_safety_test.rb +1 -3
  123. data/test/transactions_test.rb +11 -3
  124. data/test/unknown_commands_test.rb +1 -3
  125. data/test/url_param_test.rb +44 -46
  126. metadata +109 -16
  127. data/Rakefile +0 -87
data/test/bitpos_test.rb CHANGED
@@ -1,10 +1,4 @@
1
- # encoding: UTF-8
2
-
3
- require File.expand_path("helper", File.dirname(__FILE__))
4
-
5
- unless defined?(Enumerator)
6
- Enumerator = Enumerable::Enumerator
7
- end
1
+ require_relative "helper"
8
2
 
9
3
  class TestBitpos < Test::Unit::TestCase
10
4
 
@@ -13,48 +7,48 @@ class TestBitpos < Test::Unit::TestCase
13
7
  def test_bitpos_empty_zero
14
8
  target_version "2.9.11" do
15
9
  r.del "foo"
16
- assert_equal 0, r.bitpos("foo", 0)
10
+ assert_equal(0, r.bitpos("foo", 0))
17
11
  end
18
12
  end
19
13
 
20
14
  def test_bitpos_empty_one
21
15
  target_version "2.9.11" do
22
16
  r.del "foo"
23
- assert_equal -1, r.bitpos("foo", 1)
17
+ assert_equal(-1, r.bitpos("foo", 1))
24
18
  end
25
19
  end
26
20
 
27
21
  def test_bitpos_zero
28
22
  target_version "2.9.11" do
29
23
  r.set "foo", "\xff\xf0\x00"
30
- assert_equal 12, r.bitpos("foo", 0)
24
+ assert_equal(12, r.bitpos("foo", 0))
31
25
  end
32
26
  end
33
27
 
34
28
  def test_bitpos_one
35
29
  target_version "2.9.11" do
36
30
  r.set "foo", "\x00\x0f\x00"
37
- assert_equal 12, r.bitpos("foo", 1)
31
+ assert_equal(12, r.bitpos("foo", 1))
38
32
  end
39
33
  end
40
34
 
41
35
  def test_bitpos_zero_end_is_given
42
36
  target_version "2.9.11" do
43
37
  r.set "foo", "\xff\xff\xff"
44
- assert_equal 24, r.bitpos("foo", 0)
45
- assert_equal 24, r.bitpos("foo", 0, 0)
46
- assert_equal -1, r.bitpos("foo", 0, 0, -1)
38
+ assert_equal(24, r.bitpos("foo", 0))
39
+ assert_equal(24, r.bitpos("foo", 0, 0))
40
+ assert_equal(-1, r.bitpos("foo", 0, 0, -1))
47
41
  end
48
42
  end
49
43
 
50
44
  def test_bitpos_one_intervals
51
45
  target_version "2.9.11" do
52
46
  r.set "foo", "\x00\xff\x00"
53
- assert_equal 8, r.bitpos("foo", 1, 0, -1)
54
- assert_equal 8, r.bitpos("foo", 1, 1, -1)
55
- assert_equal -1, r.bitpos("foo", 1, 2, -1)
56
- assert_equal -1, r.bitpos("foo", 1, 2, 200)
57
- assert_equal 8, r.bitpos("foo", 1, 1, 1)
47
+ assert_equal(8, r.bitpos("foo", 1, 0, -1))
48
+ assert_equal(8, r.bitpos("foo", 1, 1, -1))
49
+ assert_equal(-1, r.bitpos("foo", 1, 2, -1))
50
+ assert_equal(-1, r.bitpos("foo", 1, 2, 200))
51
+ assert_equal(8, r.bitpos("foo", 1, 1, 1))
58
52
  end
59
53
  end
60
54
 
@@ -1,7 +1,5 @@
1
- # encoding: UTF-8
2
-
3
- require File.expand_path("helper", File.dirname(__FILE__))
4
- require "lint/blocking_commands"
1
+ require_relative "helper"
2
+ require_relative "lint/blocking_commands"
5
3
 
6
4
  class TestBlockingCommands < Test::Unit::TestCase
7
5
 
@@ -17,7 +15,7 @@ class TestBlockingCommands < Test::Unit::TestCase
17
15
  yield(r)
18
16
  t2 = Time.now
19
17
 
20
- assert timeout == r.client.timeout
18
+ assert timeout == r._client.timeout
21
19
  assert delay <= (t2 - t1)
22
20
  end
23
21
  end
data/test/client_test.rb CHANGED
@@ -1,4 +1,4 @@
1
- require File.expand_path("helper", File.dirname(__FILE__))
1
+ require_relative "helper"
2
2
 
3
3
  class TestClient < Test::Unit::TestCase
4
4
 
@@ -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
@@ -0,0 +1,147 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'helper'
4
+
5
+ # ruby -w -Itest test/cluster_client_options_test.rb
6
+ class TestClusterClientOptions < Test::Unit::TestCase
7
+ include Helper::Cluster
8
+
9
+ def test_option_class
10
+ option = Redis::Cluster::Option.new(cluster: %w[rediss://127.0.0.1:7000], replica: true)
11
+ assert_equal({ '127.0.0.1:7000' => { url: 'rediss://127.0.0.1:7000' } }, option.per_node_key)
12
+ assert_equal true, option.secure?
13
+ assert_equal true, option.use_replica?
14
+
15
+ option = Redis::Cluster::Option.new(cluster: %w[redis://127.0.0.1:7000], replica: false)
16
+ assert_equal({ '127.0.0.1:7000' => { url: 'redis://127.0.0.1:7000' } }, option.per_node_key)
17
+ assert_equal false, option.secure?
18
+ assert_equal false, option.use_replica?
19
+
20
+ option = Redis::Cluster::Option.new(cluster: %w[redis://127.0.0.1:7000])
21
+ assert_equal({ '127.0.0.1:7000' => { url: 'redis://127.0.0.1:7000' } }, option.per_node_key)
22
+ assert_equal false, option.secure?
23
+ assert_equal false, option.use_replica?
24
+ end
25
+
26
+ def test_client_accepts_valid_node_configs
27
+ nodes = ['redis://127.0.0.1:7000',
28
+ 'redis://127.0.0.1:7001',
29
+ { host: '127.0.0.1', port: '7002' },
30
+ { 'host' => '127.0.0.1', port: 7003 },
31
+ 'redis://127.0.0.1:7004',
32
+ 'redis://127.0.0.1:7005']
33
+
34
+ assert_nothing_raised do
35
+ build_another_client(cluster: nodes)
36
+ end
37
+ end
38
+
39
+ def test_client_accepts_valid_options
40
+ assert_nothing_raised do
41
+ build_another_client(timeout: 1.0)
42
+ end
43
+ end
44
+
45
+ def test_client_ignores_invalid_options
46
+ assert_nothing_raised do
47
+ build_another_client(invalid_option: true)
48
+ end
49
+ end
50
+
51
+ def test_client_works_even_if_so_many_unavailable_nodes_specified
52
+ nodes = (6001..7005).map { |port| "redis://127.0.0.1:#{port}" }
53
+ redis = build_another_client(cluster: nodes)
54
+
55
+ assert_equal 'PONG', redis.ping
56
+ end
57
+
58
+ def test_client_does_not_accept_db_specified_url
59
+ assert_raise(Redis::CannotConnectError, 'Could not connect to any nodes') do
60
+ build_another_client(cluster: ['redis://127.0.0.1:7000/1/namespace'])
61
+ end
62
+
63
+ assert_raise(Redis::CannotConnectError, 'Could not connect to any nodes') do
64
+ build_another_client(cluster: [{ host: '127.0.0.1', port: '7000' }], db: 1)
65
+ end
66
+ end
67
+
68
+ def test_client_does_not_accept_unconnectable_node_url_only
69
+ nodes = ['redis://127.0.0.1:7006']
70
+
71
+ assert_raise(Redis::CannotConnectError, 'Could not connect to any nodes') do
72
+ build_another_client(cluster: nodes)
73
+ end
74
+ end
75
+
76
+ def test_client_accepts_unconnectable_node_url_included
77
+ nodes = ['redis://127.0.0.1:7000', 'redis://127.0.0.1:7006']
78
+
79
+ assert_nothing_raised(Redis::CannotConnectError, 'Could not connect to any nodes') do
80
+ build_another_client(cluster: nodes)
81
+ end
82
+ end
83
+
84
+ def test_client_does_not_accept_http_scheme_url
85
+ nodes = ['http://127.0.0.1:80']
86
+
87
+ assert_raise(Redis::InvalidClientOptionError, "invalid uri scheme 'http'") do
88
+ build_another_client(cluster: nodes)
89
+ end
90
+ end
91
+
92
+ def test_client_does_not_accept_blank_included_config
93
+ nodes = ['']
94
+
95
+ assert_raise(Redis::InvalidClientOptionError, "invalid uri scheme ''") do
96
+ build_another_client(cluster: nodes)
97
+ end
98
+ end
99
+
100
+ def test_client_does_not_accept_bool_included_config
101
+ nodes = [true]
102
+
103
+ assert_raise(Redis::InvalidClientOptionError, "invalid uri scheme ''") do
104
+ build_another_client(cluster: nodes)
105
+ end
106
+ end
107
+
108
+ def test_client_does_not_accept_nil_included_config
109
+ nodes = [nil]
110
+
111
+ assert_raise(Redis::InvalidClientOptionError, "invalid uri scheme ''") do
112
+ build_another_client(cluster: nodes)
113
+ end
114
+ end
115
+
116
+ def test_client_does_not_accept_array_included_config
117
+ nodes = [[]]
118
+
119
+ assert_raise(Redis::InvalidClientOptionError, "invalid uri scheme ''") do
120
+ build_another_client(cluster: nodes)
121
+ end
122
+ end
123
+
124
+ def test_client_does_not_accept_empty_hash_included_config
125
+ nodes = [{}]
126
+
127
+ assert_raise(Redis::InvalidClientOptionError, 'Redis option of `cluster` must includes `:host` and `:port` keys') do
128
+ build_another_client(cluster: nodes)
129
+ end
130
+ end
131
+
132
+ def test_client_does_not_accept_object_included_config
133
+ nodes = [Object.new]
134
+
135
+ assert_raise(Redis::InvalidClientOptionError, 'Redis Cluster node config must includes String or Hash') do
136
+ build_another_client(cluster: nodes)
137
+ end
138
+ end
139
+
140
+ def test_client_does_not_accept_not_array_config
141
+ nodes = :not_array
142
+
143
+ assert_raise(Redis::InvalidClientOptionError, 'Redis Cluster node config must be Array') do
144
+ build_another_client(cluster: nodes)
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'helper'
4
+
5
+ # ruby -w -Itest test/cluster_client_pipelining_test.rb
6
+ class TestClusterClientPipelining < Test::Unit::TestCase
7
+ include Helper::Cluster
8
+
9
+ def test_pipelining_with_a_hash_tag
10
+ p1 = p2 = p3 = p4 = p5 = p6 = nil
11
+
12
+ redis.pipelined do |r|
13
+ r.set('{Presidents.of.USA}:1', 'George Washington')
14
+ r.set('{Presidents.of.USA}:2', 'John Adams')
15
+ r.set('{Presidents.of.USA}:3', 'Thomas Jefferson')
16
+ r.set('{Presidents.of.USA}:4', 'James Madison')
17
+ r.set('{Presidents.of.USA}:5', 'James Monroe')
18
+ r.set('{Presidents.of.USA}:6', 'John Quincy Adams')
19
+
20
+ p1 = r.get('{Presidents.of.USA}:1')
21
+ p2 = r.get('{Presidents.of.USA}:2')
22
+ p3 = r.get('{Presidents.of.USA}:3')
23
+ p4 = r.get('{Presidents.of.USA}:4')
24
+ p5 = r.get('{Presidents.of.USA}:5')
25
+ p6 = r.get('{Presidents.of.USA}:6')
26
+ end
27
+
28
+ [p1, p2, p3, p4, p5, p6].each do |actual|
29
+ assert_true actual.is_a?(Redis::Future)
30
+ end
31
+
32
+ assert_equal('George Washington', p1.value)
33
+ assert_equal('John Adams', p2.value)
34
+ assert_equal('Thomas Jefferson', p3.value)
35
+ assert_equal('James Madison', p4.value)
36
+ assert_equal('James Monroe', p5.value)
37
+ assert_equal('John Quincy Adams', p6.value)
38
+ end
39
+
40
+ def test_pipelining_without_hash_tags
41
+ assert_raise(Redis::Cluster::CrossSlotPipeliningError) do
42
+ redis.pipelined do
43
+ redis.set(:a, 1)
44
+ redis.set(:b, 2)
45
+ redis.set(:c, 3)
46
+ redis.set(:d, 4)
47
+ redis.set(:e, 5)
48
+ redis.set(:f, 6)
49
+
50
+ redis.get(:a)
51
+ redis.get(:b)
52
+ redis.get(:c)
53
+ redis.get(:d)
54
+ redis.get(:e)
55
+ redis.get(:f)
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'helper'
4
+
5
+ # ruby -w -Itest test/cluster_client_replicas_test.rb
6
+ class TestClusterClientReplicas < Test::Unit::TestCase
7
+ include Helper::Cluster
8
+
9
+ def test_client_can_command_with_replica
10
+ r = build_another_client(replica: true)
11
+
12
+ 100.times do |i|
13
+ assert_equal 'OK', r.set("key#{i}", i)
14
+ end
15
+
16
+ 100.times do |i|
17
+ assert_equal i.to_s, r.get("key#{i}")
18
+ end
19
+ end
20
+
21
+ def test_client_can_flush_with_replica
22
+ r = build_another_client(replica: true)
23
+
24
+ assert_equal 'OK', r.flushall
25
+ assert_equal 'OK', r.flushdb
26
+ end
27
+
28
+ def test_some_reference_commands_are_sent_to_slaves_if_needed
29
+ r = build_another_client(replica: true)
30
+
31
+ 5.times { |i| r.set("key#{i}", i) }
32
+
33
+ assert_equal %w[key0 key1 key2 key3 key4], r.keys
34
+ assert_equal 5, r.dbsize
35
+ end
36
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'helper'
4
+
5
+ # ruby -w -Itest test/cluster_client_slots_test.rb
6
+ class TestClusterClientSlots < Test::Unit::TestCase
7
+ include Helper::Cluster
8
+
9
+ def test_slot_class
10
+ slot = Redis::Cluster::Slot.new('127.0.0.1:7000' => 1..10)
11
+
12
+ assert_equal false, slot.exists?(0)
13
+ assert_equal true, slot.exists?(1)
14
+ assert_equal true, slot.exists?(10)
15
+ assert_equal false, slot.exists?(11)
16
+
17
+ assert_equal nil, slot.find_node_key_of_master(0)
18
+ assert_equal nil, slot.find_node_key_of_slave(0)
19
+ assert_equal '127.0.0.1:7000', slot.find_node_key_of_master(1)
20
+ assert_equal '127.0.0.1:7000', slot.find_node_key_of_slave(1)
21
+ assert_equal '127.0.0.1:7000', slot.find_node_key_of_master(10)
22
+ assert_equal '127.0.0.1:7000', slot.find_node_key_of_slave(10)
23
+ assert_equal nil, slot.find_node_key_of_master(11)
24
+ assert_equal nil, slot.find_node_key_of_slave(11)
25
+
26
+ assert_equal nil, slot.put(1, '127.0.0.1:7001')
27
+ end
28
+
29
+ def test_slot_class_with_node_flags_and_replicas
30
+ slot = Redis::Cluster::Slot.new({ '127.0.0.1:7000' => 1..10, '127.0.0.1:7001' => 1..10 },
31
+ { '127.0.0.1:7000' => 'master', '127.0.0.1:7001' => 'slave' },
32
+ true)
33
+
34
+ assert_equal false, slot.exists?(0)
35
+ assert_equal true, slot.exists?(1)
36
+ assert_equal true, slot.exists?(10)
37
+ assert_equal false, slot.exists?(11)
38
+
39
+ assert_equal nil, slot.find_node_key_of_master(0)
40
+ assert_equal nil, slot.find_node_key_of_slave(0)
41
+ assert_equal '127.0.0.1:7000', slot.find_node_key_of_master(1)
42
+ assert_equal '127.0.0.1:7001', slot.find_node_key_of_slave(1)
43
+ assert_equal '127.0.0.1:7000', slot.find_node_key_of_master(10)
44
+ assert_equal '127.0.0.1:7001', slot.find_node_key_of_slave(10)
45
+ assert_equal nil, slot.find_node_key_of_master(11)
46
+ assert_equal nil, slot.find_node_key_of_slave(11)
47
+
48
+ assert_equal nil, slot.put(1, '127.0.0.1:7002')
49
+ end
50
+
51
+ def test_slot_class_with_node_flags_and_without_replicas
52
+ slot = Redis::Cluster::Slot.new({ '127.0.0.1:7000' => 1..10, '127.0.0.1:7001' => 1..10 },
53
+ { '127.0.0.1:7000' => 'master', '127.0.0.1:7001' => 'slave' },
54
+ false)
55
+
56
+ assert_equal false, slot.exists?(0)
57
+ assert_equal true, slot.exists?(1)
58
+ assert_equal true, slot.exists?(10)
59
+ assert_equal false, slot.exists?(11)
60
+
61
+ assert_equal nil, slot.find_node_key_of_master(0)
62
+ assert_equal nil, slot.find_node_key_of_slave(0)
63
+ assert_equal '127.0.0.1:7000', slot.find_node_key_of_master(1)
64
+ assert_equal '127.0.0.1:7000', slot.find_node_key_of_slave(1)
65
+ assert_equal '127.0.0.1:7000', slot.find_node_key_of_master(10)
66
+ assert_equal '127.0.0.1:7000', slot.find_node_key_of_slave(10)
67
+ assert_equal nil, slot.find_node_key_of_master(11)
68
+ assert_equal nil, slot.find_node_key_of_slave(11)
69
+
70
+ assert_equal nil, slot.put(1, '127.0.0.1:7002')
71
+ end
72
+
73
+ def test_slot_class_with_empty_slots
74
+ slot = Redis::Cluster::Slot.new({})
75
+
76
+ assert_equal false, slot.exists?(0)
77
+ assert_equal false, slot.exists?(1)
78
+
79
+ assert_equal nil, slot.find_node_key_of_master(0)
80
+ assert_equal nil, slot.find_node_key_of_slave(0)
81
+ assert_equal nil, slot.find_node_key_of_master(1)
82
+ assert_equal nil, slot.find_node_key_of_slave(1)
83
+
84
+ assert_equal nil, slot.put(1, '127.0.0.1:7001')
85
+ end
86
+
87
+ def test_redirection_when_slot_is_resharding
88
+ 100.times { |i| redis.set("{key}#{i}", i) }
89
+
90
+ redis_cluster_resharding(12539, src: '127.0.0.1:7002', dest: '127.0.0.1:7000') do
91
+ 100.times { |i| assert_equal i.to_s, redis.get("{key}#{i}") }
92
+ end
93
+ end
94
+ end