module StatementTimeout
module_function
def with_timeout(ms)
ApplicationRecord.connection.execute("SET LOCAL statement_timeout = #{Integer(ms)}")
yield
ensure
# SET LOCAL resets automatically at transaction end
end
end
class SearchController < ApplicationController
def index
@results = ApplicationRecord.transaction do
StatementTimeout.with_timeout(800) do
Snip.search(params[:q]).limit(50)
end
end
rescue ActiveRecord::QueryCanceled
@results = []
flash.now[:alert] = 'Search timed out. Please refine your query.'
end
end
A per-request statement_timeout is a great “seatbelt” for endpoints that can run bad queries when parameters are unexpected. You can set it for a block; Postgres enforces the wall clock limit.