One line of configuration
Automatic role switching is turned on by a single line, and the size of that line is misleading: it changes which database every request in the application talks to.
config.active_record.database_selector = { delay: 2.seconds }
This application doesn't set it, and the senior course "Database architecture: replicas" explained why: there's no replica to switch to. The machinery is still worth understanding, because the moment a replica exists this line is the shortest path to using it, and the shortest path isn't always the safest one.
What it inserts
The line installs a Rack middleware that wraps every request. Its whole job, from ActiveRecord::Middleware::DatabaseSelector:
response = if resolver.reading_request?(request)
resolver.read(&blk)
else
resolver.write(&blk)
end
resolver.update_context(response)
There are three moving parts, and they're worth naming separately because each can be replaced on its own.
The context remembers something about this user between requests. By default it's the session.
The resolver decides, from the request and the context, whether this request reads from the replica.
The middleware wires them together and wraps the request in the chosen role.
flowchart TB
R["a request arrives"] --> M["DatabaseSelector middleware"]
M --> C["context: what do we remember about this session?"]
M --> S["resolver: read or write? replica or primary?"]
C --> S
S --> W["the request runs inside connected_to(role: ...)"]
W --> U["update_context: record if this request wrote"]
The whole decision is two questions
Is this a reading request? The HTTP verb answers that, and lesson 2 looks at it.
Has this session written recently? A timestamp in the session answers that, and lesson 3 looks at it.
That's the entire policy. There's no query inspection, no awareness of tables and no per-model configuration. It's a rule of thumb about HTTP applied the same way to every endpoint in the application, including endpoints written after it was turned on.
Why it's opt-in
Rails ships this turned off, and that's a deliberate statement: the framework won't guess that your GET requests are safe to serve from a replica, because it can't know.
Compare it with the explicit form from that course:
ActiveRecord::Base.connected_to(role: :reading) do
SomeReport.generate
end
That block is a claim about code you're looking at. The middleware is a claim about code you haven't read yet, and the difference between those two is the subject of this course.
What you're configuring when you set it
The delay is the only knob, and it's a duration:
{ delay: 2.seconds }
It's how long a session stays pinned to the primary after writing. Set it too low and users read stale data after saving. Set it too high and the replica goes unused for anyone who has just submitted a form.
Notice what you can't configure here: which requests are reads. The resolver fixes that, and changing it means supplying your own. Lesson 6 covers that, and it's more often the right answer than the delay knob suggests.
Key takeaways
config.active_record.database_selector = { delay: 2.seconds }installs middleware that wraps every request.- There are three replaceable parts: a context that remembers, a resolver that decides, and middleware that wires them together.
- The policy is two questions: is this a reading request, and has this session written recently?
- It ships turned off because Rails can't know whether your
GETrequests are safe on a replica. delayis the only knob. Which requests count as reads is fixed unless you supply your own resolver.
Keep going with lesson 2
The verb is the signal
The resolver treats GET and HEAD as reads, so GET requests that write must be audited first.
A free account also gives you the quizzes, your progress and notes, and the discussion.