Class: Pgtk::Retry
- Inherits:
-
Object
- Object
- Pgtk::Retry
- Defined in:
- lib/pgtk/retry.rb
Overview
Retry is a decorator for Pool that automatically retries failed SELECT queries. It provides fault tolerance for transient database errors by retrying read-only operations a configurable number of times before giving up.
This class implements the same interface as Pool but adds retry logic specifically for SELECT queries. Non-SELECT queries are executed without retry to maintain data integrity and avoid unintended side effects from duplicate writes.
Basic usage:
# Create and configure a regular pool
pool = Pgtk::Pool.new(wire, max: 4)
pool.start!
# Wrap the pool in a retry decorator with 3 attempts
retry_pool = Pgtk::Retry.new(pool, attempts: 3)
# SELECT queries are automatically retried on failure
begin
retry_pool.exec('SELECT * FROM users WHERE id = $1', [42])
rescue PG::Error => e
puts "Query failed after 3 attempts: #{e.}"
end
# Non-SELECT queries are not retried
retry_pool.exec('UPDATE users SET active = true WHERE id = $1', [42])
# Transactions pass through without retry logic
retry_pool.transaction do |t|
t.exec('SELECT * FROM accounts') # No retry within transaction
t.exec('UPDATE accounts SET balance = balance + 100')
end
# Combining with other decorators
impatient = Pgtk::Impatient.new(retry_pool, 5)
spy = Pgtk::Spy.new(impatient) do |sql, duration|
puts "Query: #{sql} (#{duration}s)"
end
- Author
-
Yegor Bugayenko (yegor256@gmail.com)
- Copyright
-
Copyright © 2019-2026 Yegor Bugayenko
- License
-
MIT
Defined Under Namespace
Classes: Exhausted
Instance Method Summary collapse
-
#dump ⇒ Object
Convert internal state into text.
-
#exec(sql) ⇒ Array
Execute a SQL query with automatic retry for SELECT queries only.
-
#initialize(pool, attempts: 3) ⇒ Retry
constructor
Constructor.
-
#start! ⇒ Object
Start a new connection pool with the given arguments.
-
#transaction {|Object| ... } ⇒ Object
Run a transaction without retry logic.
-
#version ⇒ String
Get the version of PostgreSQL server.
Constructor Details
#initialize(pool, attempts: 3) ⇒ Retry
Constructor.
61 62 63 64 65 |
# File 'lib/pgtk/retry.rb', line 61 def initialize(pool, attempts: 3) raise(ArgumentError, "Attempts must be at least 2, while #{attempts} provided") if attempts < 2 @pool = pool @attempts = attempts end |
Instance Method Details
#dump ⇒ Object
Convert internal state into text.
80 81 82 83 84 85 86 |
# File 'lib/pgtk/retry.rb', line 80 def dump [ @pool.dump, '', "Pgtk::Retry (attempts=#{@attempts})" ].join("\n") end |
#exec(sql) ⇒ Array
Execute a SQL query with automatic retry for SELECT queries only. Non-SELECT queries fail on the first error, since a failure may occur after the server received the query but before the acknowledgement reached the client, and retrying a non-idempotent write could duplicate it.
96 97 98 99 100 101 102 103 104 105 106 107 |
# File 'lib/pgtk/retry.rb', line 96 def exec(sql, *) query = sql.is_a?(Array) ? sql.join(' ') : sql attempt = 0 begin @pool.exec(sql, *) rescue StandardError, Pgtk::Impatient::TooSlow => e raise(e) unless query.strip.upcase.start_with?('SELECT') attempt += 1 raise(Exhausted, "Retry gave up after #{@attempts} attempts: #{e.}") if attempt >= @attempts retry end end |
#start! ⇒ Object
Start a new connection pool with the given arguments.
68 69 70 |
# File 'lib/pgtk/retry.rb', line 68 def start! @pool.start! end |
#transaction {|Object| ... } ⇒ Object
Run a transaction without retry logic.
113 114 115 |
# File 'lib/pgtk/retry.rb', line 113 def transaction(&) @pool.transaction(&) end |
#version ⇒ String
Get the version of PostgreSQL server.
75 76 77 |
# File 'lib/pgtk/retry.rb', line 75 def version @pool.version end |