Changes between Initial Version and Version 2 of Ticket #8443
- Timestamp:
- Jul 2, 2009, 10:32:48 PM (15 years ago)
Legend:
- Unmodified
- Added
- Removed
- Modified
-
Ticket #8443 – Description
initial v2 5 5 The database connection pool in trac is called with every env.get_db_cnx call. It has a list of "active" connections, which are connections currently in use, and then the pool. The typical pattern that happens when using a database connection looks like: 6 6 7 - some function in trac is entered8 - get connection9 - if connection in active list for thread, use that10 - otherwise get one from pool (or created one)11 - a cursor is created12 - psycopg may execute a BEGIN statement, based on the default settings for the postgres server. The installed default for postgres causes psycopg to do that.13 - function in trac finishes14 - connection wrapper is garbage collected, causing the wrapper to "reinsert" the connection into the pool15 - if it is the last "reference" to the connection, trac attempts a rollback (which typically matches the begin above)16 - if it is NOT the last "reference" to the connection, and no explicit rollback was done (eg. after an insert, trac/plugins usually do a commit) then the pool also does not rollback (THIS IS THE PROBLEM). The result is that the begin statement above remains in effect, causing "IDLE in transaction".7 - some function in trac is entered 8 - get connection 9 - if connection in active list for thread, use that 10 - otherwise get one from pool (or created one) 11 - a cursor is created 12 - psycopg may execute a BEGIN statement, based on the default settings for the postgres server. The installed default for postgres causes psycopg to do that. 13 - function in trac finishes 14 - connection wrapper is garbage collected, causing the wrapper to "reinsert" the connection into the pool 15 - if it is the last "reference" to the connection, trac attempts a rollback (which typically matches the begin above) 16 - if it is NOT the last "reference" to the connection, and no explicit rollback was done (eg. after an insert, trac/plugins usually do a commit) then the pool also does not rollback (THIS IS THE PROBLEM). The result is that the begin statement above remains in effect, causing "IDLE in transaction". 17 17 18 18 Fixing this in the pool would be the wrong place since you can never know if a rollback is safe on an "active" connection. I'm also wondering if the rollback in there currently is due to bugs reported on this issue before. … … 21 21 22 22 Without requiring a repro, this illustrates how it happens: 23 23 {{{ 24 #!python 24 25 def do_something(env): 25 26 db = env.get_db_cnx() … … 37 38 # once we're done, db is reinserted 38 39 # to the pool and rollback is called 39 40 }}} 40 41 With the above outline, two BEGIN statements were executed, but only on rollback. 41 42 42 43 The simple hack fix is to modify main.dispatch_request to get a handle on the database connection prior to handling the request, then doing a rollback after the request: 43 44 {{{ 45 #!python 44 46 req = Request(environ, start_response) 45 47 try: … … 50 52 if env and not run_once: 51 53 env.shutdown(threading._get_ident()) 52 54 }}} 53 55 I call it a hack fix because it seems ugly to me, but it's the only place that this can be handled reliably without the potential to step on a commit that is in process, and without adding commit/rollback all over the trac (and plugins) code. 54 56 55 57 The fix does not break current transaction area's (eg. after an update/insert) since psycopg will issue a new BEGIN on the next execute if commit or rollback was called. Updates, Inserts, etc. should remain explicit about calling commit, since a select will not get the new data unless that is done. 56 58 57 It also seems like some extra work is happening in the current db pooling, such as creating a PooledConnection class for each call to env.get_db_cnxeven if the connection is in the "active" list.59 It also seems like some extra work is happening in the current db pooling, such as creating a `PooledConnection` class for each call to `env.get_db_cnx` even if the connection is in the "active" list. 58 60 59 61 Looking at other bugs regarding this issue, it seems that is would also be a good idea to force close the connection at the end of a request IF the status is "in transaction" still. Log or raise and error with it as well. This would at least prevent problems around replication and vacuuming, and let people know there is a bug (likely a missing commit in some plugin). 60 61 62 63