14 |
14 |
# You should have received a copy of the GNU Affero General Public License
|
15 |
15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16 |
16 |
|
17 |
|
import math
|
18 |
|
import random
|
19 |
|
import time
|
20 |
|
|
21 |
17 |
from django.conf import settings
|
22 |
|
from django.db import connection
|
23 |
|
|
24 |
|
|
25 |
|
def poisson_random(frequency):
|
26 |
|
'''Generate random numbers following a poisson distribution'''
|
27 |
|
return -math.log(1.0 - random.random()) / frequency
|
28 |
|
|
29 |
|
|
30 |
|
SAFE_GET_OR_CREATE_RETRIES = 3
|
31 |
|
|
32 |
|
|
33 |
|
class ConcurrencyError(Exception):
|
34 |
|
pass
|
|
18 |
from django.db import connection, transaction
|
35 |
19 |
|
36 |
20 |
|
37 |
21 |
def safe_get_or_create(model, defaults=None, **kwargs):
|
... | ... | |
39 |
23 |
getattr(settings, 'TESTING', False) or not connection.in_atomic_block
|
40 |
24 |
), 'safe_get_or_create cannot be used in inside a transaction'
|
41 |
25 |
|
42 |
|
defaults = defaults or {}
|
43 |
|
exception = None
|
44 |
|
for dummy in range(SAFE_GET_OR_CREATE_RETRIES):
|
45 |
|
try:
|
46 |
|
instance, created = model.objects.get_or_create(defaults=defaults, **kwargs)
|
47 |
|
except model.MultipleObjectsReturned as e:
|
48 |
|
exception = e
|
49 |
|
time.sleep(max(poisson_random(1), 0.5))
|
50 |
|
continue
|
51 |
|
|
52 |
|
if created and model.objects.filter(**kwargs).exclude(pk=instance.pk).exists():
|
53 |
|
instance.delete()
|
54 |
|
time.sleep(max(poisson_random(1), 0.5))
|
55 |
|
continue
|
56 |
|
return instance, created
|
57 |
|
raise exception
|
|
26 |
try:
|
|
27 |
return model.objects.get(**kwargs), False
|
|
28 |
except model.DoesNotExist:
|
|
29 |
pass
|
|
30 |
with transaction.atomic():
|
|
31 |
with connection.cursor() as cur:
|
|
32 |
cur.execute('LOCK TABLE "%s" IN EXCLUSIVE MODE' % model._meta.db_table)
|
|
33 |
return model.objects.get_or_create(defaults=defaults, **kwargs)
|