1 | #------------------------------------------------------------------------
|
---|
2 | #
|
---|
3 | # Copyright (C) 2000 Autonomous Zone Industries
|
---|
4 | #
|
---|
5 | # License: This is free software. You may use this software for any
|
---|
6 | # purpose including modification/redistribution, so long as
|
---|
7 | # this header remains intact and that you do not claim any
|
---|
8 | # rights of ownership or authorship of this software. This
|
---|
9 | # software has been tested, but no warranty is expressed or
|
---|
10 | # implied.
|
---|
11 | #
|
---|
12 | # Author: Gregory P. Smith <greg@krypto.org>
|
---|
13 | #
|
---|
14 | # Note: I don't know how useful this is in reality since when a
|
---|
15 | # DBLockDeadlockError happens the current transaction is supposed to be
|
---|
16 | # aborted. If it doesn't then when the operation is attempted again
|
---|
17 | # the deadlock is still happening...
|
---|
18 | # --Robin
|
---|
19 | #
|
---|
20 | #------------------------------------------------------------------------
|
---|
21 |
|
---|
22 |
|
---|
23 | #
|
---|
24 | # import the time.sleep function in a namespace safe way to allow
|
---|
25 | # "from bsddb.dbutils import *"
|
---|
26 | #
|
---|
27 | from time import sleep as _sleep
|
---|
28 |
|
---|
29 | import sys
|
---|
30 | absolute_import = (sys.version_info[0] >= 3)
|
---|
31 | if absolute_import :
|
---|
32 | # Because this syntaxis is not valid before Python 2.5
|
---|
33 | exec("from . import db")
|
---|
34 | else :
|
---|
35 | import db
|
---|
36 |
|
---|
37 | # always sleep at least N seconds between retrys
|
---|
38 | _deadlock_MinSleepTime = 1.0/128
|
---|
39 | # never sleep more than N seconds between retrys
|
---|
40 | _deadlock_MaxSleepTime = 3.14159
|
---|
41 |
|
---|
42 | # Assign a file object to this for a "sleeping" message to be written to it
|
---|
43 | # each retry
|
---|
44 | _deadlock_VerboseFile = None
|
---|
45 |
|
---|
46 |
|
---|
47 | def DeadlockWrap(function, *_args, **_kwargs):
|
---|
48 | """DeadlockWrap(function, *_args, **_kwargs) - automatically retries
|
---|
49 | function in case of a database deadlock.
|
---|
50 |
|
---|
51 | This is a function intended to be used to wrap database calls such
|
---|
52 | that they perform retrys with exponentially backing off sleeps in
|
---|
53 | between when a DBLockDeadlockError exception is raised.
|
---|
54 |
|
---|
55 | A 'max_retries' parameter may optionally be passed to prevent it
|
---|
56 | from retrying forever (in which case the exception will be reraised).
|
---|
57 |
|
---|
58 | d = DB(...)
|
---|
59 | d.open(...)
|
---|
60 | DeadlockWrap(d.put, "foo", data="bar") # set key "foo" to "bar"
|
---|
61 | """
|
---|
62 | sleeptime = _deadlock_MinSleepTime
|
---|
63 | max_retries = _kwargs.get('max_retries', -1)
|
---|
64 | if 'max_retries' in _kwargs:
|
---|
65 | del _kwargs['max_retries']
|
---|
66 | while True:
|
---|
67 | try:
|
---|
68 | return function(*_args, **_kwargs)
|
---|
69 | except db.DBLockDeadlockError:
|
---|
70 | if _deadlock_VerboseFile:
|
---|
71 | _deadlock_VerboseFile.write(
|
---|
72 | 'dbutils.DeadlockWrap: sleeping %1.3f\n' % sleeptime)
|
---|
73 | _sleep(sleeptime)
|
---|
74 | # exponential backoff in the sleep time
|
---|
75 | sleeptime *= 2
|
---|
76 | if sleeptime > _deadlock_MaxSleepTime:
|
---|
77 | sleeptime = _deadlock_MaxSleepTime
|
---|
78 | max_retries -= 1
|
---|
79 | if max_retries == -1:
|
---|
80 | raise
|
---|
81 |
|
---|
82 |
|
---|
83 | #------------------------------------------------------------------------
|
---|