sequel-activerecord_connection

repository·master·Indexed 41 days ago

https://github.com/janko/sequel-activerecord_connection

A Sequel database extension that allows Sequel to reuse an existing ActiveRecord connection. It enables seamless transaction management, shared connection pools, and synchronized logging between ActiveRecord and Sequel within the same process. Supports PostgreSQL, MySQL2, Trilogy, and SQLite adapters.

Tokens
2.1K
Snippets
7
Records
12
Agent score
100%

What's inside sequel-activerecord_connection

  1. How transactions work between ActiveRecord and Sequel

    master

    The extension synchronizes the transaction state between Sequel and ActiveRecord. This allows you to nest transactions from both libraries seamlessly.

    Key behaviors:

    • DB.in_transaction? will return true if an ActiveRecord transaction is currently open.
    • Sequel's transaction API (including isolation levels and savepoints) is fully supported.
    • Transaction hooks like after_commit are managed to ensure they execute correctly relative to the outer transaction state.
    ActiveRecord::Base.transaction do
      DB.in_transaction? #=> true
    end
    
    # Using Sequel's API within an ActiveRecord transaction
    DB.transaction(isolation: :serializable) do
      DB.after_commit { ... }
      DB.transaction(savepoint: true) do
        DB.after_commit(savepoint: true) { ... }
      end
    end
  2. Initialize Sequel to reuse ActiveRecord connections

    master

    To enable the extension, initialize your Sequel database object with the :activerecord_connection extension. This ensures Sequel queries use the existing ActiveRecord connection, allowing them to participate in the same transactions and appear in ActiveRecord logs.

    Choose the initialization method corresponding to your database adapter (e.g., postgres, mysql2, trilogy, or sqlite). For JRuby environments, use the Sequel.connect method with the appropriate JDBC URL.

    require "sequel"
    # For PostgreSQL
    DB = Sequel.postgres(extensions: :activerecord_connection)
    
    # For other adapters:
    Sequel.mysql2(extensions: :activerecord_connection)
    Sequel.trilogy(extensions: :activerecord_connection)
    Sequel.sqlite(extensions: :activerecord_connection)
    
    # For JRuby (JDBC):
    Sequel.connect("jdbc:postgresql://", extensions: :activerecord_connection)
    Sequel.connect("jdbc:mysql://", extensions: :activerecord_connection)
    Sequel.connect("jdbc:sqlite://", extensions: :activerecord_connection)
  3. Normalize SQL logs to match ActiveRecord style

    master

    By default, Sequel interpolates values directly into SQL strings, whereas ActiveRecord uses bound variables (e.g., $1, $2). To make Sequel logs look like ActiveRecord logs (which helps with log grouping and sensitive data protection), load the :sql_log_normalizer extension alongside :activerecord_connection.

    # For PostgreSQL
    Sequel.postgres(extensions: [:activerecord_connection, :sql_log_normalizer])
    
    # Resulting log format:
    # SELECT accounts.* FROM accounts WHERE accounts.email = ? LIMIT ?
  4. Install sequel-activerecord_connection

    master

    Add the gem to your project using bundle. If you are using ActiveRecord 7.1 or older, you must also include the after_commit_everywhere gem to ensure proper transaction hook behavior.

    $ bundle add sequel-activerecord_connection
    
    # Required if using ActiveRecord 7.1 or older
    $ bundle add after_commit_everywhere
  5. Configure SQLite extended result codes and prepared statement cache

    master

    When using the sequel-activerecord_connection extension with SQLite, the library automatically configures the underlying SQLite3::Database connection to enable extended_result_codes (if supported) and initializes a prepared statements cache via Utils.add_prepared_statements_cache. Additionally, if the Sequel database is configured with a :utc timezone and responds to current_timestamp_utc, the extension will enable current_timestamp_utc on the database object.

    # The extension applies these configurations automatically when extended onto a Sequel::Database instance
    # using the ActiveRecord connection reuse pattern.
  6. PostgreSQL disconnect error handling

    master

    The PostgreSQL adapter for sequel-activerecord_connection includes logic to detect connection failures and reraise them as Sequel::DatabaseDisconnectError.

    It identifies disconnects using two methods:

    1. Exception Class Matching: It checks if the raised error is included in DISCONNECT_ERROR_CLASSES (inherited from Sequel::Postgres::Adapter).
    2. Message Parsing: If a PG::Error is raised, it checks the connection status and parses the error message against DISCONNECT_ERROR_REGEX to determine if the connection is no longer valid.
  7. How the ActiveRecordConnection extension works

    master

    The activerecord_connection extension allows Sequel to reuse existing ActiveRecord database connections instead of creating its own. This ensures that Sequel and ActiveRecord share the same connection pool, transaction state, and logging context.

    Key behaviors include:

    • Connection Sharing: Sequel's connect method is disabled to prevent creating separate connections. Instead, Sequel uses ActiveRecord's connection pool via synchronize.
    • Transaction Synchronization: Sequel synchronizes its internal transaction state with ActiveRecord. This allows Sequel to correctly identify if it is inside an ActiveRecord transaction or savepoint, ensuring proper behavior for hooks and savepoint creation.
    • Logging: Queries executed via Sequel are instrumented into the ActiveRecord logger using ActiveSupport::Notifications.
    • Timezone Alignment: Sequel's timezone is synchronized with ActiveRecord.default_timezone.
    • Hook Delegation: When an ActiveRecord transaction is active, Sequel transaction/savepoint hooks (like after_commit) are delegated to ActiveRecord to ensure they execute at the correct time relative to the actual database commit.
  8. Register the activerecord_connection extension

    master

    To enable ActiveRecord connection reuse in Sequel, register the extension using Database#register_extension. This is typically done during the Sequel database initialization.

    Note: The extension attempts to load an adapter-specific module (e.g., Sequel::ActiveRecordConnection::Mysql2) to handle specific database requirements. If the adapter-specific module is not found, it assumes the standard Sequel adapter already works with ActiveRecord.

    DB.register_extension(:activerecord_connection)
  9. Configure a custom ActiveRecord model for the connection

    master

    By default, the extension reads connection configuration from ActiveRecord::Base. If your application uses multiple databases or specific connection settings defined on a different model, you can assign that model to the Sequel database object using DB.activerecord_model = ModelName.

    class MyModel < ActiveRecord::Base
      connects_to database: { writing: :animals, reading: :animals_replica }
    end
    
    DB.activerecord_model = MyModel
  10. Transaction isolation levels in ActiveRecordConnection

    master

    When starting a transaction via Sequel while using this extension, isolation levels are mapped from Sequel's symbols to ActiveRecord-compatible symbols using the TRANSACTION_ISOLATION_MAP:

    Sequel SymbolActiveRecord Symbol
    :uncommitted:read_uncommitted
    :committed:read_committed
    :repeatable:repeatable_read
    :serializable:serializable