From be8d2b9de0427688bb8902eed0ed3ff58619b8d7 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Fri, 30 Jun 2023 00:40:55 +0530 Subject: [PATCH] perf: lazy websocket connection on website Establishing 1 connection for every website visit is too much. Only after calling frappe.realtime.on(...) for ANY event, we will establish a websocket connection. This is used for handful of things: - Discussion component - File upload Socketio was initially added here: https://github.com/frappe/frappe/pull/6866 this use case no longer exists. Rarely anywhere website uses realtime. --- .../js/frappe/build_events/BuildError.vue | 4 +- frappe/public/js/frappe/desk.js | 2 +- frappe/public/js/frappe/form/form.js | 2 +- frappe/public/js/frappe/form/toolbar.js | 2 +- frappe/public/js/frappe/list/list_view.js | 4 +- frappe/public/js/frappe/request.js | 2 +- frappe/public/js/frappe/socketio_client.js | 43 ++++++++++++------- .../js/frappe/views/reports/report_view.js | 2 +- frappe/templates/discussions/discussions.js | 8 ++-- frappe/website/js/website.js | 2 +- realtime/middlewares/authenticate.js | 1 - 11 files changed, 42 insertions(+), 30 deletions(-) diff --git a/frappe/public/js/frappe/build_events/BuildError.vue b/frappe/public/js/frappe/build_events/BuildError.vue index 13c0ce39a2..4cdbbd85c0 100644 --- a/frappe/public/js/frappe/build_events/BuildError.vue +++ b/frappe/public/js/frappe/build_events/BuildError.vue @@ -24,7 +24,7 @@ function hide() { data.value = null; } function open_in_editor(location) { - frappe.socketio.socket.emit("open_in_editor", location); + frappe.realtime.emit("open_in_editor", location); } function error_component(error, i) { let location = data.value.error.errors[i].location; @@ -40,7 +40,7 @@ function error_component(error, i) { template: `
${template}
`, methods: { open() { - frappe.socketio.socket.emit("open_in_editor", location); + frappe.realtime.emit("open_in_editor", location); } } }; diff --git a/frappe/public/js/frappe/desk.js b/frappe/public/js/frappe/desk.js index a797c6302f..5476dc9dbf 100644 --- a/frappe/public/js/frappe/desk.js +++ b/frappe/public/js/frappe/desk.js @@ -31,7 +31,7 @@ frappe.Application = class Application { } startup() { - frappe.socketio.init(); + frappe.realtime.init(); frappe.model.init(); this.load_bootinfo(); diff --git a/frappe/public/js/frappe/form/form.js b/frappe/public/js/frappe/form/form.js index 876be1867a..e8fa017cf0 100644 --- a/frappe/public/js/frappe/form/form.js +++ b/frappe/public/js/frappe/form/form.js @@ -1950,7 +1950,7 @@ frappe.ui.form.Form = class FrappeForm { let docname = this.docname; if (this.doc && !this.is_new()) { - frappe.socketio.doc_subscribe(doctype, docname); + frappe.realtime.doc_subscribe(doctype, docname); } frappe.realtime.off("docinfo_update"); frappe.realtime.on("docinfo_update", ({ doc, key, action = "update" }) => { diff --git a/frappe/public/js/frappe/form/toolbar.js b/frappe/public/js/frappe/form/toolbar.js index fa1d7330cf..60b90fe9db 100644 --- a/frappe/public/js/frappe/form/toolbar.js +++ b/frappe/public/js/frappe/form/toolbar.js @@ -108,7 +108,7 @@ frappe.ui.form.Toolbar = class Toolbar { } let rename_document = () => { - if (input_name != docname) frappe.socketio.doctype_subscribe(doctype, input_name); + if (input_name != docname) frappe.realtime.doctype_subscribe(doctype, input_name); return frappe .xcall("frappe.model.rename_doc.update_document_title", { doctype, diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js index 1ca72a4e45..10f933aecd 100644 --- a/frappe/public/js/frappe/list/list_view.js +++ b/frappe/public/js/frappe/list/list_view.js @@ -1362,7 +1362,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { if (this.list_view_settings?.disable_auto_refresh || this.realtime_events_setup) { return; } - frappe.socketio.doctype_subscribe(this.doctype); + frappe.realtime.doctype_subscribe(this.doctype); frappe.realtime.off("list_update"); frappe.realtime.on("list_update", (data) => { if (data?.doctype !== this.doctype) { @@ -1385,7 +1385,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { } disable_realtime_updates() { - frappe.socketio.doctype_unsubscribe(this.doctype); + frappe.realtime.doctype_unsubscribe(this.doctype); this.realtime_events_setup = false; } diff --git a/frappe/public/js/frappe/request.js b/frappe/public/js/frappe/request.js index 47ca1f7548..69867a6b14 100644 --- a/frappe/public/js/frappe/request.js +++ b/frappe/public/js/frappe/request.js @@ -75,7 +75,7 @@ frappe.call = function (opts) { var callback = function (data, response_text) { if (data.task_id) { // async call, subscribe - frappe.socketio.subscribe(data.task_id, opts); + frappe.realtime.subscribe(data.task_id, opts); if (opts.queued) { opts.queued(data); diff --git a/frappe/public/js/frappe/socketio_client.js b/frappe/public/js/frappe/socketio_client.js index 7084e9e2df..4797756baa 100644 --- a/frappe/public/js/frappe/socketio_client.js +++ b/frappe/public/js/frappe/socketio_client.js @@ -10,6 +10,7 @@ class RealTimeClient { on(event, callback) { if (this.socket) { + this.connect(); this.socket.on(event, callback); } } @@ -20,7 +21,19 @@ class RealTimeClient { } } - init(port = 9000) { + connect() { + if (this.lazy_connect) { + this.socket.connect(); + this.lazy_connect = false; + } + } + + emit(event, ...args) { + this.connect(); + this.socket.emit(event, ...args); + } + + init(port = 9000, lazy_connect = false) { if (frappe.boot.disable_async) { return; } @@ -28,7 +41,7 @@ class RealTimeClient { if (this.socket) { return; } - + this.lazy_connect = lazy_connect; let me = this; // Enable secure option when using HTTPS @@ -37,11 +50,13 @@ class RealTimeClient { secure: true, withCredentials: true, reconnectionAttempts: 3, + autoConnect: !lazy_connect, }); } else if (window.location.protocol == "http:") { this.socket = io(this.get_host(port), { withCredentials: true, reconnectionAttempts: 3, + autoConnect: !lazy_connect, }); } @@ -108,24 +123,22 @@ class RealTimeClient { } subscribe(task_id, opts) { - // TODO DEPRECATE - - this.socket.emit("task_subscribe", task_id); - this.socket.emit("progress_subscribe", task_id); + this.emit("task_subscribe", task_id); + this.emit("progress_subscribe", task_id); this.open_tasks[task_id] = opts; } task_subscribe(task_id) { - this.socket.emit("task_subscribe", task_id); + this.emit("task_subscribe", task_id); } task_unsubscribe(task_id) { - this.socket.emit("task_unsubscribe", task_id); + this.emit("task_unsubscribe", task_id); } doctype_subscribe(doctype) { - this.socket.emit("doctype_subscribe", doctype); + this.emit("doctype_subscribe", doctype); } doctype_unsubscribe(doctype) { - this.socket.emit("doctype_unsubscribe", doctype); + this.emit("doctype_unsubscribe", doctype); } doc_subscribe(doctype, docname) { if (frappe.flags.doc_subscribe) { @@ -143,18 +156,18 @@ class RealTimeClient { frappe.flags.doc_subscribe = false; }, 1000); - this.socket.emit("doc_subscribe", doctype, docname); + this.emit("doc_subscribe", doctype, docname); this.open_docs.add(`${doctype}:${docname}`); } doc_unsubscribe(doctype, docname) { - this.socket.emit("doc_unsubscribe", doctype, docname); + this.emit("doc_unsubscribe", doctype, docname); return this.open_docs.delete(`${doctype}:${docname}`); } doc_open(doctype, docname) { - this.socket.emit("doc_open", doctype, docname); + this.emit("doc_open", doctype, docname); } doc_close(doctype, docname) { - this.socket.emit("doc_close", doctype, docname); + this.emit("doc_close", doctype, docname); } setup_listeners() { this.socket.on("task_status_change", function (data) { @@ -194,7 +207,7 @@ class RealTimeClient { publish(event, message) { if (this.socket) { - this.socket.emit(event, message); + this.emit(event, message); } } } diff --git a/frappe/public/js/frappe/views/reports/report_view.js b/frappe/public/js/frappe/views/reports/report_view.js index b45f2346f1..2efe2314f5 100644 --- a/frappe/public/js/frappe/views/reports/report_view.js +++ b/frappe/public/js/frappe/views/reports/report_view.js @@ -56,7 +56,7 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView { if (this.list_view_settings?.disable_auto_refresh) { return; } - frappe.socketio.doctype_subscribe(this.doctype); + frappe.realtime.doctype_subscribe(this.doctype); frappe.realtime.on("list_update", (data) => this.on_update(data)); } diff --git a/frappe/templates/discussions/discussions.js b/frappe/templates/discussions/discussions.js index ad6d13e834..56ab3e1ed2 100644 --- a/frappe/templates/discussions/discussions.js +++ b/frappe/templates/discussions/discussions.js @@ -69,14 +69,14 @@ const show_new_topic_modal = (e) => { }; const setup_socket_io = () => { - frappe.socketio.init(window.socketio_port || "9000"); - frappe.socketio.socket.on("publish_message", (data) => { + frappe.realtime.init(window.socketio_port || "9000"); + frappe.realtime.on("publish_message", (data) => { publish_message(data); }); - frappe.socketio.socket.on("update_message", (data) => { + frappe.realtime.on("update_message", (data) => { update_message(data); }); - frappe.socketio.socket.on("delete_message", (data) => { + frappe.realtime.socket.on("delete_message", (data) => { delete_message(data); }); }; diff --git a/frappe/website/js/website.js b/frappe/website/js/website.js index a93c4d88cf..13f6390e97 100644 --- a/frappe/website/js/website.js +++ b/frappe/website/js/website.js @@ -653,5 +653,5 @@ $(document).on("page-change", function () { frappe.ready(function () { frappe.show_language_picker(); frappe.setup_videos(); - frappe.socketio.init(window.socketio_port); + frappe.realtime.init(window.socketio_port, true); // lazy connection }); diff --git a/realtime/middlewares/authenticate.js b/realtime/middlewares/authenticate.js index 1f8876a1da..6d81cd2e15 100644 --- a/realtime/middlewares/authenticate.js +++ b/realtime/middlewares/authenticate.js @@ -56,7 +56,6 @@ function get_site_name(socket) { conf.default_site && ["localhost", "127.0.0.1"].indexOf(get_hostname(socket.request.headers.host)) !== -1 ) { - // from currentsite.txt since host is localhost socket.site_name = conf.default_site; } else if (socket.request.headers.origin) { socket.site_name = get_hostname(socket.request.headers.origin);