-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Make sure that we close cursors before returning from a query #6408
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Fix an intermittent exception when handling read-receipts. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -409,16 +409,16 @@ def _new_transaction( | |
i = 0 | ||
N = 5 | ||
while True: | ||
cursor = conn.cursor() | ||
cursor = LoggingTransaction( | ||
cursor, | ||
name, | ||
self.database_engine, | ||
after_callbacks, | ||
exception_callbacks, | ||
) | ||
try: | ||
txn = conn.cursor() | ||
txn = LoggingTransaction( | ||
txn, | ||
name, | ||
self.database_engine, | ||
after_callbacks, | ||
exception_callbacks, | ||
) | ||
r = func(txn, *args, **kwargs) | ||
r = func(cursor, *args, **kwargs) | ||
conn.commit() | ||
return r | ||
except self.database_engine.module.OperationalError as e: | ||
|
@@ -456,6 +456,33 @@ def _new_transaction( | |
) | ||
continue | ||
raise | ||
finally: | ||
# we're either about to retry with a new cursor, or we're about to | ||
# release the connection. Once we release the connection, it could | ||
# get used for another query, which might do a conn.rollback(). | ||
# | ||
# In the latter case, even though that probably wouldn't affect the | ||
# results of this transaction, python's sqlite will reset all | ||
# statements on the connection [1], which will make our cursor | ||
# invalid [2]. | ||
# | ||
# While the above probably doesn't apply to postgres, we still need | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't actually use cursors in postgres, since we don't name them, therefore we're just fetching all the data on a query. psycopg2's cursor when not named is just an abstraction. fwiw postgres says that any cursor can see any changes from any other cursor on the same connection, named or not There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. also calling |
||
# to make sure that we have done with the cursor before we release | ||
# the connection, for compatibility with sqlite. | ||
# | ||
# In any case, continuing to read rows after commit()ing seems | ||
# dubious from the PoV of ACID transactional semantics | ||
# (sqlite explicitly says that once you commit, you may see rows | ||
# from subsequent updates.) | ||
# | ||
# In short, if we haven't finished with the cursor yet, that's a | ||
# problem waiting to bite us. | ||
# | ||
# TL;DR: we're done with the cursor, so we can close it. | ||
# | ||
# [1]: https://github.com/python/cpython/blob/v3.8.0/Modules/_sqlite/connection.c#L465 | ||
# [2]: https://github.com/python/cpython/blob/v3.8.0/Modules/_sqlite/cursor.c#L236 | ||
cursor.close() | ||
except Exception as e: | ||
logger.debug("[TXN FAIL] {%s} %s", name, e) | ||
raise | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -280,7 +280,7 @@ def get_all_updated_receipts_txn(txn): | |
args.append(limit) | ||
txn.execute(sql, args) | ||
|
||
return (r[0:5] + (json.loads(r[5]),) for r in txn) | ||
return [r[0:5] + (json.loads(r[5]),) for r in txn] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should we not wrap this in an explicit There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. isn't that the same as using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oh, yes, the I'd personally do |
||
|
||
return self.runInteraction( | ||
"get_all_updated_receipts", get_all_updated_receipts_txn | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we call
conn.cursor()
in here instead of redefining a variable? this will bite us when mypy gets to it eventually :)