123456789_123456789_123456789_123456789_123456789_

Class: ActiveRecord::Middleware::DatabaseSelector

Overview

Database Selector Middleware

The DatabaseSelector ::ActiveRecord::Middleware provides a framework for automatically swapping from the primary to the replica database connection. Rails provides a basic framework to determine when to swap and allows for applications to write custom strategy classes to override the default behavior.

The resolver class defines when the application should switch (i.e. read from the primary if a write occurred less than 2 seconds ago) and a resolver context class that sets a value that helps the resolver class decide when to switch.

Rails default middleware uses the request’s session to set a timestamp that informs the application when to read from a primary or read from a replica.

To use the DatabaseSelector in your application with default settings, run the provided generator.

$ bin/rails g active_record:multi_db

This will create a file named config/initializers/multi_db.rb with the following contents:

Rails.application.configure do
  config.active_record.database_selector = { delay: 2.seconds }
  config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
  config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
end

Alternatively you can set the options in your environment config or any other config file loaded on boot.

The default behavior can be changed by setting the config options to a custom class:

config.active_record.database_selector = { delay: 2.seconds }
config.active_record.database_resolver = MyResolver
config.active_record.database_resolver_context = MyResolver::MySession

Note: If you are using rails new my_app --minimal you will need to call require "active_support/core_ext/integer/time" to load the core extension in order to use 2.seconds

Class Method Summary

Instance Attribute Summary

Instance Method Summary

Constructor Details

.new(app, resolver_klass = nil, context_klass = nil, options = {}) ⇒ DatabaseSelector

[ GitHub ]

  
# File 'activerecord/lib/active_record/middleware/database_selector.rb', line 52

def initialize(app, resolver_klass = nil, context_klass = nil, options = {})
  @app = app
  @resolver_klass = resolver_klass || Resolver
  @context_klass = context_klass || Resolver::Session
  @options = options
end

Instance Attribute Details

#context_klass (readonly)

[ GitHub ]

  
# File 'activerecord/lib/active_record/middleware/database_selector.rb', line 59

attr_reader :resolver_klass, :context_klass, :options

#options (readonly)

[ GitHub ]

  
# File 'activerecord/lib/active_record/middleware/database_selector.rb', line 59

attr_reader :resolver_klass, :context_klass, :options

#resolver_klass (readonly)

[ GitHub ]

  
# File 'activerecord/lib/active_record/middleware/database_selector.rb', line 59

attr_reader :resolver_klass, :context_klass, :options

Instance Method Details

#call(env)

::ActiveRecord::Middleware that determines which database connection to use in a multiple database application.

[ GitHub ]

  
# File 'activerecord/lib/active_record/middleware/database_selector.rb', line 63

def call(env)
  request = ActionDispatch::Request.new(env)

  select_database(request) do
    @app.call(env)
  end
end

#select_database(request, &blk) (private)

[ GitHub ]

  
# File 'activerecord/lib/active_record/middleware/database_selector.rb', line 72

def select_database(request, &blk)
  context = context_klass.call(request)
  resolver = resolver_klass.call(context, options)

  response = if resolver.reading_request?(request)
    resolver.read(&blk)
  else
    resolver.write(&blk)
  end

  resolver.update_context(response)
  response
end