Skip to content

Commit

Permalink
perf: load _doc_before_save sooner to avoid DB call in `check_if_la…
Browse files Browse the repository at this point in the history
…test` (frappe#18666)

* perf: load `_doc_before_save` sooner to avoid DB calls in `check_if_latest`

* fix: specify `for update` in `load_doc_before_save`

(cherry picked from commit 6d45b50)
  • Loading branch information
sagarvora authored and mergify[bot] committed Oct 29, 2022
1 parent 5f7928b commit 2710d08
Showing 1 changed file with 26 additions and 47 deletions.
73 changes: 26 additions & 47 deletions frappe/model/document.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ def insert(
self._set_defaults()
self.set_user_and_timestamp()
self.set_docstatus()
self.load_doc_before_save()
self.check_if_latest()
self._validate_links()
self.check_permission("create")
Expand Down Expand Up @@ -328,6 +329,7 @@ def _save(self, ignore_permissions=None, ignore_version=None) -> "Document":

self.set_user_and_timestamp()
self.set_docstatus()
self.load_doc_before_save()
self.check_if_latest()
self.set_parent_in_children()
self.set_name_in_children()
Expand Down Expand Up @@ -746,49 +748,24 @@ def check_if_latest(self):
Will also validate document transitions (Save > Submit > Cancel) calling
`self.check_docstatus_transition`."""
conflict = False
self._action = "save"
if not self.get("__islocal") and not self.meta.get("is_virtual"):
if self.meta.issingle:
modified = frappe.db.sql(
"""select value from tabSingles
where doctype=%s and field='modified' for update""",
self.doctype,
)
modified = modified and modified[0][0]
if modified and modified != cstr(self._original_modified):
conflict = True
else:
tmp = frappe.db.sql(
"""select modified, docstatus from `tab{}`
where name = %s for update""".format(
self.doctype
),
self.name,
as_dict=True,
)

if not tmp:
frappe.throw(_("Record does not exist"))
else:
tmp = tmp[0]

modified = cstr(tmp.modified)
self._action = "save"
previous = self.get_doc_before_save()

if modified and modified != cstr(self._original_modified):
conflict = True
if not previous or self.meta.get("is_virtual"):
self.check_docstatus_transition(0)
return

self.check_docstatus_transition(tmp.docstatus)
if cstr(previous.modified) != cstr(self._original_modified):
frappe.msgprint(
_("Error: Document has been modified after you have opened it")
+ (f" ({previous.modified}, {self.modified}). ")
+ _("Please refresh to get the latest document."),
raise_exception=frappe.TimestampMismatchError,
)

if conflict:
frappe.msgprint(
_("Error: Document has been modified after you have opened it")
+ (f" ({modified}, {self.modified}). ")
+ _("Please refresh to get the latest document."),
raise_exception=frappe.TimestampMismatchError,
)
else:
self.check_docstatus_transition(0)
if not self.meta.issingle:
self.check_docstatus_transition(previous.docstatus)

def check_docstatus_transition(self, to_docstatus):
"""Ensures valid `docstatus` transition.
Expand Down Expand Up @@ -1048,7 +1025,6 @@ def run_before_save_methods(self):
Will also update title_field if set"""

self.load_doc_before_save()
self.reset_seen()

# before_validate method should be executed before ignoring validations
Expand All @@ -1072,14 +1048,17 @@ def run_before_save_methods(self):
self.set_title_field()

def load_doc_before_save(self):
"""Save load document from db before saving"""
"""load existing document from db before saving"""

self._doc_before_save = None
if not self.is_new():
try:
self._doc_before_save = frappe.get_doc(self.doctype, self.name)
except frappe.DoesNotExistError:
self._doc_before_save = None
frappe.clear_last_message()

if self.is_new():
return

try:
self._doc_before_save = frappe.get_doc(self.doctype, self.name, for_update=True)
except frappe.DoesNotExistError:
frappe.clear_last_message()

def run_post_save_methods(self):
"""Run standard methods after `INSERT` or `UPDATE`. Standard Methods are:
Expand Down

0 comments on commit 2710d08

Please sign in to comment.