perf: Batched List Updates

* Perform batched list updates for a N documents made every second
* list_update callbacks for doc refreshes maintained in cur_list.pending_document_refreshes
This commit is contained in:
Gavin D'souza 2023-01-31 13:18:27 +05:30
parent b3b846472e
commit 636c4701cf

View file

@ -1320,6 +1320,11 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
}
setup_realtime_updates() {
this.pending_document_refreshes = [];
setInterval(() => {
this.process_document_refreshes();
}, 1000);
if (this.list_view_settings && this.list_view_settings.disable_auto_refresh) {
return;
}
@ -1333,28 +1338,42 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
return;
}
const { doctype, name } = data;
if (doctype !== this.doctype) return;
this.pending_document_refreshes.push(data);
});
}
// filters to get only the doc with this name
const call_args = this.get_call_args();
call_args.args.filters.push([this.doctype, "name", "=", name]);
call_args.args.start = 0;
process_document_refreshes() {
if (!this.pending_document_refreshes.length) return;
frappe.call(call_args).then(({ message }) => {
if (!message) return;
const data = frappe.utils.dict(message.keys, message.values);
if (!(data && data.length)) {
// this doc was changed and should not be visible
// in the listview according to filters applied
// let's remove it manually
this.data = this.data.filter((d) => d.name !== name);
this.render_list();
return;
}
const names = this.pending_document_refreshes
.filter((d) => d.doctype === this.doctype)
.map((d) => d.name);
this.pending_document_refreshes = this.pending_document_refreshes.filter(
(d) => names.indexOf(d.name) === -1
);
const datum = data[0];
const index = this.data.findIndex((d) => d.name === datum.name);
if (!names.length) return;
// filters to get only the doc with this name
const call_args = this.get_call_args();
call_args.args.filters.push([this.doctype, "name", "in", names]);
call_args.args.start = 0;
frappe.call(call_args).then(({ message }) => {
if (!message) return;
const data = frappe.utils.dict(message.keys, message.values);
if (!(data && data.length)) {
// this doc was changed and should not be visible
// in the listview according to filters applied
// let's remove it manually
this.data = this.data.filter((d) => names.indexOf(d.name) === -1);
this.render_list();
return;
}
data.forEach((datum) => {
const index = this.data.findIndex((doc) => doc.name === datum.name);
if (index === -1) {
// append new data
@ -1363,31 +1382,31 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
// update this data in place
this.data[index] = datum;
}
this.data.sort((a, b) => {
const a_value = a[this.sort_by] || "";
const b_value = b[this.sort_by] || "";
let return_value = 0;
if (a_value > b_value) {
return_value = 1;
}
if (b_value > a_value) {
return_value = -1;
}
if (this.sort_order === "desc") {
return_value = -return_value;
}
return return_value;
});
this.toggle_result_area();
this.render_list();
if (this.$checks && this.$checks.length) {
this.set_rows_as_checked();
}
});
this.data.sort((a, b) => {
const a_value = a[this.sort_by] || "";
const b_value = b[this.sort_by] || "";
let return_value = 0;
if (a_value > b_value) {
return_value = 1;
}
if (b_value > a_value) {
return_value = -1;
}
if (this.sort_order === "desc") {
return_value = -return_value;
}
return return_value;
});
if (this.$checks && this.$checks.length) {
this.set_rows_as_checked();
}
this.toggle_result_area();
this.render_list();
});
}