Solid Cable

repository·main·Indexed 43 days ago

https://github.com/rails/solid_cable

A database-backed Action Cable adapter for Ruby on Rails that replaces the need for Redis by storing messages in a database table and polling for updates. It simplifies deployments by reducing external dependencies and supports features such as message payload encryption via Active Record Encryption, configurable message retention, and automatic trimming.

Tokens
3.1K
Snippets
10
Records
13
Agent score
56%

What's inside Solid Cable

  1. Manage message trimming

    main

    Solid Cable automatically trims messages based on the message_retention setting. For every message written, Solid Cable attempts to trim twice as many expired messages.

    If you want to avoid the performance impact of autotrimming during broadcasts, you can:

    1. Set autotrim: false in config/cable.yml.
    2. Manually enqueue the trim job using SolidCable::TrimJob.perform_later or run it on a recurring interval out of band.
  2. Configure the cable database in database.yml

    main

    After installation, you must define the cable database configuration in config/database.yml. The name used in config/database.yml must match the name used in the connects_to block of config/cable.yml (which defaults to cable).

    SQLite Example:

    production:
      primary:
        <<: *default
        database: storage/production.sqlite3
      cable:
        <<: *default
        database: storage/production_cable.sqlite3
        migrations_paths: db/cable_migrate

    MySQL/PostgreSQL/Trilogy Example:

    production:
      primary: &primary_production
        <<: *default
        database: app_production
        username: app
        password: <%= ENV["APP_DATABASE_PASSWORD"] %>
      cable:
        <<: *primary_production
        database: app_production_cable
        migrations_paths: db/cable_migrate

    After configuring the database, run bin/rails db:prepare in production to create the database and load the schema.

  3. Configure Solid Cable with a single database

    main

    While running Solid Cable in a separate database is recommended, you can use your primary database for both the app and Action Cable by following these steps:

    1. Copy the contents of db/cable_schema.rb into a standard migration and delete db/cable_schema.rb.
    2. Remove the connects_to block from config/cable.yml.
    3. Run bin/rails db:migrate.

    In this configuration, database.yml only needs the primary database definition.

  4. Run benchmarks for Solid Cable using k6

    main

    Solid Cable includes a minimal Rails app in the bench directory for benchmarking. To run load tests, use k6 with the xk6-cable extension.

    Setup Steps

    1. Install k6.
    2. Build a custom k6 binary with the xk6-cable extension:
      xk6 build --with github.com/anycable/xk6-cable
    3. Run the load test script:
      ./k6 run loadtest.js

    Environment Variables

    The loadtest.js script accepts the following environment variables to configure the test:

    • WS_URL: The URL to send websocket connections to.
    • MAX: The number of virtual users (VUs) to hit the server with.
    • TIME: The duration of the load test.
    • MESSAGES_NUM: The number of messages each VU will send to the server.
    xk6 build --with github.com/anycable/xk6-cable
    ./k6 run loadtest.js
  5. Install Solid Cable

    main

    If you are not using Rails 8, you can manually add Solid Cable to your application. This process configures Solid Cable as the production cable adapter by overwriting config/cable.yml and creating db/cable_schema.rb.

    bundle add solid_cable
    bin/rails solid_cable:install
  6. Enable message payload encryption

    main

    Solid Cable can encrypt stored message payloads using Active Record Encryption.

    Requirements:

    1. Your application must be configured to use Active Record Encryption.
    2. You must include the msgpack gem (Solid Cable uses the binary MessagePack serializer by default).
    3. Note for PostgreSQL users: Active Record Encryption does not support encrypted binary columns on PostgreSQL with Rails 7. Solid Cable will raise an error during boot if this combination is used.

    Step 1: Update config/cable.yml

    production:
      adapter: solid_cable
      encrypt: true
      connects_to:
        database:
          writing: cable

    Step 2: Configure encryption context in an initializer Since encryption context properties contain Ruby objects, they must be set in config/initializers/solid_cable.rb rather than the YAML config:

    SolidCable.configuration.encryption_context_properties = {
      encryptor: ActiveRecord::Encryption::Encryptor.new,
      message_serializer: ActiveRecord::Encryption::MessageSerializer.new
    }
    # config/initializers/solid_cable.rb
    SolidCable.configuration.encryption_context_properties = {
      encryptor: ActiveRecord::Encryption::Encryptor.new,
      message_serializer: ActiveRecord::Encryption::MessageSerializer.new
    }
  7. Configure Solid Cable options in cable.yml

    main

    All Solid Cable settings are managed in config/cable.yml.

    Default Configuration:

    production:
      adapter: solid_cable
      connects_to:
        database:
          writing: cable
      polling_interval: 0.1.seconds
      message_retention: 1.day

    Available Options:

    • connects_to: Sets the Active Record database configuration for Solid Cable models. Supports all Active Record options.
    • polling_interval: Frequency of the polling interval. (Default: 0.1.seconds)
    • message_retention: Retention time for messages. Used as the cut-off for trimming. (Default: 1.day)
    • autotrim: Whether Solid Cable handles autotrimming. (Default: true)
    • silence_polling: Whether to silence Active Record logs emitted during polling. (Default: true)
    • use_skip_locked: Whether to use FOR UPDATE SKIP LOCKED during trimming. Set to false if your database doesn't support it (e.g., MySQL < 8 or PostgreSQL < 9.5). (Default: true)
    • trim_batch_size: Batch size for deleting old records. (Default: 100)
    • reconnect_attempts: Number of connection attempts or an array of durations to wait between attempts. (Default: 1 retry attempt)
    • encrypt: Whether to encrypt message payloads with Active Record Encryption. (Default: false)
  8. Install Solid Cable using the Rails generator

    main

    To set up Solid Cable in a Rails application, run the installation generator. This will copy the necessary database schema and configuration files into your project.

    Specifically, it generates:

    • db/cable_schema.rb: The schema required for Solid Cable's database tables.
    • config/cable.yml: The configuration file for cable settings.
    rails generate solid_cable:install
  9. Configure Solid Cable settings

    main

    Solid Cable is configured via a configuration object, typically initialized in config/initializers/solid_cable.rb. You can tune performance, retention, and database behavior using several configuration keys.

    Database and Polling

    • connects_to: A hash of database connection configurations.
    • silence_polling: Boolean. When false, polling is disabled. Defaults to true.
    • polling_interval: The duration between polls. Defaults to 0.1.seconds.
    • use_skip_locked: Boolean. Enables SKIP LOCKED for database queries to improve concurrency. Defaults to true.

    Message Retention and Trimming

    • message_retention: The duration for which messages are kept. Defaults to 1.day.
    • autotrim: Boolean. Enables automatic trimming of old messages. Defaults to true.
    • trim_batch_size: The number of records to delete in a single trim operation. Defaults to 100 if set to less than 2.

    Writer and Reconnection

    • writer_batch_size: Number of messages to batch during writes. Defaults to 4.
    • writer_batch_delay: The delay applied to writer batches. Defaults to 0.001.seconds.
    • reconnect_attempts: An integer representing the number of attempts, or an array of specific delays. Defaults to 1 (represented as [0]).

    Encryption

    • encrypt: Enables message encryption. Set this to a truthy value to enable.
    • encryption_context_properties: A hash of properties for the encryption context. If encrypt is enabled, it defaults to using ActiveRecord::Encryption::Encryptor (with compression) and ActiveRecord::Encryption::MessagePackMessageSerializer (a binary-only serializer that is 40% more efficient than the default).
    # Example configuration in config/initializers/solid_cable.rb
    SolidCable.configure do |config|
      config.connects_to = { database: :cable }
      config.polling_interval = 0.5.seconds
      config.message_retention = 1.week
      config.encrypt = true
      config.encryption_context_properties = { some_key: 'some_value' }
    end
  10. Encryption compatibility warning for PostgreSQL in Rails 7

    main
    When using Solid Cable with encryption enabled (SolidCable.encrypt? = true), you cannot use the PostgreSQL adapter if you are running Rails 7. This is because Active Record Encryption in Rails 7 does not support encrypting binary columns on PostgreSQL. If this configuration is detected during application initialization, Solid Cable will raise an error to prevent runtime failures.
  11. Configure Solid Cable via the configuration object

    main

    Solid Cable can be configured globally using the SolidCable.configure method or by providing a configuration hash. The configuration is initialized using settings from config/cable.yml via Rails.application.config_for("cable") by default.

    You can access the current configuration via SolidCable.configuration and modify it, or reset it using SolidCable.reset_configuration!.

    # Configure with a hash of options
    SolidCable.configure(
      polling_interval: 5,
      message_retention: 1.day,
      encrypt?: true
    )
    
    # Or access and modify the existing configuration
    SolidCable.configuration.polling_interval = 10