From 7fea981ebf5c97ff7d072945f4f5db532c1c4b55 Mon Sep 17 00:00:00 2001 From: Taishi Kasuga Date: Wed, 30 Apr 2025 13:15:32 +0900 Subject: [PATCH 1/5] test: add a case for Ractor --- test/test_concurrency.rb | 69 +++++++++++++++++++++++++++++++++++++++ test/testing_constants.rb | 2 ++ 2 files changed, 71 insertions(+) diff --git a/test/test_concurrency.rb b/test/test_concurrency.rb index 0f816cf..9532b4c 100644 --- a/test/test_concurrency.rb +++ b/test/test_concurrency.rb @@ -132,6 +132,75 @@ def test_threading_with_transaction assert_equal(WANT, @client.call('GET', '{key}1')) end + def test_ractor + skip('Ractor is not available') unless Object.const_defined?(:Ractor, false) + + ractors = Array.new(MAX_THREADS) do |i| + Ractor.new(i) do |i| + c = ::RedisClient.cluster( + nodes: TEST_NODE_URIS, + fixed_hostname: TEST_FIXED_HOSTNAME, + **TEST_GENERIC_OPTIONS + ).new_client + c.call('get', "key#{i}") + rescue StandardError => e + e + ensure + c&.close + end + end + + ractors.each { |r| assert_equal(WANT, r.take) } + end + + def test_ractor_with_pipelining + skip('Ractor is not available') unless Object.const_defined?(:Ractor, false) + + ractors = Array.new(MAX_THREADS) do |i| + Ractor.new(i) do |i| + c = ::RedisClient.cluster( + nodes: TEST_NODE_URIS, + fixed_hostname: TEST_FIXED_HOSTNAME, + **TEST_GENERIC_OPTIONS + ).new_client + c.pipelined do |pi| + pi.call('get', "key#{i}") + pi.call('get', "key#{i}") + end + rescue StandardError => e + e + ensure + c&.close + end + end + + ractors.each { |r| assert_equal([WANT, WANT], r.take) } + end + + def test_ractor_with_transaction + skip('Ractor is not available') unless Object.const_defined?(:Ractor, false) + + ractors = Array.new(MAX_THREADS) do |i| + Ractor.new(i) do |i| + c = ::RedisClient.cluster( + nodes: TEST_NODE_URIS, + fixed_hostname: TEST_FIXED_HOSTNAME, + **TEST_GENERIC_OPTIONS + ).new_client + c.multi(watch: ["key#{i}"]) do |tx| + tx.call('incr', "key#{i}") + tx.call('incr', "key#{i}") + end + rescue StandardError => e + e + ensure + c&.close + end + end + + ractors.each { |r| assert_equal([2, 3], r.take) } + end + private def new_test_client diff --git a/test/testing_constants.rb b/test/testing_constants.rb index 6dcf724..3209ece 100644 --- a/test/testing_constants.rb +++ b/test/testing_constants.rb @@ -76,6 +76,8 @@ raise NotImplementedError, TEST_REDIS_HOST end +Ractor.make_shareable(TEST_NODE_URIS) if Object.const_defined?(:Ractor, false) && Ractor.respond_to?(:make_shareable) + TEST_GENERIC_OPTIONS = (TEST_REDIS_SSL ? _base_opts.merge(_ssl_opts) : _base_opts).freeze _tmp_cli = _new_raw_cli.call(**TEST_GENERIC_OPTIONS) From 28e8a263aedf4055b0210d768a4c9f71309d7f06 Mon Sep 17 00:00:00 2001 From: Taishi Kasuga Date: Wed, 30 Apr 2025 13:24:22 +0900 Subject: [PATCH 2/5] fix --- .rubocop.yml | 2 +- test/prof_stack2.rb | 12 ++++++------ test/testing_constants.rb | 7 +++++-- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 488493f..5955f09 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,5 +1,5 @@ --- -require: +plugins: - rubocop-performance - rubocop-rake - rubocop-minitest diff --git a/test/prof_stack2.rb b/test/prof_stack2.rb index f0ced04..f893a01 100644 --- a/test/prof_stack2.rb +++ b/test/prof_stack2.rb @@ -16,13 +16,13 @@ def run case mode = ENV.fetch('PROFILE_MODE', :single).to_sym when :single - execute(client, mode) do |client| - ATTEMPTS.times { |i| client.call('get', "key#{i}") } + execute(client, mode) do |cli| + ATTEMPTS.times { |i| cli.call('get', "key#{i}") } end when :transaction - execute(client, mode) do |client| + execute(client, mode) do |cli| ATTEMPTS.times do |i| - client.multi do |tx| + cli.multi do |tx| SIZE.times do |j| n = SIZE * i + j tx.call('set', "{group:#{i}}:key:#{n}", n) @@ -31,9 +31,9 @@ def run end end when :pipeline - execute(client, mode) do |client| + execute(client, mode) do |cli| ATTEMPTS.times do |i| - client.pipelined do |pi| + cli.pipelined do |pi| SIZE.times do |j| n = SIZE * i + j pi.call('get', "key#{n}") diff --git a/test/testing_constants.rb b/test/testing_constants.rb index 3209ece..2252128 100644 --- a/test/testing_constants.rb +++ b/test/testing_constants.rb @@ -76,8 +76,6 @@ raise NotImplementedError, TEST_REDIS_HOST end -Ractor.make_shareable(TEST_NODE_URIS) if Object.const_defined?(:Ractor, false) && Ractor.respond_to?(:make_shareable) - TEST_GENERIC_OPTIONS = (TEST_REDIS_SSL ? _base_opts.merge(_ssl_opts) : _base_opts).freeze _tmp_cli = _new_raw_cli.call(**TEST_GENERIC_OPTIONS) @@ -88,4 +86,9 @@ BENCH_ENVOY_OPTIONS = { port: 7000, protocol: 2 }.freeze BENCH_REDIS_CLUSTER_PROXY_OPTIONS = { port: 7001, protocol: 2 }.freeze +if Object.const_defined?(:Ractor, false) && Ractor.respond_to?(:make_shareable) + Ractor.make_shareable(TEST_NODE_URIS) + Ractor.make_shareable(TEST_GENERIC_OPTIONS) +end + # rubocop:enable Lint/UnderscorePrefixedVariableName From 1c924f5f8b32ab33796cefae139f1420fd5317dd Mon Sep 17 00:00:00 2001 From: Taishi Kasuga Date: Wed, 30 Apr 2025 13:40:14 +0900 Subject: [PATCH 3/5] fix --- test/test_concurrency.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/test_concurrency.rb b/test/test_concurrency.rb index 9532b4c..bc44a3a 100644 --- a/test/test_concurrency.rb +++ b/test/test_concurrency.rb @@ -134,6 +134,7 @@ def test_threading_with_transaction def test_ractor skip('Ractor is not available') unless Object.const_defined?(:Ractor, false) + skip('hiredis driver is not safe for Ractor') if ::RedisClient.default_driver == :hiredis ractors = Array.new(MAX_THREADS) do |i| Ractor.new(i) do |i| @@ -155,6 +156,7 @@ def test_ractor def test_ractor_with_pipelining skip('Ractor is not available') unless Object.const_defined?(:Ractor, false) + skip('hiredis driver is not safe for Ractor') if ::RedisClient.default_driver == :hiredis ractors = Array.new(MAX_THREADS) do |i| Ractor.new(i) do |i| @@ -179,6 +181,7 @@ def test_ractor_with_pipelining def test_ractor_with_transaction skip('Ractor is not available') unless Object.const_defined?(:Ractor, false) + skip('hiredis driver is not safe for Ractor') if ::RedisClient.default_driver == :hiredis ractors = Array.new(MAX_THREADS) do |i| Ractor.new(i) do |i| From 484540cfadf61cebaa32a7e835d69514dc277c95 Mon Sep 17 00:00:00 2001 From: Taishi Kasuga Date: Wed, 30 Apr 2025 13:50:34 +0900 Subject: [PATCH 4/5] fix --- test/test_concurrency.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_concurrency.rb b/test/test_concurrency.rb index bc44a3a..2910a35 100644 --- a/test/test_concurrency.rb +++ b/test/test_concurrency.rb @@ -167,7 +167,7 @@ def test_ractor_with_pipelining ).new_client c.pipelined do |pi| pi.call('get', "key#{i}") - pi.call('get', "key#{i}") + pi.call('echo', 'hi') end rescue StandardError => e e @@ -176,7 +176,7 @@ def test_ractor_with_pipelining end end - ractors.each { |r| assert_equal([WANT, WANT], r.take) } + ractors.each { |r| assert_equal([WANT, 'hi'], r.take) } end def test_ractor_with_transaction From 6f8e269a4ae6ddb2be0bae78859ef656fb18e43f Mon Sep 17 00:00:00 2001 From: Taishi Kasuga Date: Wed, 30 Apr 2025 14:17:28 +0900 Subject: [PATCH 5/5] fix --- test/test_concurrency.rb | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/test/test_concurrency.rb b/test/test_concurrency.rb index 2910a35..2e044bd 100644 --- a/test/test_concurrency.rb +++ b/test/test_concurrency.rb @@ -134,7 +134,9 @@ def test_threading_with_transaction def test_ractor skip('Ractor is not available') unless Object.const_defined?(:Ractor, false) - skip('hiredis driver is not safe for Ractor') if ::RedisClient.default_driver == :hiredis + skip("#{RedisClient.default_driver} is not safe for Ractor") if RedisClient.default_driver != RedisClient::RubyConnection + skip('OpenSSL gem has non-shareable objects') if TEST_REDIS_SSL + skip('test case may get stuck') if RUBY_ENGINE == 'ruby' && RUBY_ENGINE_VERSION.split('.').take(2).join('.').to_f < 3.1 ractors = Array.new(MAX_THREADS) do |i| Ractor.new(i) do |i| @@ -156,7 +158,9 @@ def test_ractor def test_ractor_with_pipelining skip('Ractor is not available') unless Object.const_defined?(:Ractor, false) - skip('hiredis driver is not safe for Ractor') if ::RedisClient.default_driver == :hiredis + skip("#{RedisClient.default_driver} is not safe for Ractor") if RedisClient.default_driver != RedisClient::RubyConnection + skip('OpenSSL gem has non-shareable objects') if TEST_REDIS_SSL + skip('test case may get stuck') if RUBY_ENGINE == 'ruby' && RUBY_ENGINE_VERSION.split('.').take(2).join('.').to_f < 3.1 ractors = Array.new(MAX_THREADS) do |i| Ractor.new(i) do |i| @@ -181,7 +185,9 @@ def test_ractor_with_pipelining def test_ractor_with_transaction skip('Ractor is not available') unless Object.const_defined?(:Ractor, false) - skip('hiredis driver is not safe for Ractor') if ::RedisClient.default_driver == :hiredis + skip("#{RedisClient.default_driver} is not safe for Ractor") if RedisClient.default_driver != RedisClient::RubyConnection + skip('OpenSSL gem has non-shareable objects') if TEST_REDIS_SSL + skip('test case may get stuck') if RUBY_ENGINE == 'ruby' && RUBY_ENGINE_VERSION.split('.').take(2).join('.').to_f < 3.1 ractors = Array.new(MAX_THREADS) do |i| Ractor.new(i) do |i|