Skip to content
Snippets Groups Projects
Commit a7233123 authored by Alexandre Kühn's avatar Alexandre Kühn
Browse files

[FIX] bus, im_livechat: lazy start bus worker


Before this commit, the bus service was starting worker at page
load, regardless on whether the user interacts with chat features.

This commit starts bus worker only when it's really useful, e.g.
when adding a new channel to subscribe. Functionally, on livechat,
the worker does not start before the livechat is open.

Task-3266973

closes odoo/odoo#119525

Signed-off-by: default avatarStockbauer Matthieu (tsm) <tsm@odoo.com>
parent 811cf78e
No related branches found
No related tags found
No related merge requests found
......@@ -29,6 +29,7 @@ export const busService = {
let isActive = false;
let isInitialized = false;
let isUsingSharedWorker = browser.SharedWorker && !isIosApp();
const startTs = new Date().getTime();
const connectionInitializedDeferred = new Deferred();
/**
......@@ -40,6 +41,9 @@ export const busService = {
* executed.
*/
function send(action, data) {
if (!worker) {
return;
}
const message = { action, data };
if (isUsingSharedWorker) {
worker.port.postMessage(message);
......@@ -92,6 +96,7 @@ export const busService = {
debug: odoo.debug,
lastNotificationId: multiTab.getSharedValue('last_notification_id', 0),
uid,
startTs,
});
}
......@@ -148,12 +153,14 @@ export const busService = {
}
});
browser.addEventListener('offline', () => send('stop'));
startWorker();
await connectionInitializedDeferred;
return {
addEventListener: bus.addEventListener.bind(bus),
addChannel: channel => {
addChannel: async channel => {
if (!worker) {
startWorker();
await connectionInitializedDeferred;
}
send('add_channel', channel);
send('start');
isActive = true;
......@@ -163,7 +170,11 @@ export const busService = {
trigger: bus.trigger.bind(bus),
removeEventListener: bus.removeEventListener.bind(bus),
send: (eventName, data) => send('send', { event_name: eventName, data }),
start: () => {
start: async () => {
if (!worker) {
startWorker();
await connectionInitializedDeferred;
}
send('start');
isActive = true;
},
......
......@@ -34,7 +34,7 @@ export const WEBSOCKET_CLOSE_CODES = Object.freeze({
});
// Should be incremented on every worker update in order to force
// update of the worker in browser cache.
export const WORKER_VERSION = '1.0.4';
export const WORKER_VERSION = '1.0.5';
const INITIAL_RECONNECT_DELAY = 1000;
const MAXIMUM_RECONNECT_DELAY = 60000;
......@@ -47,6 +47,8 @@ const MAXIMUM_RECONNECT_DELAY = 60000;
*/
export class WebsocketWorker {
constructor() {
// Timestamp of start of most recent bus service sender
this.newestStartTs = undefined;
this.websocketURL = "";
this.currentUID = null;
this.isWaitingForNewUID = true;
......@@ -214,8 +216,16 @@ export class WebsocketWorker {
* - Number: user is logged whether on the frontend/backend.
* - false: user is not logged.
* - undefined: not available (e.g. livechat support page)
* @param {Number} param0.startTs Timestamp of start of bus service sender.
*/
_initializeConnection(client, { debug, lastNotificationId, uid, websocketURL }) {
_initializeConnection(client, { debug, lastNotificationId, uid, websocketURL, startTs }) {
if (this.newestStartTs && this.newestStartTs > startTs) {
this.debugModeByClient[client] = debug;
this.isDebug = Object.values(this.debugModeByClient).some(debugValue => debugValue !== '');
this.sendToClient(client, "initialized");
return;
}
this.newestStartTs = startTs;
this.websocketURL = websocketURL;
this.lastNotificationId = lastNotificationId;
this.debugModeByClient[client] = debug;
......
......@@ -36,6 +36,7 @@ QUnit.module('Bus', {
const pyEnv = await startServer();
const env = await makeTestEnv({ activateMockServer: true });
await env.services['bus_service'].start();
env.services['bus_service'].addEventListener('notification', ({ detail: notifications }) => {
assert.step('notification - ' + notifications.map(notif => notif.payload).toString());
});
......@@ -58,6 +59,8 @@ QUnit.module('Bus', {
const pyEnv = await startServer();
const firstTabEnv = await makeTestEnv({ activateMockServer: true });
const secondTabEnv = await makeTestEnv({ activateMockServer: true });
await firstTabEnv.services['bus_service'].start();
await secondTabEnv.services['bus_service'].start();
firstTabEnv.services['bus_service'].addEventListener('notification', ({ detail: notifications }) => {
assert.step('1 - notification - ' + notifications.map(notif => notif.payload).toString());
......@@ -128,15 +131,16 @@ QUnit.module('Bus', {
mainEnv.services['bus_service'].addEventListener('notification', ({ detail: notifications }) => {
steps.add('main - notification - ' + notifications.map(notif => notif.payload).toString());
});
mainEnv.services['bus_service'].addChannel('lambda');
await mainEnv.services['bus_service'].addChannel('lambda');
// slave
const slaveEnv = await makeTestEnv();
await slaveEnv.services['bus_service'].start();
slaveEnv.services['bus_service'].addEventListener('notification', ({ detail: notifications }) => {
steps.add('slave - notification - ' + notifications.map(notif => notif.payload).toString());
});
slaveEnv.services['bus_service'].addChannel('lambda');
await slaveEnv.services['bus_service'].addChannel('lambda');
pyEnv['bus.bus']._sendone('lambda', 'notifType', 'beta');
// Wait one tick for the worker `postMessage` to reach the bus_service.
......@@ -157,6 +161,7 @@ QUnit.module('Bus', {
const steps = new Set();
// main
const mainEnv = await makeTestEnv({ activateMockServer: true });
await mainEnv.services['bus_service'].start();
mainEnv.services['bus_service'].addEventListener('notification', ({ detail: notifications }) => {
steps.add('main - notification - ' + notifications.map(notif => notif.payload).toString());
});
......@@ -330,7 +335,8 @@ QUnit.module('Bus', {
return this._super(...arguments);
},
});
await makeTestEnv();
const env1 = await makeTestEnv();
await env1.services['bus_service'].start();
await updateLastNotificationDeferred;
// First bus service has never received notifications thus the
// default is 0.
......@@ -344,7 +350,8 @@ QUnit.module('Bus', {
await nextTick();
updateLastNotificationDeferred = makeDeferred();
await makeTestEnv();
const env2 = await makeTestEnv();
await env2.services['bus_service'].start();
await updateLastNotificationDeferred;
// Second bus service sends the last known notification id.
assert.verifySteps([`initialize_connection - 1`]);
......@@ -365,9 +372,11 @@ QUnit.module('Bus', {
});
const firstTabEnv = await makeTestEnv();
firstTabEnv.services["bus_service"].start();
await firstTabEnv.services["bus_service"].start();
firstTabEnv.services['bus_service'].addEventListener('connect', () => {
assert.step('connect');
if (session.user_id) {
assert.step('connect');
}
connectionOpenedDeferred.resolve();
connectionOpenedDeferred = makeDeferred();
});
......@@ -382,8 +391,8 @@ QUnit.module('Bus', {
patchWithCleanup(session, {
user_id: false,
});
await makeTestEnv();
await nextTick();
const env2 = await makeTestEnv();
await env2.services['bus_service'].start();
assert.verifySteps([
'connect',
......@@ -406,7 +415,7 @@ QUnit.module('Bus', {
});
const firstTabEnv = await makeTestEnv();
firstTabEnv.services['bus_service'].start();
await firstTabEnv.services['bus_service'].start();
firstTabEnv.services['bus_service'].addEventListener('connect', () => {
assert.step("connect");
websocketConnectedDeferred.resolve();
......@@ -424,7 +433,7 @@ QUnit.module('Bus', {
user_id: 1,
});
const env = await makeTestEnv();
env.services["bus_service"].start();
await env.services["bus_service"].start();
await websocketConnectedDeferred;
assert.verifySteps([
'connect',
......@@ -458,7 +467,7 @@ QUnit.module('Bus', {
const env = await makeTestEnv();
env.services["bus_service"].addEventListener("connect", () => assert.step("connect"));
env.services["bus_service"].addEventListener("disconnect", () => assert.step("disconnect"));
env.services["bus_service"].start();
await env.services["bus_service"].start();
window.dispatchEvent(new Event("offline"));
await nextTick();
window.dispatchEvent(new Event("online"));
......@@ -568,7 +577,8 @@ QUnit.module('Bus', {
assert.step(message);
},
})
await makeTestEnv();
const env = await makeTestEnv();
await env.services['bus_service'].start();
assert.verifySteps([
"shared-worker creation",
"Error while loading \"bus_service\" SharedWorker, fallback on Worker.",
......
......@@ -148,7 +148,7 @@ registerModel({
await this._sendMessage(message);
this._sendMessageChatbotAfter();
},
start() {
async start() {
if (!this.messaging.publicLivechatGlobal.hasWebsiteLivechatFeature) {
this.widget.$el.text(this.buttonText);
}
......@@ -157,7 +157,7 @@ registerModel({
for (const m of this.messaging.publicLivechatGlobal.history) {
this.addMessage(m);
}
this.openChat();
await this.openChat();
} else if (!this.messaging.device.isSmall && this.messaging.publicLivechatGlobal.rule.action === 'auto_popup') {
const autoPopupCookie = getCookie('im_livechat_auto_popup');
if (!autoPopupCookie || JSON.parse(autoPopupCookie)) {
......@@ -184,7 +184,7 @@ registerModel({
/**
* @private
*/
_openChat() {
async _openChat() {
if (this.isOpeningChat) {
return;
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment