Skip to content

Commit b293513

Browse files
authored
perf: use case-insensitive comparison instead of normalization for command names (#410)
1 parent 25c5fd5 commit b293513

21 files changed

+292
-382
lines changed

lib/redis_client/cluster.rb

+3-2
Original file line numberDiff line numberDiff line change
@@ -152,8 +152,9 @@ def router
152152
end
153153

154154
def method_missing(name, *args, **kwargs, &block)
155-
if router.command_exists?(name)
156-
args.unshift(name)
155+
cmd = name.respond_to?(:name) ? name.name : name.to_s
156+
if router.command_exists?(cmd)
157+
args.unshift(cmd)
157158
command = @command_builder.generate(args, kwargs)
158159
return router.send_command(:call_v, command, &block)
159160
end

lib/redis_client/cluster/command.rb

+37-17
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
require 'redis_client'
44
require 'redis_client/cluster/errors'
55
require 'redis_client/cluster/key_slot_converter'
6-
require 'redis_client/cluster/normalized_cmd_name'
76

87
class RedisClient
98
class Cluster
@@ -30,7 +29,7 @@ def load(nodes, slow_command_timeout: -1) # rubocop:disable Metrics/AbcSize
3029
nodes&.each do |node|
3130
regular_timeout = node.read_timeout
3231
node.read_timeout = slow_command_timeout > 0.0 ? slow_command_timeout : regular_timeout
33-
reply = node.call('COMMAND')
32+
reply = node.call('command')
3433
node.read_timeout = regular_timeout
3534
commands = parse_command_reply(reply)
3635
cmd = ::RedisClient::Cluster::Command.new(commands)
@@ -51,7 +50,7 @@ def parse_command_reply(rows)
5150
rows&.each_with_object({}) do |row, acc|
5251
next if row[0].nil?
5352

54-
acc[row[0].downcase] = ::RedisClient::Cluster::Command::Detail.new(
53+
acc[row.first] = ::RedisClient::Cluster::Command::Detail.new(
5554
first_key_position: row[3],
5655
key_step: row[5],
5756
write?: row[2].include?('write'),
@@ -73,38 +72,59 @@ def extract_first_key(command)
7372
end
7473

7574
def should_send_to_primary?(command)
76-
name = ::RedisClient::Cluster::NormalizedCmdName.instance.get_by_command(command)
77-
@commands[name]&.write?
75+
find_command_info(command.first)&.write?
7876
end
7977

8078
def should_send_to_replica?(command)
81-
name = ::RedisClient::Cluster::NormalizedCmdName.instance.get_by_command(command)
82-
@commands[name]&.readonly?
79+
find_command_info(command.first)&.readonly?
8380
end
8481

8582
def exists?(name)
86-
@commands.key?(::RedisClient::Cluster::NormalizedCmdName.instance.get_by_name(name))
83+
@commands.key?(name) || @commands.key?(name.to_s.downcase(:ascii))
8784
end
8885

8986
private
9087

91-
def determine_first_key_position(command) # rubocop:disable Metrics/CyclomaticComplexity
92-
case name = ::RedisClient::Cluster::NormalizedCmdName.instance.get_by_command(command)
93-
when 'eval', 'evalsha', 'zinterstore', 'zunionstore' then 3
94-
when 'object' then 2
95-
when 'memory'
88+
def find_command_info(name)
89+
@commands[name] || @commands[name.to_s.downcase(:ascii)]
90+
end
91+
92+
def determine_first_key_position(command) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/AbcSize, Metrics/PerceivedComplexity
93+
if command.first.casecmp('get').zero?
94+
find_command_info(command.first)&.first_key_position.to_i
95+
elsif command.first.casecmp('mget').zero?
96+
find_command_info(command.first)&.first_key_position.to_i
97+
elsif command.first.casecmp('set').zero?
98+
find_command_info(command.first)&.first_key_position.to_i
99+
elsif command.first.casecmp('mset').zero?
100+
find_command_info(command.first)&.first_key_position.to_i
101+
elsif command.first.casecmp('del').zero?
102+
find_command_info(command.first)&.first_key_position.to_i
103+
elsif command.first.casecmp('eval').zero?
104+
3
105+
elsif command.first.casecmp('evalsha').zero?
106+
3
107+
elsif command.first.casecmp('zinterstore').zero?
108+
3
109+
elsif command.first.casecmp('zunionstore').zero?
110+
3
111+
elsif command.first.casecmp('object').zero?
112+
2
113+
elsif command.first.casecmp('memory').zero?
96114
command[1].to_s.casecmp('usage').zero? ? 2 : 0
97-
when 'migrate'
115+
elsif command.first.casecmp('migrate').zero?
98116
command[3].empty? ? determine_optional_key_position(command, 'keys') : 3
99-
when 'xread', 'xreadgroup'
117+
elsif command.first.casecmp('xread').zero?
118+
determine_optional_key_position(command, 'streams')
119+
elsif command.first.casecmp('xreadgroup').zero?
100120
determine_optional_key_position(command, 'streams')
101121
else
102-
@commands[name]&.first_key_position.to_i
122+
find_command_info(command.first)&.first_key_position.to_i
103123
end
104124
end
105125

106126
def determine_optional_key_position(command, option_name)
107-
idx = command.map { |e| e.to_s.downcase }.index(option_name&.downcase)
127+
idx = command.map { |e| e.to_s.downcase(:ascii) }.index(option_name)
108128
idx.nil? ? 0 : idx + 1
109129
end
110130
end

lib/redis_client/cluster/node.rb

+2-2
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ def initialize(scale_read: false, **kwargs)
9090

9191
def build_connection_prelude
9292
prelude = super.dup
93-
prelude << ['READONLY'] if @scale_read
93+
prelude << ['readonly'] if @scale_read
9494
prelude.freeze
9595
end
9696
end
@@ -309,7 +309,7 @@ def refetch_node_info_list(startup_clients) # rubocop:disable Metrics/AbcSize, M
309309
work_group.push(i, raw_client) do |client|
310310
regular_timeout = client.read_timeout
311311
client.read_timeout = @config.slow_command_timeout > 0.0 ? @config.slow_command_timeout : regular_timeout
312-
reply = client.call_once('CLUSTER', 'NODES')
312+
reply = client.call_once('cluster', 'nodes')
313313
client.read_timeout = regular_timeout
314314
parse_cluster_node_reply(reply)
315315
rescue StandardError => e

lib/redis_client/cluster/node/latency_replica.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def measure_latencies(clients, concurrent_worker) # rubocop:disable Metrics/AbcS
4747
min = DUMMY_LATENCY_MSEC
4848
MEASURE_ATTEMPT_COUNT.times do
4949
starting = obtain_current_time
50-
cli.call_once('PING')
50+
cli.call_once('ping')
5151
duration = obtain_current_time - starting
5252
min = duration if duration < min
5353
end

lib/redis_client/cluster/normalized_cmd_name.rb

-71
This file was deleted.

lib/redis_client/cluster/optimistic_locking.rb

+3-3
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,15 @@ def watch(keys) # rubocop:disable Metrics/AbcSize
1818
handle_redirection(slot, retry_count: 1) do |nd|
1919
nd.with do |c|
2020
c.ensure_connected_cluster_scoped(retryable: false) do
21-
c.call('ASKING') if @asking
22-
c.call('WATCH', *keys)
21+
c.call('asking') if @asking
22+
c.call('watch', *keys)
2323
begin
2424
yield(c, slot, @asking)
2525
rescue ::RedisClient::ConnectionError
2626
# No need to unwatch on a connection error.
2727
raise
2828
rescue StandardError
29-
c.call('UNWATCH')
29+
c.call('unwatch')
3030
raise
3131
end
3232
rescue ::RedisClient::CommandError => e

lib/redis_client/cluster/pipeline.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ def redirect_command(node, pipeline, inner_index)
290290
end
291291

292292
def try_asking(node)
293-
node.call('ASKING') == 'OK'
293+
node.call('asking') == 'OK'
294294
rescue StandardError
295295
false
296296
end

lib/redis_client/cluster/pub_sub.rb

+15-7
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
require 'redis_client'
44
require 'redis_client/cluster/errors'
5-
require 'redis_client/cluster/normalized_cmd_name'
65

76
class RedisClient
87
class Cluster
@@ -108,12 +107,21 @@ def next_event(timeout = nil) # rubocop:disable Metrics/AbcSize, Metrics/Cycloma
108107

109108
private
110109

111-
def _call(command)
112-
case ::RedisClient::Cluster::NormalizedCmdName.instance.get_by_command(command)
113-
when 'subscribe', 'psubscribe', 'ssubscribe' then call_to_single_state(command)
114-
when 'unsubscribe', 'punsubscribe' then call_to_all_states(command)
115-
when 'sunsubscribe' then call_for_sharded_states(command)
116-
else call_to_single_state(command)
110+
def _call(command) # rubocop:disable Metrics/AbcSize
111+
if command.first.casecmp('subscribe').zero?
112+
call_to_single_state(command)
113+
elsif command.first.casecmp('psubscribe').zero?
114+
call_to_single_state(command)
115+
elsif command.first.casecmp('ssubscribe').zero?
116+
call_to_single_state(command)
117+
elsif command.first.casecmp('unsubscribe').zero?
118+
call_to_all_states(command)
119+
elsif command.first.casecmp('punsubscribe').zero?
120+
call_to_all_states(command)
121+
elsif command.first.casecmp('sunsubscribe').zero?
122+
call_for_sharded_states(command)
123+
else
124+
call_to_single_state(command)
117125
end
118126
end
119127

0 commit comments

Comments
 (0)