Description
Running into an issue where a simple pubnub chat causes Safari to eat up 90% CPU when any channels have been joined. I've isolated it to opening a connection to channels. As soon as I call connect, it will have this issue after refreshing. It wont happen every refresh, but it happens 90% of the time.
Safari Version: Version 17.5 (19618.2.12.11.6)
Prior to refreshing, a lot of errors pop up in the console (~10 times):
Fetch API cannot load [x] due to access control checks.
This occurs even in a simple boilerplate page with nothing on it. This is from a vanilla vite project compilation, with an empty page:
Chat.init({
publishKey: import.meta.env.VITE_PUBNUB_PUB_KEY,
subscribeKey: import.meta.env.VITE_PUBNUB_SUB_KEY,
userId: "1111111",
storeUserActivityTimestamps: true,
}).then((chat) => {
const channel = await chat.getChannel("50")
// connect or join
channel.join((msg) => {
console.log(msg);
});
});
I do not see the errors Fetch API cannot load [x] due to access control checks when reloading via the developer console.
muh_cpu.mp4
If you force the browser to disconnect before reloading, we don't see the same behavior:
window.onbeforeunload = () => {
console.log('unloading!!!!', window.chatRef);
window.chatRef.sdk.disconnect();
}
On the development side, since we're in a Webkit view, i cannot seem to fix it for a reload. But i can at least prevent it from happening during hot module reloads:
import.meta.hot.on('vite:beforeFullReload', () => {
if(window.chat) {
console.log('!!!!UNLOADING CHAT!!!!');
window.chat.sdk.disconnect();
console.log('!!!!SDK DISCONNECTED!!!!');
}
});
However the webkit environment does not fire the events onbeforeunload
etc when refreshing which means this bug is basically stuck.
On reload, it appears that the timers that were scheduled are simply running forever. I set up a setTimeout() to simply disconnect Pubnub and set the object to undefined and the timers still hang out forever.
If I load, then reload the window and call destroy() on the current instance:
setTimeout(() => {
console.log('Calling Destroy()');
window.chatRef.sdk.destroy();
}, 5000)
When we compare snapshots we see that Safari seems to be hanging onto the SDK and throwing partial GCs hundreds of times:
Likewise, when calling destroy()
on the SDK underneath, we still hang onto memory because there is no destroy() function on the ChatSDK. I can manually clean up both and THAT appears to fix the issue. However, this does not work when reloading. This must be called before we reload the window.
setTimeout(() => {
console.log('Calling Destroy()');
window.chatRef.sdk.destroy();
delete window.chatRef;
}, 5000)
Perhaps I am missing something here?