This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: [sqlite3] only reset statements when needed
Type: Stage: patch review
Components: Extension Modules Versions:
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: berker.peksag, erlendaasland, kj, miss-islington, pablogsal, serhiy.storchaka
Priority: normal Keywords: patch

Created on 2021-08-19 20:05 by erlendaasland, last changed 2022-04-11 14:59 by admin.

Pull Requests
URL Status Linked Edit
PR 27844 merged erlendaasland, 2021-08-19 20:07
PR 28490 merged erlendaasland, 2021-09-21 12:52
PR 28574 merged erlendaasland, 2021-09-26 20:23
PR 30379 open erlendaasland, 2022-01-03 23:42
Messages (14)
msg399939 - (view) Author: Erlend E. Aasland (erlendaasland) * (Python triager) Date: 2021-08-20 07:33
Ref. Serhiy's msg387858 in bpo-43350:
"Maybe the code could be rewritten in more explicit way and call pysqlite_statement_reset() only when it is necessary [...]"

Currently, we try to reset statements in all "statement exit" routes. IMO, it would be cleaner to just reset statements when we really need to:
1. before the first sqlite3_step() every time we (re)execute a query
2. at cursor exit, if there's an active statement
(3. in pysqlite_do_all_statements() ... see bpo-44092)

This will make the code easier to follow, and it will minimise the number of resets. The current patch is pretty small: 7 insertions(+), 33 deletions(-)

Pro:
- less lines of code, less maintenance
- cleaner exit paths
- optimise SQLite API usage
Con:
- code churn


If this is accepted, PR 25984 of bpo-44073 will be easier to land and review :)
msg399984 - (view) Author: Erlend E. Aasland (erlendaasland) * (Python triager) Date: 2021-08-20 18:05
I did a quick count of sqlite3_reset()s in the sqlite3 test suite:
- main:     2976 calls
- PR 27844: 1730 calls

Since we never call sqlite3_reset() with a NULL pointer, all sqlite3_reset() calls we execute hold the SQLite db mutex; reducing the number of sqlite3_reset() calls reduces the number of times we need to hold the mutex.
msg401186 - (view) Author: Erlend E. Aasland (erlendaasland) * (Python triager) Date: 2021-09-06 22:35
In msg399939, item 2 lacks one more "reset path":

> 2. at cursor exit, if there's an active statement

Rewording this to:

2. when a statement is removed from a cursor; that is either at cursor dealloc, or when the current statement is replaced.
msg402312 - (view) Author: Pablo Galindo Salgado (pablogsal) * (Python committer) Date: 2021-09-21 11:20
New changeset 050d1035957379d70e8601e6f5636637716a264b by Erlend Egeberg Aasland in branch 'main':
bpo-44958: Only reset `sqlite3` statements when needed (GH-27844)
https://github.com/python/cpython/commit/050d1035957379d70e8601e6f5636637716a264b
msg402317 - (view) Author: miss-islington (miss-islington) Date: 2021-09-21 13:16
New changeset 3e3ff09058a71b92c32d1d7dc32470c0cf49300c by Erlend Egeberg Aasland in branch 'main':
bpo-44958: Fix ref. leak introduced in GH-27844 (GH-28490)
https://github.com/python/cpython/commit/3e3ff09058a71b92c32d1d7dc32470c0cf49300c
msg402319 - (view) Author: Erlend E. Aasland (erlendaasland) * (Python triager) Date: 2021-09-21 14:25
Now that pysqlite_statement_reset() is only used in cursor.c, I suggest to move it to cursor.c and make it a static function.
msg402402 - (view) Author: Erlend E. Aasland (erlendaasland) * (Python triager) Date: 2021-09-21 22:33
> Now that pysqlite_statement_reset() is only used in cursor.c, I suggest to move it to cursor.c and make it a static function.

