Skip to content

Commit

Permalink
Fix compatibility with Ractors
Browse files Browse the repository at this point in the history
  • Loading branch information
jgmontoya authored and byroot committed Jan 23, 2024
1 parent 84f1c65 commit 6339bf4
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 23 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Unreleased

- Make `RedisClient::RESP3::DUMP_TYPES` immutable to allow Ractor compatibility and add basic tests for usage with Ractors.

# 0.19.1

- Fixed a bug in `hiredis-client` that could cause a crash if interrupted by `Timeout.timeout` or other `Thread#raise` based mecanism.
Expand Down
6 changes: 3 additions & 3 deletions lib/redis_client/ruby_connection/resp3.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ module RESP3

EOL = "\r\n".b.freeze
EOL_SIZE = EOL.bytesize
DUMP_TYPES = { # rubocop:disable Style/MutableConstant
DUMP_TYPES = {
String => :dump_string,
Symbol => :dump_symbol,
Integer => :dump_numeric,
Float => :dump_numeric,
}
}.freeze
PARSER_TYPES = {
'#' => :parse_boolean,
'$' => :parse_blob,
Expand Down Expand Up @@ -57,7 +57,7 @@ def new_buffer
def dump_any(object, buffer)
method = DUMP_TYPES.fetch(object.class) do |unexpected_class|
if superclass = DUMP_TYPES.keys.find { |t| t > unexpected_class }
DUMP_TYPES[unexpected_class] = DUMP_TYPES[superclass]
DUMP_TYPES[superclass]
else
raise TypeError, "Unsupported command argument type: #{unexpected_class}"
end
Expand Down
44 changes: 44 additions & 0 deletions test/redis_client/ractor_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# frozen_string_literal: true

require "test_helper"

class RactorTest < Minitest::Test
def setup
skip("Ractors are not supported on this Ruby version") unless defined?(::Ractor)
skip("Hiredis is not Ractor safe") if RedisClient.default_driver.name == "RedisClient::HiredisConnection"
begin
Ractor.new { RedisClient.default_driver.name }.take
rescue Ractor::RemoteError
skip("Ractor implementation is too limited (MRI 3.0?)")
end
end

def test_get_and_set_within_ractor
ractor = Ractor.new do
within_ractor_redis = ClientTestHelper.new_client
within_ractor_redis.call("SET", "foo", "bar")
within_ractor_redis.call("GET", "foo")
end

assert_equal("bar", ractor.take)
end

def test_multiple_ractors
ractor1 = Ractor.new do
within_ractor_redis = ClientTestHelper.new_client
within_ractor_redis.call("SET", "foo", "bar")
within_ractor_redis.call("GET", "foo")
end

ractor1.take # We do this to ensure that the SET has been processed

ractor2 = Ractor.new do
key = Ractor.receive
within_ractor_redis = ClientTestHelper.new_client
within_ractor_redis.call("GET", key)
end
ractor2.send("foo")

assert_equal("bar", ractor2.take)
end
end
42 changes: 22 additions & 20 deletions test/support/client_test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,28 @@ def travel(seconds, &block)
Process.stub(:clock_gettime, now, &block)
end

def simulate_network_errors(client, failures)
client.close
client.instance_variable_set(:@raw_connection, nil)

original_config = client.config
flaky_driver = Class.new(original_config.driver)
flaky_driver.include(FlakyDriver)
flaky_driver.failures = failures
flaky_config = original_config.dup
flaky_config.instance_variable_set(:@driver, flaky_driver)
begin
client.instance_variable_set(:@config, flaky_config)
yield
ensure
client.instance_variable_set(:@config, original_config)
client.close
client.instance_variable_set(:@raw_connection, nil)
end
end

module_function

def tcp_config
{
host: Servers::HOST,
Expand Down Expand Up @@ -82,24 +104,4 @@ def unix_config
def new_client(**overrides)
RedisClient.new(**tcp_config.merge(overrides))
end

def simulate_network_errors(client, failures)
client.close
client.instance_variable_set(:@raw_connection, nil)

original_config = client.config
flaky_driver = Class.new(original_config.driver)
flaky_driver.include(FlakyDriver)
flaky_driver.failures = failures
flaky_config = original_config.dup
flaky_config.instance_variable_set(:@driver, flaky_driver)
begin
client.instance_variable_set(:@config, flaky_config)
yield
ensure
client.instance_variable_set(:@config, original_config)
client.close
client.instance_variable_set(:@raw_connection, nil)
end
end
end
3 changes: 3 additions & 0 deletions test/support/raise_warnings.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
$VERBOSE = true
module RaiseWarnings
def warn(message, *)
return if message.include?('Ractor is experimental')

super

raise message
end
ruby2_keywords :warn if respond_to?(:ruby2_keywords, true)
Expand Down

0 comments on commit 6339bf4

Please sign in to comment.