@@ -283,25 +283,27 @@ proc transaction {tables script} {
db-execute "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE"
lock-tables $tables
uplevel 1 $script
+ db-execute COMMIT
} result]
- if {!$rc} {
- if {[catch {
- db-execute COMMIT
- } emsg]} {
- puts "commit failed: $emsg; retrying ..."
- db-execute ROLLBACK
- if {[incr retries -1] <= 0} {
- error \
- "commit failed, too many retries: $emsg\n$errorInfo\n$errorCode\n"
+ set ei $errorInfo
+ set ec $errorCode
+ if {$rc} {
+ db-execute ROLLBACK
+ switch -glob $errorCode {
+ {OSSTEST-PSQL * 40P01} {
+ # DEADLOCK DETECTED
+ puts "transaction deadlock ($result) retrying ..."
+ if {[incr retries -1] <= 0} {
+ error \
+ "transaction failed, too many retries: $result\n$errorInfo\n$errorCode\n"
+ }
+ after 500
+ continue
}
- after 500
- continue
}
- } else {
- db-execute ROLLBACK
}
db-close
- return -code $rc $result
+ return -code $rc -errorinfo $ei -errorcode $ec $result
}
}
Use the new errorCode coming out of db-execute* to tell when the error is that we got a database deadlock, which is the situation in which we should retry. This involves combining the two catch blocks, so that there is only one error handling strategy. Previously errors on COMMIT would be retried and others would not. Now errors anywhere might be retried but only if the DB indicated deadlock. We now unconditionally execute ROLLBACK. This is more correct, since we always previously executed BEGIN. And, we pass the errorInfo and errorCode from the $body to the caller. I have tested this with a test db instance, using contrived means to generate a database deadlock, and it does actually retry. Signed-off-by: Ian Jackson <Ian.Jackson@eu.citrix.com> --- tcl/JobDB-Executive.tcl | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-)