blob: b5b5df074c741dd02ac8b793e241eb4268df43b0 (
plain) (
tree)
|
|
# frozen_string_literal: true
require "active_record/middleware/database_selector/resolver"
module ActiveRecord
module Middleware
# The DatabaseSelector 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 add
# the following options to your environment config:
#
# 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
#
# New applications will include these lines commented out in the production.rb.
#
# 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
class DatabaseSelector
def initialize(app, resolver_klass = Resolver, context_klass = Resolver::Session, options = {})
@app = app
@resolver_klass = resolver_klass
@context_klass = context_klass
@options = options
end
attr_reader :resolver_klass, :context_klass, :options
# Middleware that determines which database connection to use in a multiple
# database application.
def call(env)
request = ActionDispatch::Request.new(env)
select_database(request) do
@app.call(env)
end
end
private
def select_database(request, &blk)
context = context_klass.call(request)
resolver = resolver_klass.call(context, options)
if reading_request?(request)
resolver.read(&blk)
else
resolver.write(&blk)
end
end
def reading_request?(request)
request.get? || request.head?
end
end
end
end
|