Class: Pgtk::Impatient
- Inherits:
-
Object
- Object
- Pgtk::Impatient
- Defined in:
- lib/pgtk/impatient.rb
Overview
Impatient is a decorator for Pool that enforces timeouts on all database operations. It ensures that SQL queries don’t run indefinitely, which helps prevent application hangs and resource exhaustion when database operations are slow or stalled.
This class implements the same interface as Pool but wraps each database operation in a timeout block. If a query exceeds the specified timeout, it raises a Timeout::Error exception, allowing the application to handle slow queries gracefully.
Basic usage:
# Create and configure a regular pool
pool = Pgtk::Pool.new(wire, max: 4)
pool.start!
# Wrap the pool in an impatient decorator with a 2-second timeout
impatient = Pgtk::Impatient.new(pool, 2)
# Execute queries with automatic timeout enforcement
begin
impatient.exec('SELECT * FROM large_table WHERE complex_condition')
rescue Timeout::Error
puts "Query timed out after 2 seconds"
end
# Transactions also enforce timeouts on each query
begin
impatient.transaction do |t|
t.exec('UPDATE large_table SET processed = true')
t.exec('DELETE FROM queue WHERE processed = true')
end
rescue Timeout::Error
puts "Transaction timed out"
end
# Combining with Spy for timeout monitoring
spy = Pgtk::Spy.new(impatient) do |sql, duration|
puts "Query completed in #{duration} seconds: #{sql}"
end
# Now queries are both timed and monitored
spy.exec('SELECT * FROM users')
- Author
-
Yegor Bugayenko ([email protected])
- Copyright
-
Copyright © 2019-2025 Yegor Bugayenko
- License
-
MIT
Defined Under Namespace
Classes: TooSlow
Instance Method Summary collapse
-
#dump ⇒ Object
Convert internal state into text.
-
#exec(query, *args) ⇒ Array
Execute a SQL query with a timeout.
-
#initialize(pool, timeout, *off) ⇒ Impatient
constructor
Constructor.
-
#start! ⇒ Object
Start a new connection pool with the given arguments.
-
#transaction {|Pgtk::Impatient| ... } ⇒ Object
Run a transaction with a timeout for each query.
-
#version ⇒ String
Get the version of PostgreSQL server.
Constructor Details
#initialize(pool, timeout, *off) ⇒ Impatient
Constructor.
65 66 67 68 69 |
# File 'lib/pgtk/impatient.rb', line 65 def initialize(pool, timeout, *off) @pool = pool @timeout = timeout @off = off end |
Instance Method Details
#dump ⇒ Object
Convert internal state into text.
84 85 86 87 88 89 90 91 |
# File 'lib/pgtk/impatient.rb', line 84 def dump [ @pool.dump, '', "Pgtk::Impatient (timeout=#{@timeout}s):", @off.map { |re| " #{re}" } ].join("\n") end |
#exec(query, *args) ⇒ Array
Execute a SQL query with a timeout.
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/pgtk/impatient.rb', line 99 def exec(query, *args) sql = query.is_a?(Array) ? query.join(' ') : query return @pool.exec(sql, *args) if @off.any? { |re| re.match?(sql) } start = Time.now token = SecureRandom.uuid begin Timeout.timeout(@timeout, Timeout::Error, token) do @pool.exec(sql, *args) end rescue Timeout::Error => e raise e unless e. == token raise TooSlow, [ 'SQL query', ("with #{args.count} argument#{'s' if args.count > 1}" unless args.empty?), 'was terminated after', start.ago, 'of waiting' ].compact.join(' ') end end |
#start! ⇒ Object
Start a new connection pool with the given arguments.
72 73 74 |
# File 'lib/pgtk/impatient.rb', line 72 def start! @pool.start! end |
#transaction {|Pgtk::Impatient| ... } ⇒ Object
Run a transaction with a timeout for each query.
124 125 126 127 128 129 |
# File 'lib/pgtk/impatient.rb', line 124 def transaction @pool.transaction do |t| t.exec("SET LOCAL statement_timeout = #{(@timeout * 1000).to_i}") yield Pgtk::Impatient.new(t, @timeout) end end |
#version ⇒ String
Get the version of PostgreSQL server.
79 80 81 |
# File 'lib/pgtk/impatient.rb', line 79 def version @pool.version end |