Locking the element on take_snapshot to avoid racing condition issues
parent
985838cd1a
commit
50fd9b6161
|
@ -30,6 +30,7 @@ django-ipware==0.1.0
|
||||||
premailer==2.8.1
|
premailer==2.8.1
|
||||||
django-transactional-cleanup==0.1.13
|
django-transactional-cleanup==0.1.13
|
||||||
lxml==3.4.1
|
lxml==3.4.1
|
||||||
|
git+https://github.com/Xof/django-pglocks.git@dbb8d7375066859f897604132bd437832d2014ea
|
||||||
|
|
||||||
# Comment it if you are using python >= 3.4
|
# Comment it if you are using python >= 3.4
|
||||||
enum34==1.0
|
enum34==1.0
|
||||||
|
|
|
@ -37,6 +37,7 @@ from django.contrib.contenttypes.models import ContentType
|
||||||
from django.core.paginator import Paginator, InvalidPage
|
from django.core.paginator import Paginator, InvalidPage
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
from django.db import transaction as tx
|
from django.db import transaction as tx
|
||||||
|
from django_pglocks import advisory_lock
|
||||||
|
|
||||||
from taiga.mdrender.service import render as mdrender
|
from taiga.mdrender.service import render as mdrender
|
||||||
from taiga.base.utils.db import get_typename_for_model_class
|
from taiga.base.utils.db import get_typename_for_model_class
|
||||||
|
@ -269,6 +270,7 @@ def get_modified_fields(obj:object, last_modifications):
|
||||||
|
|
||||||
return modified_fields
|
return modified_fields
|
||||||
|
|
||||||
|
|
||||||
@tx.atomic
|
@tx.atomic
|
||||||
def take_snapshot(obj:object, *, comment:str="", user=None, delete:bool=False):
|
def take_snapshot(obj:object, *, comment:str="", user=None, delete:bool=False):
|
||||||
"""
|
"""
|
||||||
|
@ -280,56 +282,57 @@ def take_snapshot(obj:object, *, comment:str="", user=None, delete:bool=False):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
key = make_key_from_model_object(obj)
|
key = make_key_from_model_object(obj)
|
||||||
typename = get_typename_for_model_class(obj.__class__)
|
with advisory_lock(key) as acquired_key_lock:
|
||||||
|
typename = get_typename_for_model_class(obj.__class__)
|
||||||
|
|
||||||
new_fobj = freeze_model_instance(obj)
|
new_fobj = freeze_model_instance(obj)
|
||||||
old_fobj, need_real_snapshot = get_last_snapshot_for_key(key)
|
old_fobj, need_real_snapshot = get_last_snapshot_for_key(key)
|
||||||
|
|
||||||
entry_model = apps.get_model("history", "HistoryEntry")
|
entry_model = apps.get_model("history", "HistoryEntry")
|
||||||
user_id = None if user is None else user.id
|
user_id = None if user is None else user.id
|
||||||
user_name = "" if user is None else user.get_full_name()
|
user_name = "" if user is None else user.get_full_name()
|
||||||
|
|
||||||
# Determine history type
|
# Determine history type
|
||||||
if delete:
|
if delete:
|
||||||
entry_type = HistoryType.delete
|
entry_type = HistoryType.delete
|
||||||
elif new_fobj and not old_fobj:
|
elif new_fobj and not old_fobj:
|
||||||
entry_type = HistoryType.create
|
entry_type = HistoryType.create
|
||||||
elif new_fobj and old_fobj:
|
elif new_fobj and old_fobj:
|
||||||
entry_type = HistoryType.change
|
entry_type = HistoryType.change
|
||||||
else:
|
else:
|
||||||
raise RuntimeError("Unexpected condition")
|
raise RuntimeError("Unexpected condition")
|
||||||
|
|
||||||
fdiff = make_diff(old_fobj, new_fobj)
|
fdiff = make_diff(old_fobj, new_fobj)
|
||||||
|
|
||||||
# If diff and comment are empty, do
|
# If diff and comment are empty, do
|
||||||
# not create empty history entry
|
# not create empty history entry
|
||||||
if (not fdiff.diff and not comment
|
if (not fdiff.diff and not comment
|
||||||
and old_fobj is not None
|
and old_fobj is not None
|
||||||
and entry_type != HistoryType.delete):
|
and entry_type != HistoryType.delete):
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
fvals = make_diff_values(typename, fdiff)
|
fvals = make_diff_values(typename, fdiff)
|
||||||
|
|
||||||
if len(comment) > 0:
|
if len(comment) > 0:
|
||||||
is_hidden = False
|
is_hidden = False
|
||||||
else:
|
else:
|
||||||
is_hidden = is_hidden_snapshot(fdiff)
|
is_hidden = is_hidden_snapshot(fdiff)
|
||||||
|
|
||||||
kwargs = {
|
kwargs = {
|
||||||
"user": {"pk": user_id, "name": user_name},
|
"user": {"pk": user_id, "name": user_name},
|
||||||
"key": key,
|
"key": key,
|
||||||
"type": entry_type,
|
"type": entry_type,
|
||||||
"snapshot": fdiff.snapshot if need_real_snapshot else None,
|
"snapshot": fdiff.snapshot if need_real_snapshot else None,
|
||||||
"diff": fdiff.diff,
|
"diff": fdiff.diff,
|
||||||
"values": fvals,
|
"values": fvals,
|
||||||
"comment": comment,
|
"comment": comment,
|
||||||
"comment_html": mdrender(obj.project, comment),
|
"comment_html": mdrender(obj.project, comment),
|
||||||
"is_hidden": is_hidden,
|
"is_hidden": is_hidden,
|
||||||
"is_snapshot": need_real_snapshot,
|
"is_snapshot": need_real_snapshot,
|
||||||
}
|
}
|
||||||
|
|
||||||
return entry_model.objects.create(**kwargs)
|
return entry_model.objects.create(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
# High level query api
|
# High level query api
|
||||||
|
|
Loading…
Reference in New Issue