redis-rb Ruby Client

repository·master·Indexed 26 days ago

https://github.com/redis/redis-rb

An idiomatic Ruby client for Redis that maintains a one-to-one mapping with the Redis command API. It supports RESP3, Redis Sentinel, and Redis Cluster (via the redis-clustering gem). Key features include pipelining, atomic transactions with MULTI/EXEC, SSL/TLS support, and an optional hiredis driver for improved performance. It also provides experimental support for Bulk Hash Ingestion (HIMPORT) for Redis 8.10+.

Tokens
31.5K
Snippets
59
Records
256
Agent score
80%

What's inside redis-rb

  1. Understand the redis-rb dependency tree

    master

    The redis-rb library acts as a high-level DSL. It depends on redis-client (by Shopify) for RESP parsing, socket management, and pipelines.

    If you are using redis-clustering, note that it depends on redis (the main gem) and is pinned to the exact same version. redis-cluster-client is a peer driver that handles cluster topology and slot routing but is unaware of the redis-rb DSL; it receives raw command arrays (e.g., ["INCR", "k"]) and routes them based on the Redis server's COMMAND introspection.

  2. Use the Redis Query Engine (RediSearch) in redis-rb

    master

    The Redis Query Engine (RediSearch, FT.* commands) is available in redis-rb through two interoperable approaches:

    1. Low-level ft_* methods: These are thin wrappers around raw Redis commands (e.g., ft_search, ft_create). They are always available and represent the most current feature set. Use these when you need direct control or the highest level of fidelity to the Redis protocol.
    2. High-level Abstractions: Classes like Schema, Query, Index, and AggregateRequest sit on top of the low-level methods. These builders make common tasks (like defining schemas or building complex queries) more readable and type-safe. Use these for standard workflows to improve code maintainability.

    You can mix and match these approaches freely; for example, you can call ft_search with a hand-built query string and still receive a reshaped SearchResult object.

  3. Use SSL/TLS with Redis::Cluster

    master

    For Redis versions 7.* and newer, you can use SSL/TLS by providing rediss:// URLs.

    For Redis versions prior to 6.*, if you are connecting to a single endpoint via SSL/TLS that represents the cluster, you must provide the fixed_hostname option. This prevents certificate verification failures when the cluster returns IP addresses instead of the expected FQDN.

  4. Initialize a Redis connection

    master
    You can connect to Redis by instantiating the Redis class. By default, it attempts to connect to localhost on port 6379. You can specify a host, port, and database index, or use a redis:// URL. Note that passwords with special characters in URLs must be URL-encoded.
  5. Configure Redis Sentinel support

    master

    The client supports automatic failover via Redis Sentinel. You must provide a list of sentinels and the name of the master group. You can optionally specify a role (:master or :slave).

    Important Authentication Note: If you provide a password or username at the top level, it applies to the Redis instance, NOT the sentinels. To authenticate against both, you must explicitly provide sentinel_password (and sentinel_username if applicable).

  6. Gate tests for specific Redis versions

    master

    When adding commands that only exist in newer Redis versions, use the provided helpers in test/helper.rb to prevent breaking CI runs on older Redis versions.

    • target_version(version): Wraps a block so it only executes if the server version is at least the specified version.
    • omit_version(min_ver): Skips the entire current test if the server version is below min_ver.
    def test_set_with_exat
      target_version "6.2" do
        r.set("foo", "bar", exat: Time.now.to_i + 2)
        assert_in_range 0..2, r.ttl("foo")
      end
    end
    
    def test_my_command
      omit_version("7.4")
      r.my_new_command(...)
    end
  7. Migrating to RESP3 in redis-rb 6.0

    master

    Starting with version 6.0, redis-rb negotiates the RESP3 protocol (HELLO 3) by default. Most wrapped commands automatically reshape RESP3 replies to match the Ruby objects used in RESP2, ensuring backward compatibility.

    Key Changes:

    • GEO coordinates: GEOPOS and GEOSEARCH/GEORADIUS with WITHCOORD now return coordinates as Float instead of String.
    • Raw/Unwrapped commands: Commands called via redis.call(...) or through method_missing (unwrapped) will return native RESP3 types (e.g., Hash instead of flat Array for maps, true/false instead of 1/0 for booleans) rather than the reshaped RESP2-compatible objects.
  8. Implement a new Redis command

    master

    Follow this checklist when adding a new command to the library:

    1. Implementation:
      • Place the method in the correct category file under lib/redis/commands/.
      • Use keyword arguments for flags.
      • Coerce inputs (e.g., Integer, Float, to_s) at the boundary.
      • Use send_command or send_blocking_command.
      • Use an existing reshape lambda if the reply needs shaping; otherwise, create a new one.
      • Add a YARD docstring with @param, @return, and @example.
    2. Distributed Support:
      • Add support in lib/redis/distributed.rb using node_for(key).method(...) for single-key commands, or ensure_same_node for multi-key commands.
    3. Testing:
      • Add tests to test/lint/<category>.rb for core behavior.
      • Add distributed-specific tests in test/distributed/.
      • Add cluster-specific tests in cluster/test/ if applicable.
      • Gate version-specific tests using target_version or omit_version.
      • Add a pipelined test if the command involves multiple steps or post-processing.
    4. Errors: If a new error is required, add it to lib/redis/errors.rb and Redis::Client::ERROR_MAPPING.
  9. Avoid CROSSSLOT errors in Cluster mode

    master

    In Redis Cluster, commands involving multiple keys (like MGET, MSET, or DEL) will fail with a Redis::CommandError (CROSSSLOT Keys in request don't hash to the same slot) if the keys do not belong to the same hash slot.

    To ensure multiple keys are handled in the same slot, use hash tags (e.g., {key}name). While redis-clustering provides an internal implementation to allow these commands without hash tags by preventing cross-slot errors, it is highly recommended to use hash tags for better performance.

    redis = Redis::Cluster.new(nodes: %w[redis://127.0.0.1:7000])
    
    # This will fail if keys are in different slots
    redis.mget('key1', 'key2')
    #=> Redis::CommandError (CROSSSLOT Keys in request don't hash to the same slot)
    
    # This succeeds because of the hash tag
    redis.mget('{key}1', '{key}2')
    #=> [nil, nil]
  10. Implement keyword-flag commands (SET pattern)

    master

    When adding commands that support optional flags (common in Redis 6+), follow the SET pattern to ensure compatibility with pipelines and correct return values:

    • Use keyword arguments (e.g., ex: nil) instead of positional booleans.
    • Build the command array imperatively using args << "FLAG" rather than conditional array literals.
    • Capitalize flag names (e.g., "EX", "NX", "KEEPTTL") to follow Redis convention.
    • Branch send_command calls if the return shape of the command changes based on the flags provided. Use a reshape lambda (like &BoolifySet) for the branch that requires a different return type.
    def set(key, value, ex: nil, px: nil, exat: nil, pxat: nil, nx: nil, xx: nil, keepttl: nil, get: nil)
      args = [:set, key, value.to_s]
      args << "EX" << Integer(ex) if ex
      args << "PX" << Integer(px) if px
      args << "EXAT" << Integer(exat) if exat
      args << "PXAT" << Integer(pxat) if pxat
      args << "NX" if nx
      args << "XX" if xx
      args << "KEEPTTL" if keepttl
      args << "GET" if get
    
      if nx || xx
        send_command(args, &BoolifySet)
      else
        send_command(args)
      end
    end