mirror of
https://github.com/finos/SymphonyElectron.git
synced 2024-12-27 01:11:13 -06:00
Electron-136 (Optimize memory by reloading the app when the user is idle) (#335)
- Refresh app when memory exceeds the threshold and if user is inactive - Add logic to reload app when memory exceeds threshold and if user is inactive - Add reload threshold - Fix unit test
This commit is contained in:
parent
2e25c97bec
commit
2de86c8003
@ -6,6 +6,7 @@
|
|||||||
"bringToFront": false,
|
"bringToFront": false,
|
||||||
"whitelistUrl": "*",
|
"whitelistUrl": "*",
|
||||||
"isCustomTitleBar": true,
|
"isCustomTitleBar": true,
|
||||||
|
"memoryRefresh": true,
|
||||||
"notificationSettings": {
|
"notificationSettings": {
|
||||||
"position": "upper-right",
|
"position": "upper-right",
|
||||||
"display": ""
|
"display": ""
|
||||||
|
@ -5,6 +5,13 @@ const throttle = require('../utils/throttle');
|
|||||||
const log = require('../log.js');
|
const log = require('../log.js');
|
||||||
const logLevels = require('../enums/logLevels.js');
|
const logLevels = require('../enums/logLevels.js');
|
||||||
|
|
||||||
|
let setIsAutoReload;
|
||||||
|
if (!process.env.ELECTRON_QA) {
|
||||||
|
/* eslint-disable global-require */
|
||||||
|
setIsAutoReload = require('../windowMgr').setIsAutoReload;
|
||||||
|
/* eslint-enable global-require */
|
||||||
|
}
|
||||||
|
|
||||||
let maxIdleTime;
|
let maxIdleTime;
|
||||||
let activityWindow;
|
let activityWindow;
|
||||||
let intervalId;
|
let intervalId;
|
||||||
@ -51,6 +58,9 @@ function monitorUserActivity() {
|
|||||||
if (systemIdleTime.getIdleTime() < maxIdleTime) {
|
if (systemIdleTime.getIdleTime() < maxIdleTime) {
|
||||||
// If system is active, send an update to the app bridge and clear the timer
|
// If system is active, send an update to the app bridge and clear the timer
|
||||||
sendActivity();
|
sendActivity();
|
||||||
|
if (typeof setIsAutoReload === 'function') {
|
||||||
|
setIsAutoReload(false);
|
||||||
|
}
|
||||||
clearInterval(intervalId);
|
clearInterval(intervalId);
|
||||||
intervalId = undefined;
|
intervalId = undefined;
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ const dirs = new AppDirectory('Symphony');
|
|||||||
let userConfig;
|
let userConfig;
|
||||||
let globalConfig;
|
let globalConfig;
|
||||||
|
|
||||||
let ignoreSettings = ['minimizeOnClose', 'launchOnStartup', 'alwaysOnTop', 'url'];
|
let ignoreSettings = ['minimizeOnClose', 'launchOnStartup', 'alwaysOnTop', 'url', 'memoryRefresh'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tries to read given field from user config file, if field doesn't exist
|
* Tries to read given field from user config file, if field doesn't exist
|
||||||
|
@ -19,7 +19,8 @@ const cmds = keyMirror({
|
|||||||
sanitize: null,
|
sanitize: null,
|
||||||
bringToFront: null,
|
bringToFront: null,
|
||||||
openScreenPickerWindow: null,
|
openScreenPickerWindow: null,
|
||||||
popupMenu: null
|
popupMenu: null,
|
||||||
|
optimizeMemoryConsumption: null
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
@ -17,6 +17,7 @@ const { bringToFront } = require('./bringToFront.js');
|
|||||||
const eventEmitter = require('./eventEmitter');
|
const eventEmitter = require('./eventEmitter');
|
||||||
const { isMac } = require('./utils/misc');
|
const { isMac } = require('./utils/misc');
|
||||||
const { openScreenPickerWindow } = require('./desktopCapturer');
|
const { openScreenPickerWindow } = require('./desktopCapturer');
|
||||||
|
const { optimizeMemory } = require('./memoryMonitor');
|
||||||
|
|
||||||
const apiEnums = require('./enums/api.js');
|
const apiEnums = require('./enums/api.js');
|
||||||
const apiCmds = apiEnums.cmds;
|
const apiCmds = apiEnums.cmds;
|
||||||
@ -147,6 +148,11 @@ electron.ipcMain.on(apiName, (event, arg) => {
|
|||||||
windowMgr.getMenu().popup(browserWin, { x: 20, y: 15, async: true });
|
windowMgr.getMenu().popup(browserWin, { x: 20, y: 15, async: true });
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case apiCmds.optimizeMemoryConsumption:
|
||||||
|
if (typeof arg.memory === 'object' && typeof arg.memory.workingSetSize === 'number') {
|
||||||
|
optimizeMemory(arg.memory);
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,14 @@
|
|||||||
|
|
||||||
const log = require('./log.js');
|
const log = require('./log.js');
|
||||||
const logLevels = require('./enums/logLevels.js');
|
const logLevels = require('./enums/logLevels.js');
|
||||||
|
const { getMainWindow, setIsAutoReload } = require('./windowMgr');
|
||||||
|
const systemIdleTime = require('@paulcbetts/system-idle-time');
|
||||||
|
const { getConfigField } = require('./config');
|
||||||
|
|
||||||
|
const maxMemory = 800;
|
||||||
|
let maxIdleTime = 4 * 60 * 1000;
|
||||||
|
let reloadThreshold = 30 * 60 * 1000;
|
||||||
|
let reloadedTimeStamp;
|
||||||
|
|
||||||
// once a minute
|
// once a minute
|
||||||
setInterval(gatherMemory, 1000 * 60);
|
setInterval(gatherMemory, 1000 * 60);
|
||||||
@ -18,3 +26,35 @@ function gatherMemory() {
|
|||||||
' sharedBytes: ' + memory.sharedBytes;
|
' sharedBytes: ' + memory.sharedBytes;
|
||||||
log.send(logLevels.INFO, details);
|
log.send(logLevels.INFO, details);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method that checks memory usage every minute
|
||||||
|
* and verify if the user in inactive if so it reloads the
|
||||||
|
* application to free up some memory consumption
|
||||||
|
*
|
||||||
|
* @param memoryInfo
|
||||||
|
*/
|
||||||
|
function optimizeMemory(memoryInfo) {
|
||||||
|
const memoryConsumed = (memoryInfo && memoryInfo.workingSetSize / 1024) || 0;
|
||||||
|
const canReload = (!reloadedTimeStamp || (new Date().getTime() - reloadedTimeStamp) > reloadThreshold);
|
||||||
|
|
||||||
|
if (memoryConsumed > maxMemory && systemIdleTime.getIdleTime() > maxIdleTime && canReload) {
|
||||||
|
getConfigField('memoryRefresh')
|
||||||
|
.then((enabled) => {
|
||||||
|
if (enabled) {
|
||||||
|
const mainWindow = getMainWindow();
|
||||||
|
|
||||||
|
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||||
|
setIsAutoReload(true);
|
||||||
|
reloadedTimeStamp = new Date().getTime();
|
||||||
|
log.send(logLevels.INFO, 'Reloading the app to optimize memory usage');
|
||||||
|
mainWindow.webContents.reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
optimizeMemory
|
||||||
|
};
|
@ -16,13 +16,15 @@ const configFields = [
|
|||||||
'launchOnStartup',
|
'launchOnStartup',
|
||||||
'alwaysOnTop',
|
'alwaysOnTop',
|
||||||
'notificationSettings',
|
'notificationSettings',
|
||||||
'bringToFront'
|
'bringToFront',
|
||||||
|
'memoryRefresh'
|
||||||
];
|
];
|
||||||
|
|
||||||
let minimizeOnClose = false;
|
let minimizeOnClose = false;
|
||||||
let launchOnStartup = false;
|
let launchOnStartup = false;
|
||||||
let isAlwaysOnTop = false;
|
let isAlwaysOnTop = false;
|
||||||
let bringToFront = false;
|
let bringToFront = false;
|
||||||
|
let memoryRefresh = false;
|
||||||
|
|
||||||
let symphonyAutoLauncher;
|
let symphonyAutoLauncher;
|
||||||
|
|
||||||
@ -305,6 +307,22 @@ function getTemplate(app) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Window/View menu -> separator
|
||||||
|
template[index].submenu.push({
|
||||||
|
type: 'separator',
|
||||||
|
});
|
||||||
|
|
||||||
|
// Window - View menu -> memoryRefresh
|
||||||
|
template[index].submenu.push({
|
||||||
|
label: 'Memory Refresh',
|
||||||
|
type: 'checkbox',
|
||||||
|
checked: memoryRefresh,
|
||||||
|
click: function(item) {
|
||||||
|
memoryRefresh = item.checked;
|
||||||
|
updateConfigField('memoryRefresh', memoryRefresh);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (!isMac) {
|
if (!isMac) {
|
||||||
template[index].submenu.push({
|
template[index].submenu.push({
|
||||||
label: 'Quit Symphony',
|
label: 'Quit Symphony',
|
||||||
@ -356,6 +374,9 @@ function setCheckboxValues() {
|
|||||||
case 'bringToFront':
|
case 'bringToFront':
|
||||||
bringToFront = configData[key];
|
bringToFront = configData[key];
|
||||||
break;
|
break;
|
||||||
|
case 'memoryRefresh':
|
||||||
|
memoryRefresh = configData[key];
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -64,6 +64,15 @@ const throttledSetBadgeCount = throttle(1000, function(count) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Gathers renderer process memory
|
||||||
|
setInterval(() => {
|
||||||
|
const memory = process.getProcessMemoryInfo();
|
||||||
|
local.ipcRenderer.send(apiName, {
|
||||||
|
cmd: apiCmds.optimizeMemoryConsumption,
|
||||||
|
memory: memory
|
||||||
|
});
|
||||||
|
}, 1000 * 60 * 4);
|
||||||
|
|
||||||
createAPI();
|
createAPI();
|
||||||
|
|
||||||
// creates API exposed from electron.
|
// creates API exposed from electron.
|
||||||
|
@ -40,6 +40,7 @@ let alwaysOnTop = false;
|
|||||||
let position = 'lower-right';
|
let position = 'lower-right';
|
||||||
let display;
|
let display;
|
||||||
let sandboxed = false;
|
let sandboxed = false;
|
||||||
|
let isAutoReload = false;
|
||||||
|
|
||||||
// By default, we set the user's default download directory
|
// By default, we set the user's default download directory
|
||||||
let defaultDownloadsDirectory = app.getPath("downloads");
|
let defaultDownloadsDirectory = app.getPath("downloads");
|
||||||
@ -710,6 +711,13 @@ function setIsOnline(status) {
|
|||||||
* without giving focus
|
* without giving focus
|
||||||
*/
|
*/
|
||||||
function activate(windowName, shouldFocus = true) {
|
function activate(windowName, shouldFocus = true) {
|
||||||
|
|
||||||
|
// don't activate when the app is reloaded programmatically
|
||||||
|
// Electron-136
|
||||||
|
if (isAutoReload) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
let keys = Object.keys(windows);
|
let keys = Object.keys(windows);
|
||||||
for (let i = 0, len = keys.length; i < len; i++) {
|
for (let i = 0, len = keys.length; i < len; i++) {
|
||||||
let window = windows[keys[i]];
|
let window = windows[keys[i]];
|
||||||
@ -735,6 +743,16 @@ function activate(windowName, shouldFocus = true) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets if is auto reloading the app
|
||||||
|
* @param reload
|
||||||
|
*/
|
||||||
|
function setIsAutoReload(reload) {
|
||||||
|
if (typeof reload === 'boolean') {
|
||||||
|
isAutoReload = reload
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* name of renderer window to notify when bounds of child window changes.
|
* name of renderer window to notify when bounds of child window changes.
|
||||||
* @param {object} window Renderer window to use IPC with to inform about size/
|
* @param {object} window Renderer window to use IPC with to inform about size/
|
||||||
@ -972,5 +990,6 @@ module.exports = {
|
|||||||
activate: activate,
|
activate: activate,
|
||||||
setBoundsChangeWindow: setBoundsChangeWindow,
|
setBoundsChangeWindow: setBoundsChangeWindow,
|
||||||
verifyDisplays: verifyDisplays,
|
verifyDisplays: verifyDisplays,
|
||||||
getMenu: getMenu
|
getMenu: getMenu,
|
||||||
|
setIsAutoReload: setIsAutoReload
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user