Class: Redis::Client

Inherits:
RedisClient
  • Object
show all
Defined in:
lib/redis/client.rb

Constant Summary collapse

ERROR_MAPPING =
{
  RedisClient::ConnectionError => Redis::ConnectionError,
  RedisClient::CommandError => Redis::CommandError,
  RedisClient::ReadTimeoutError => Redis::TimeoutError,
  RedisClient::CannotConnectError => Redis::CannotConnectError,
  RedisClient::AuthenticationError => Redis::CannotConnectError,
  RedisClient::FailoverError => Redis::CannotConnectError,
  RedisClient::PermissionError => Redis::PermissionError,
  RedisClient::WrongTypeError => Redis::WrongTypeError,
  RedisClient::ReadOnlyError => Redis::ReadOnlyError,
  RedisClient::ProtocolError => Redis::ProtocolError,
  RedisClient::OutOfMemoryError => Redis::OutOfMemoryError,
}

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.config(protocol: 3, **kwargs) ⇒ Object



23
24
25
26
# File 'lib/redis/client.rb', line 23

def config(protocol: 3, **kwargs)
  super(protocol: protocol, **kwargs,
        driver_info: Redis::LibIdentity.driver_info(kwargs[:driver_info]))
end

.resp3_unsupported?(error) ⇒ Boolean

Whether error raised during the connection handshake means the server can't speak RESP3 (so we should retry the connection as RESP2). Covers Redis < 6 (no HELLO command, surfaced by redis-client as UnsupportedServer) and any server replying NOPROTO to HELLO 3.

Returns:

  • (Boolean)


42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/redis/client.rb', line 42

def resp3_unsupported?(error)
  return true if error.is_a?(::RedisClient::UnsupportedServer)
  return true if error.is_a?(::RedisClient::CommandError) && error.message.include?("NOPROTO")

  # Redis::Cluster discovers its topology by connecting to each startup node. When those nodes
  # don't speak RESP3, redis-cluster-client collects the per-node failures and re-raises them
  # wrapped in an InitialSetupError, discarding the original error classes (see
  # RedisClient::Cluster::InitialSetupError.from_errors). Only the concatenated message
  # survives, so match it to still trigger the RESP2 fallback for pre-6.0 clusters. Guarded by
  # defined? because the cluster error class is only loaded with the redis-clustering gem.
  defined?(::RedisClient::Cluster::InitialSetupError) &&
    error.is_a?(::RedisClient::Cluster::InitialSetupError) &&
    (error.message.include?("NOPROTO") || error.message.include?("HELLO command"))
end

.sentinel(protocol: 3, **kwargs) ⇒ Object



28
29
30
31
32
# File 'lib/redis/client.rb', line 28

def sentinel(protocol: 3, **kwargs)
  super(protocol: protocol, **kwargs,
        driver_info: Redis::LibIdentity.driver_info(kwargs[:driver_info]),
        client_implementation: ::RedisClient)
end

.translate_error!(error, mapping: ERROR_MAPPING) ⇒ Object

Raises:

  • (redis_error)


34
35
36
37
# File 'lib/redis/client.rb', line 34

def translate_error!(error, mapping: ERROR_MAPPING)
  redis_error = translate_error_class(error.class, mapping: mapping)
  raise redis_error, error.message, error.backtrace
end

Instance Method Details

#blocking_call_v(timeout, command, &block) ⇒ Object



128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/redis/client.rb', line 128

def blocking_call_v(timeout, command, &block)
  if timeout && timeout > 0
    # Can't use the command timeout argument as the connection timeout
    # otherwise it would be very racy. So we add the regular read_timeout on top
    # to account for the network delay.
    timeout += config.read_timeout
  end

  super(timeout, command, &block)
rescue ::RedisClient::Error => error
  raise if Client.resp3_unsupported?(error)

  Client.translate_error!(error)
end

#call_v(command, &block) ⇒ Object

We default to RESP3. Servers that can't speak it reject the HELLO 3 handshake (Redis < 6.0 has no HELLO at all; others answer NOPROTO). Re-raise those errors untranslated so they reach Redis#with_protocol_fallback as RedisClient::Error and trigger a transparent rebuild as RESP2 — the same path the sentinel and cluster clients already use. Everything else is translated to the matching Redis::* error.



120
121
122
123
124
125
126
# File 'lib/redis/client.rb', line 120

def call_v(command, &block)
  super(command, &block)
rescue ::RedisClient::Error => error
  raise if Client.resp3_unsupported?(error)

  Client.translate_error!(error)
end

#dbObject



82
83
84
# File 'lib/redis/client.rb', line 82

def db
  config.db
end

#hostObject



90
91
92
# File 'lib/redis/client.rb', line 90

def host
  config.host unless config.path
end

#idObject



70
71
72
# File 'lib/redis/client.rb', line 70

def id
  config.id
end

#inherit_socket!Object



159
160
161
# File 'lib/redis/client.rb', line 159

def inherit_socket!
  @inherit_socket = true
end

#multi(watch: nil) ⇒ Object



151
152
153
154
155
156
157
# File 'lib/redis/client.rb', line 151

def multi(watch: nil)
  super
rescue ::RedisClient::Error => error
  raise if Client.resp3_unsupported?(error)

  Client.translate_error!(error)
end

#passwordObject



106
107
108
# File 'lib/redis/client.rb', line 106

def password
  config.password
end

#pathObject



98
99
100
# File 'lib/redis/client.rb', line 98

def path
  config.path
end

#pipelined(exception: true) ⇒ Object



143
144
145
146
147
148
149
# File 'lib/redis/client.rb', line 143

def pipelined(exception: true)
  super
rescue ::RedisClient::Error => error
  raise if Client.resp3_unsupported?(error)

  Client.translate_error!(error)
end

#portObject



94
95
96
# File 'lib/redis/client.rb', line 94

def port
  config.port unless config.path
end

#protocolObject



86
87
88
# File 'lib/redis/client.rb', line 86

def protocol
  config.protocol
end

#server_urlObject



74
75
76
# File 'lib/redis/client.rb', line 74

def server_url
  config.server_url
end

#timeoutObject



78
79
80
# File 'lib/redis/client.rb', line 78

def timeout
  config.read_timeout
end

#usernameObject



102
103
104
# File 'lib/redis/client.rb', line 102

def username
  config.username
end