Part 7 · 1 chapters · ~8 min
Rails Internals
Rack and the middleware stack, the router, Zeitwerk autoloading and eager loading, Active Record's relation building and Arel, callbacks and their ordering, transactions and locking (lock!, with_lock), N+1 and strict loading, Active Support, and the Rails boot process.
8
Active Record underneath
code
Transfer.where(state: "completed").order(created_at: :desc).limit(50).to_sql # see the SQL a relation builds
Transfer.includes(account: :owner).where(created_at: 1.day.ago..).each { ... } # no N+1
# pessimistic locking for money (ordered to avoid deadlocks, Concurrency P2)
Account.transaction do
a, b = Account.where(id: [from_id, to_id]).order(:id).lock.to_a # SELECT ... FOR UPDATE ordered by id
src, dst = a.id == from_id ? [a, b] : [b, a]
raise Ledger::InsufficientFunds if src.balance_kobo < amount
src.update!(balance_kobo: src.balance_kobo - amount)
dst.update!(balance_kobo: dst.balance_kobo + amount)
end
bin/rails middleware # the Rack stack
Rails.autoloaders.main.dirs # Zeitwerk: file names map to constants (app/models/ledger/entry.rb → Ledger::Entry)ACTIVE RECORD AND N+1
includes, preload and eager_load, and the tools that find the problem
swipe the figure sideways, or tap expand for full screen
1/5
the problem
Accessing an association inside a loop loads it per record: 50 transfers, 100 extra queries (Python course P7 shows the Django version).
a query per record in a loopthe most common Rails slowdown