add auto reconnect for postgres
This only works on the second database interaction, since psycopg2 only notices that
the connection is gone, when a query is executed.
So in the common case reconnect works as follows:
- some bot method calls a cursor function like .execute(), .fetchone(), etc.
- this raises an error if the connection is broken
- if following code then requests a new cursor, this will also fail since psycopg2
now knows that the connection is gone
- the error is caught in storage.DBConn.cursor(), a new connection will be set up
of which a new cursor is yielded
If the error happens in connection.commit() or .rollback() instead we can instantly
reconnect since these methods are wrapped.
So why not wrap the cursor methods as well?
Consider the following example:
A query is the last thing that was executed on a cursor.
The database connection is lost.
Now .fetchone() is called on the cursor.
We could wrap .fetchone() and reconnect, but we'd have to use a new cursor since
cursors are linked to connections. And on this new cursor .fetchone() wouldn't
make any sense, since we haven't executed a query on this cursor.
This commit is contained in:
55
bot/timer.py
55
bot/timer.py
@@ -34,17 +34,18 @@ class Timer(DatabasePlugin):
|
||||
return 'Invalid timer delay: {}'.format(delay)
|
||||
|
||||
try:
|
||||
self.cur.execute('''
|
||||
INSERT INTO
|
||||
timers (mask, target, message, delay, ends_at)
|
||||
VALUES
|
||||
(%s, %s, %s, %s, now() + INTERVAL %s)
|
||||
RETURNING
|
||||
*
|
||||
''', [mask, target, message, delay, delay])
|
||||
with self.con.cursor() as cur:
|
||||
cur.execute('''
|
||||
INSERT INTO
|
||||
timers (mask, target, message, delay, ends_at)
|
||||
VALUES
|
||||
(%s, %s, %s, %s, now() + INTERVAL %s)
|
||||
RETURNING
|
||||
*
|
||||
''', [mask, target, message, delay, delay])
|
||||
self.con.commit()
|
||||
|
||||
asyncio.ensure_future(self.exec_timer(self.cur.fetchone()))
|
||||
asyncio.ensure_future(self.exec_timer(cur.fetchone()))
|
||||
|
||||
self.bot.notice(mask.nick, 'Timer in {delay} set: {message}'.format(delay=delay, message=message))
|
||||
except Error as ex:
|
||||
@@ -54,18 +55,19 @@ class Timer(DatabasePlugin):
|
||||
def set_timers(self):
|
||||
"""Function which queries all timers in the next hour and schedules them."""
|
||||
self.log.debug('Fetching timers')
|
||||
self.cur.execute('''
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
timers
|
||||
WHERE
|
||||
ends_at >= now()
|
||||
AND ends_at < now() + INTERVAL '1h'
|
||||
''')
|
||||
with self.con.cursor() as cur:
|
||||
cur.execute('''
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
timers
|
||||
WHERE
|
||||
ends_at >= now()
|
||||
AND ends_at < now() + INTERVAL '1h'
|
||||
''')
|
||||
|
||||
for timer in self.cur.fetchall():
|
||||
asyncio.ensure_future(self.exec_timer(timer))
|
||||
for timer in cur.fetchall():
|
||||
asyncio.ensure_future(self.exec_timer(timer))
|
||||
|
||||
async def exec_timer(self, timer: DictRow):
|
||||
"""Sets the actual timer (sleeps until it fires), sends the reminder and deletes the timer from database."""
|
||||
@@ -84,10 +86,11 @@ class Timer(DatabasePlugin):
|
||||
))
|
||||
|
||||
self.timers.remove(timer['id'])
|
||||
self.cur.execute('''
|
||||
DELETE FROM
|
||||
timers
|
||||
WHERE
|
||||
id = %s
|
||||
''', [timer['id']])
|
||||
with self.con.cursor() as cur:
|
||||
cur.execute('''
|
||||
DELETE FROM
|
||||
timers
|
||||
WHERE
|
||||
id = %s
|
||||
''', [timer['id']])
|
||||
self.con.commit()
|
||||
|
||||
Reference in New Issue
Block a user