I'll wait until PR 25984 is merged before opening a PR for relocating pysqlite_statement_reset().
msg402423 - (view) Author: Ken Jin (kj) * (Python committer) Date: 2021-09-22 12:03
Erlend, I suspect that 050d1035957379d70e8601e6f5636637716a264b may have introduced a perf regression in pyperformance's sqlite_synth benchmark:
https://speed.python.org/timeline/?exe=12&base=&ben=sqlite_synth&env=1&revs=50&equid=off&quarts=on&extr=on
The benchmark code is here https://github.com/python/pyperformance/blob/main/pyperformance/benchmarks/bm_sqlite_synth.py.
msg402424 - (view) Author: Erlend E. Aasland (erlendaasland) * (Python triager) Date: 2021-09-22 12:11
Ouch, that's quite a regression! Thanks for the heads up! I'll have a look at it right away.
msg402425 - (view) Author: Erlend E. Aasland (erlendaasland) * (Python triager) Date: 2021-09-22 12:19
I'm unable to reproduce this regression on my machine (macOS, debug build, no optimisations). Are you able to reproduce, Ken?
msg402427 - (view) Author: Erlend E. Aasland (erlendaasland) * (Python triager) Date: 2021-09-22 12:27
> I'm unable to reproduce this regression on my machine (macOS, debug build, no optimisations) [...]

Correction: I _am_ able to reproduce this.
msg402490 - (view) Author: Erlend E. Aasland (erlendaasland) * (Python triager) Date: 2021-09-23 10:51
Explicitly resetting statements when we're done with them removes the performance regression; SQLite works more efficient when we keep the number of non-reset statements low.
msg402677 - (view) Author: Erlend E. Aasland (erlendaasland) * (Python triager) Date: 2021-09-26 20:11
I'll revert PR 27844 for now (except the tests).

Since SQLite works better when we keep the number of non-reset statements to a minimum, we need to ensure that we reset statements when we're done with them (sqlite3_step() returns SQLITE_DONE or an error). Before doing such a change, we should clean up _pysqlite_query_execute() so we don't need to sprinkle that function with pysqlite_statement_reset's. I plan to do this before attempting to clean up reset usage again.
msg402683 - (view) Author: Pablo Galindo Salgado (pablogsal) * (Python committer) Date: 2021-09-26 21:24
New changeset 7b88f63e1dd4006b1a08b9c9f087dd13449ecc76 by Erlend Egeberg Aasland in branch 'main':
bpo-44958: Revert GH-27844 (GH-28574)
https://github.com/python/cpython/commit/7b88f63e1dd4006b1a08b9c9f087dd13449ecc76
History
Date User Action Args
2022-04-11 14:59:49adminsetgithub: 89121
2022-01-03 23:42:52erlendaaslandsetpull_requests: + pull_request28589
2021-09-26 21:24:25pablogsalsetmessages: + msg402683
2021-09-26 20:23:16erlendaaslandsetpull_requests: + pull_request26956
2021-09-26 20:11:03erlendaaslandsetmessages: + msg402677
2021-09-23 10:51:33erlendaaslandsetmessages: + msg402490
2021-09-22 12:27:53erlendaaslandsetmessages: + msg402427
2021-09-22 12:19:39erlendaaslandsetmessages: + msg402425
2021-09-22 12:11:19erlendaaslandsetmessages: + msg402424
2021-09-22 12:03:51kjsetnosy: + kj
messages: + msg402423
2021-09-21 22:33:36erlendaaslandsetmessages: + msg402402
2021-09-21 14:25:57erlendaaslandsetmessages: + msg402319
2021-09-21 13:16:03miss-islingtonsetnosy: + miss-islington
messages: + msg402317
2021-09-21 12:52:47erlendaaslandsetpull_requests: + pull_request26886
2021-09-21 11:20:38pablogsalsetmessages: + msg402312
2021-09-06 22:35:40erlendaaslandsetmessages: + msg401186
2021-08-20 18:05:33erlendaaslandsetmessages: + msg399984
2021-08-20 07:33:50erlendaaslandsetmessages: - msg399931
2021-08-20 07:33:44erlendaaslandsetmessages: + msg399939
2021-08-19 20:14:31erlendaaslandlinkissue43350 superseder
2021-08-19 20:07:00erlendaaslandsetkeywords: + patch
stage: patch review
pull_requests: + pull_request26307
2021-08-19 20:05:09erlendaaslandcreate