fix: ensure that prepared report is set even on timeout (#25565)

* fix: ensure that prepared report is set even on timeout

Under following condition prepared report is never enabled:
- Process takes too much time and is killed
- HTTP timeout

Fix:
- We spawn a thread and ask it to wait till prepared report threshold
  time is elapsed and set prepared report on it.
- Condvar is used to immdiately wake up and end the thread if report
  finsihed early.

* refactor: use threading.Timer

No need to implement it ourselves
This commit is contained in:
Ankush Menat 2024-03-21 16:48:29 +05:30 committed by GitHub
parent a12fc118f4
commit 6613ed76fe
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -2,6 +2,7 @@
# License: MIT. See LICENSE
import datetime
import json
import threading
import frappe
import frappe.desk.query_report
@ -15,6 +16,8 @@ from frappe.modules.export_file import export_to_files
from frappe.utils import cint, cstr
from frappe.utils.safe_exec import check_safe_sql_query, safe_exec
PREPARED_REPORT_THRESHOLD = 20 # seconds
class Report(Document):
# begin: auto-generated types
@ -153,21 +156,24 @@ class Report(Document):
def execute_script_report(self, filters):
# save the timestamp to automatically set to prepared
threshold = 15
start_time = datetime.datetime.now()
if not self.prepared_report:
set_prepared_report = threading.Timer(
interval=PREPARED_REPORT_THRESHOLD,
function=enable_prepared_report,
kwargs={"report": self.name, "site": frappe.local.site},
)
set_prepared_report.start()
# The JOB
if self.is_standard == "Yes":
res = self.execute_module(filters)
else:
res = self.execute_script(filters)
# automatically set as prepared
set_prepared_report.cancel()
execution_time = (datetime.datetime.now() - start_time).total_seconds()
if execution_time > threshold and not self.prepared_report:
frappe.enqueue(enable_prepared_report, report=self.name)
frappe.cache.hset("report_execution_time", self.name, execution_time)
return res
@ -414,5 +420,9 @@ def get_group_by_column_label(args, meta):
return label
def enable_prepared_report(report: str):
frappe.db.set_value("Report", report, "prepared_report", 1)
def enable_prepared_report(*, site: str, report: str):
frappe.init(site)
frappe.connect()
frappe.db.set_value("Report", report, "prepared_report", 1, update_modified=True)
frappe.db.commit()
frappe.destroy()