Kredis

repository·main·Indexed 23 days ago

https://github.com/rails/kredis

Kredis provides high-level, object-oriented abstractions for Redis data structures, allowing developers to treat Redis keys as coherent attributes within Active Model and Active Record models. It features a declarative DSL for typed scalars (string, integer, json, etc.) and complex structures (lists, sets, counters, limiters), support for multiple Redis servers via YAML configuration, and a migration utility for moving or deleting keys.

Tokens
5.6K
Snippets
8
Records
31
Agent score
74%

What's inside kredis

  1. What is Kredis

    main

    Kredis (Keyed Redis) provides high-level data structures and types encapsulated around single Redis keys. Instead of executing isolated procedural Redis commands, you interact with coherent objects.

    Key features include:

    • Declarative DSL: Configure Kredis structures as attributes within Active Model and Active Record classes.
    • Scalability: Use env-aware YAML configuration via Rails.application.config_for to map data structures to specific Redis instances.
    • Namespacing: Built-in support for key namespacing allows for safe parallel testing without data collisions between different test runs.
  2. Install Kredis

    main

    To install Kredis in your Rails application, follow these steps:

    1. Add the gem to your bundle:
      ./bin/bundle add kredis
    2. Run the installation generator to create the default configuration file at config/redis/shared.yml:
      ./bin/rails kredis:install
    ./bin/bundle add kredis
    ./bin/rails kredis:install
  3. Use multiple Redis servers with Kredis

    main

    By default, Kredis uses the shared Redis instance. You can direct specific Kredis structures to a different Redis server by providing a config option. This configuration is managed via config/redis/secondary.yml.

    Example:

    one_string = Kredis.string "mystring"
    two_string = Kredis.string "mystring", config: :secondary
    one_string = Kredis.string "mystring"
    two_string = Kredis.string "mystring", config: :secondary
    
    one_string.value = "just on shared"
    two_string.value != one_string.value
  4. Set SSL options on Redis Connections

    main

    To connect to a Redis instance using SSL, manually add an entry to the Kredis::Connections.connections hash. This is useful for configurations requiring Client Authentication or specific SSL parameters. This code should be placed in an initializer or config/environments/production.rb.

    Kredis::Connections.connections[:shared] = Redis.new(
      url: ENV["REDIS_URL"],
      ssl_params: {
        cert_store: OpenSSL::X509::Store.new.tap { |store| 
          store.add_file(Rails.root.join("config", "ca_cert.pem").to_s) 
        },
    
        cert: OpenSSL::X509::Certificate.new(File.read(
          Rails.root.join("config", "client.crt")
        )),
    
        key: OpenSSL::PKey::RSA.new(
          Rails.application.credentials.redis[:client_key]
        ),
    
        verify_mode: OpenSSL::SSL::VERIFY_PEER
      }
    )
  5. Integrate Kredis into ActiveRecord models

    main

    You can define Kredis attributes directly within your Rails models using Kredis methods. This allows you to associate Redis keys with specific model instances.

    Key features:

    • Custom Keys: Use a lambda or a method name to generate keys dynamically (e.g., key: ->(p) { "person:#{p.id}:names" }).
    • Default Values: Set a default value. Note that Kredis will perform additional Redis calls (WATCH, EXISTS, UNWATCH) to ensure the default is written if the key does not exist.
    • Callbacks: Use after_change to trigger logic when the Kredis attribute is mutated.

    Example:

    class Person < ApplicationRecord
      kredis_list :names, after_change: ->(p) { puts "Names changed!" }
      kredis_enum :morning, values: %w[ bright blue black ], default: "bright"
      kredis_counter :steps, expires_in: 1.hour
      
      private
        def generate_names_key
          "person:#{id}:names"
        end
      kredis_list :names_with_custom_key, key: :generate_names_key
    end
    class Person < ApplicationRecord
      kredis_list :names
      kredis_list :names_with_custom_key_via_lambda, key: ->(p) { "person:#{p.id}:names_customized" }
      kredis_list :names_with_custom_key_via_method, key: :generate_names_key
      kredis_unique_list :skills, limit: 2
      kredis_enum :morning, values: %w[ bright blue black ], default: "bright"
      kredis_counter :steps, expires_in: 1.hour
    
      private
        def generate_names_key
          "key-generated-from-private-method"
        end
    end
  6. Development workflow with Kredis

    main

    You can experiment with Kredis using the development console:

    1. Start the console: bin/console.
    2. Use Kredis.string (or other types) to interact with Redis.
    3. Use debugger to insert breakpoints for debugging within the console or test suite (requires the debug gem).

    To run tests, use bin/test.

    >> str = Kredis.string "mystring"
      Kredis  (0.1ms)  Connected to shared
    => #<Kredis::Types::Scalar:0x0000000134c7d938>
    >> str.value = "hello, world"
      Kredis Proxy (2.4ms)  SET mystring ["hello, world"]
    => "hello, world"
    >> str.value
  7. Use callbacks with Kredis types

    main

    Most Kredis type definitions accept an after_change option. When provided, Kredis wraps the type in a CallbacksProxy, allowing you to trigger logic whenever the underlying Redis value is updated.

    Example usage:

    class User < ApplicationRecord
      include Kredis::Types
    
      # The block is executed after the value changes
      status = string("status", after_change: ->(new_value) { puts "Status changed to #{new_value}" })
    end
  8. Configure Kredis Redis connections

    main

    Kredis uses YAML files located in config/redis/*.yml to manage connection settings.

    • The default configuration is config/redis/shared.yml.
    • You can create custom configurations (e.g., config/redis/strings.yml) and reference them when creating a Kredis type using the config: option.
    • If no configuration file is found, Kredis looks for the REDIS_URL environment variable, falling back to redis://127.0.0.1:6379/0.
    • Kredis passes the configuration hash directly to Redis.new.
  9. Manage Set members with type casting

    main

    When using Kredis::Types::Set, you can use the typed attribute to ensure members are automatically converted between Redis strings and specific Ruby types.

    • add(*members) and << will convert Ruby objects to strings before storing them.
    • members (and to_a) will convert the strings retrieved from Redis back into the specified types.
    • include?(member) converts the input member to a string to perform the check.
    • sample(count) returns either a single typed object (if count is nil) or an array of typed objects.
  10. Customize the Redis client creation via connector

    main

    By default, Kredis uses Redis.new(config) to establish connections. You can override this behavior by setting config.kredis.connector in your application.rb. The connector is a proc that receives the configuration and should return a Redis-compatible client.

    config.kredis.connector = ->(config) { SomeRedisProxy.new(config) }
  11. Use Kredis typed scalars

    main

    Kredis provides typed scalars for common data types. You can create these using Kredis.string, Kredis.integer, Kredis.decimal, Kredis.float, Kredis.boolean, Kredis.datetime, and Kredis.json. These objects allow you to interact with Redis values using Ruby types while Kredis handles the serialization/deserialization.

    Note that Kredis.decimal is used for high precision, while Kredis.float is optimized for speed.

    string = Kredis.string "mystring"
    string.value = "hello world!"
    
    integer = Kredis.integer "myinteger"
    integer.value = 5
    
    decimal = Kredis.decimal "mydecimal"
    decimal.value = "%.47f" % (1.0 / 10)
    
    float = Kredis.float "myfloat"
    float.value = 1.0 / 10
    
    boolean = Kredis.boolean "myboolean"
    boolean.value = true
    
    datetime = Kredis.datetime "mydatetime"
    datetime.value = Time.zone.now.midnight
    
    json = Kredis.json "myjson"
    json.value = { "one" => 1, "two" => "2" }
  12. Use Kredis data structures

    main

    Beyond simple scalars, Kredis provides specialized data structures for complex logic:

    • Lists: Kredis.list (standard list), Kredis.unique_list (list with uniqueness logic).
    • Sets: Kredis.set (standard set), Kredis.ordered_set (ZSET-based ordered set).
    • Hashes: Kredis.hash (standard hash), Kredis.hash with typed: :integer for integer values.
    • Counters: Kredis.counter (supports increments, decrements, and expires_in).
    • Cycles: Kredis.cycle (iterates through a fixed set of values).
    • Enums: Kredis.enum (fixed set of values with helper methods like .one?).
    • Slots: Kredis.slots (manages a pool of available slots) and Kredis.slot (single slot).
    • Flags: Kredis.flag (simple existence-based flag).
    • Limiters: Kredis.limiter (rate limiting with limit and expires_in).