From 930d006f4c7d314b791f57cdbae29c994fd9ddf0 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 21 Jan 2025 14:55:58 +0530 Subject: [PATCH] perf: HTTP cache list view counts when count is >1000 (#29871) This allows for caching the count which are frequently requested but rarely change and even if they changed, UI doesn't show accurate value to begin with. After this change any filters that result in "1,000+" response will be just cached by client and won't be requested again and again (for at least 30 minutes, it's SWR after that so UI should still be snappy) --- frappe/desk/reportview.py | 3 +++ frappe/public/js/frappe/db.js | 21 +++++++++++++-------- frappe/public/js/frappe/list/list_view.js | 12 ++++++++---- frappe/public/js/frappe/request.js | 7 ++++--- 4 files changed, 28 insertions(+), 15 deletions(-) diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py index 145130dae7..fef7a0ebc0 100644 --- a/frappe/desk/reportview.py +++ b/frappe/desk/reportview.py @@ -67,6 +67,9 @@ def get_count() -> int: args.fields = [fieldname] partial_query = execute(**args, run=0) count = frappe.db.sql(f"""select count(*) from ( {partial_query} ) p""")[0][0] + + if count == args.limit: + frappe.response.can_cache = True else: args.fields = [f"count({fieldname}) as total_count"] count = execute(**args)[0].get("total_count") diff --git a/frappe/public/js/frappe/db.js b/frappe/public/js/frappe/db.js index 3affd94f89..469182ace8 100644 --- a/frappe/public/js/frappe/db.js +++ b/frappe/public/js/frappe/db.js @@ -102,7 +102,7 @@ frappe.db = { frappe.call("frappe.client.delete", { doctype, name }, (r) => resolve(r.message)); }); }, - count: function (doctype, args = {}) { + count: function (doctype, args = {}, cache = false) { let filters = args.filters || {}; let limit = args.limit; @@ -115,13 +115,18 @@ frappe.db = { const fields = []; - return frappe.xcall("frappe.desk.reportview.get_count", { - doctype, - filters, - fields, - distinct, - limit, - }); + return frappe.xcall( + "frappe.desk.reportview.get_count", + { + doctype, + filters, + fields, + distinct, + limit, + }, + cache ? "GET" : "POST", + { cache } + ); }, get_link_options(doctype, txt = "", filters = {}) { return new Promise((resolve) => { diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js index f2a867add4..0c8cccce9b 100644 --- a/frappe/public/js/frappe/list/list_view.js +++ b/frappe/public/js/frappe/list/list_view.js @@ -1008,10 +1008,14 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { let count_without_children = this.data.uniqBy((d) => d.name).length; return frappe.db - .count(this.doctype, { - filters: this.get_filters_for_args(), - limit: this.count_upper_bound, - }) + .count( + this.doctype, + { + filters: this.get_filters_for_args(), + limit: this.count_upper_bound, + }, + Boolean(this.count_upper_bound) + ) .then((total_count) => { this.total_count = total_count || current_count; this.count_without_children = diff --git a/frappe/public/js/frappe/request.js b/frappe/public/js/frappe/request.js index 7bd2823f21..50a26bcf0f 100644 --- a/frappe/public/js/frappe/request.js +++ b/frappe/public/js/frappe/request.js @@ -10,7 +10,7 @@ frappe.request.ajax_count = 0; frappe.request.waiting_for_ajax = []; frappe.request.logs = {}; -frappe.xcall = function (method, params, type) { +frappe.xcall = function (method, params, type, opts = {}) { return new Promise((resolve, reject) => { frappe.call({ method: method, @@ -22,6 +22,7 @@ frappe.xcall = function (method, params, type) { error: (r) => { reject(r?.message); }, + ...opts, }); }); }; @@ -118,11 +119,11 @@ frappe.call = function (opts) { freeze_message: opts.freeze_message, headers: opts.headers || {}, error_handlers: opts.error_handlers || {}, - // show_spinner: !opts.no_spinner, async: opts.async, silent: opts.silent, api_version: opts.api_version, url, + cache: opts.cache, }); }; @@ -268,7 +269,7 @@ frappe.request.call = function (opts) { }, opts.headers ), - cache: window.dev_server ? false : true, + cache: window.dev_server ? false : opts.cache || false, }; if (opts.args && opts.args.doctype) {