diff --git a/packages/app-desktop/app.ts b/packages/app-desktop/app.ts index a77f0d7394..c3727b1639 100644 --- a/packages/app-desktop/app.ts +++ b/packages/app-desktop/app.ts @@ -60,6 +60,9 @@ import { PackageInfo } from '@joplin/lib/versionInfo'; import { CustomProtocolHandler } from './utils/customProtocols/handleCustomProtocols'; import { refreshFolders } from '@joplin/lib/folders-screen-utils'; import initializeCommandService from './utils/initializeCommandService'; +import PerformanceLogger from '@joplin/lib/PerformanceLogger'; + +const perfLogger = PerformanceLogger.create('app-desktop/app'); const pluginClasses = [ require('./plugins/GotoAnything').default, @@ -67,6 +70,8 @@ const pluginClasses = [ const appDefaultState = createAppDefaultState(resourceEditWatcherDefaultState); +type StartupTask = { label: string; task: ()=> void|Promise }; + class Application extends BaseApplication { // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied @@ -411,56 +416,53 @@ class Application extends BaseApplication { }); } - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied - public async start(argv: string[], startOptions: StartOptions = null): Promise { - // If running inside a package, the command line, instead of being "node.exe " is "joplin.exe " so - // insert an extra argument so that they can be processed in a consistent way everywhere. - if (!bridge().electronIsDev()) argv.splice(1, 0, '.'); + private buildStartupTasks_() { + const tasks: StartupTask[] = []; + const addTask = (label: string, task: StartupTask['task']) => { + tasks.push({ label, task }); + }; - argv = await super.start(argv, startOptions); + addTask('set up extra debug logging', () => { + reg.logger().info('app.start: doing regular boot'); + const dir: string = Setting.value('profileDir'); - await this.setupIntegrationTestUtils(); + syncDebugLog.enabled = false; - bridge().setLogFilePath(Logger.globalLogger.logFilePath()); + if (dir.endsWith('dev-desktop-2')) { + syncDebugLog.addTarget(TargetType.File, { + path: `${homedir()}/synclog.txt`, + }); + syncDebugLog.enabled = true; + syncDebugLog.info(`Profile dir: ${dir}`); + } + }); - await this.applySettingsSideEffects(); + addTask('set up registry', () => { + reg.setDispatch(this.dispatch.bind(this)); + reg.setShowErrorMessageBoxHandler((message: string) => { bridge().showErrorMessageBox(message); }); + }); - if (Setting.value('sync.upgradeState') === Setting.SYNC_UPGRADE_STATE_MUST_DO) { - reg.logger().info('app.start: doing upgradeSyncTarget action'); - bridge().mainWindow().show(); - return { action: 'upgradeSyncTarget' }; - } + addTask('set up auto updater', () => { + this.setupAutoUpdaterService(); + }); - reg.logger().info('app.start: doing regular boot'); - - const dir: string = Setting.value('profileDir'); - - syncDebugLog.enabled = false; - - if (dir.endsWith('dev-desktop-2')) { - syncDebugLog.addTarget(TargetType.File, { - path: `${homedir()}/synclog.txt`, - }); - syncDebugLog.enabled = true; - syncDebugLog.info(`Profile dir: ${dir}`); - } - - this.setupAutoUpdaterService(); - - AlarmService.setDriver(new AlarmServiceDriverNode({ appName: packageInfo.build.appId })); - AlarmService.setLogger(reg.logger()); - - reg.setDispatch(this.dispatch.bind(this)); - reg.setShowErrorMessageBoxHandler((message: string) => { bridge().showErrorMessageBox(message); }); + addTask('set up AlarmService', () => { + AlarmService.setDriver(new AlarmServiceDriverNode({ appName: packageInfo.build.appId })); + AlarmService.setLogger(reg.logger()); + }); if (Setting.value('flagOpenDevTools')) { - bridge().openDevTools(); + addTask('openDevTools', () => { + bridge().openDevTools(); + }); } - this.protocolHandler_ = bridge().electronApp().getCustomProtocolHandler(); - this.protocolHandler_.allowReadAccessToDirectory(__dirname); // App bundle directory - this.protocolHandler_.allowReadAccessToDirectory(Setting.value('cacheDir')); - this.protocolHandler_.allowReadAccessToDirectory(Setting.value('resourceDir')); + addTask('set up custom protocol handler', async () => { + this.protocolHandler_ = bridge().electronApp().getCustomProtocolHandler(); + this.protocolHandler_.allowReadAccessToDirectory(__dirname); // App bundle directory + this.protocolHandler_.allowReadAccessToDirectory(Setting.value('cacheDir')); + this.protocolHandler_.allowReadAccessToDirectory(Setting.value('resourceDir')); + }); // this.protocolHandler_.allowReadAccessTo(Setting.value('tempDir')); // For now, this doesn't seem necessary: // this.protocolHandler_.allowReadAccessTo(Setting.value('profileDir')); @@ -468,44 +470,52 @@ class Application extends BaseApplication { // handler, and, as such, it may make sense to also limit permissions of // allowed pages with a Content Security Policy. - PluginManager.instance().dispatch_ = this.dispatch.bind(this); - PluginManager.instance().setLogger(reg.logger()); - PluginManager.instance().register(pluginClasses); + addTask('initialize PluginManager, redux, CommandService, and KeymapService', async () => { + PluginManager.instance().dispatch_ = this.dispatch.bind(this); + PluginManager.instance().setLogger(reg.logger()); + PluginManager.instance().register(pluginClasses); - this.initRedux(); + this.initRedux(); - PerFolderSortOrderService.initialize(); + initializeCommandService(this.store(), Setting.value('env') === 'dev'); - initializeCommandService(this.store(), Setting.value('env') === 'dev'); + const keymapService = KeymapService.instance(); + // We only add the commands that appear in the menu because only + // those can have a shortcut associated with them. + keymapService.initialize(menuCommandNames()); - const keymapService = KeymapService.instance(); - // We only add the commands that appear in the menu because only - // those can have a shortcut associated with them. - keymapService.initialize(menuCommandNames()); - - try { - await keymapService.loadCustomKeymap(`${dir}/keymap-desktop.json`); - } catch (error) { - reg.logger().error(error); - } - - // Since the settings need to be loaded before the store is - // created, it will never receive the SETTING_UPDATE_ALL even, - // which mean state.settings will not be initialised. So we - // manually call dispatchUpdateAll() to force an update. - Setting.dispatchUpdateAll(); - - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied - await refreshFolders((action: any) => this.dispatch(action), ''); - - const tags = await Tag.allWithNotes(); - - this.dispatch({ - type: 'TAG_UPDATE_ALL', - items: tags, + try { + await keymapService.loadCustomKeymap(`${Setting.value('profileDir')}/keymap-desktop.json`); + } catch (error) { + reg.logger().error(error); + } }); - await this.setupCustomCss(); + addTask('initialize PerFolderSortOrderService', () => { + PerFolderSortOrderService.initialize(); + }); + + addTask('dispatch initial settings', () => { + // Since the settings need to be loaded before the store is + // created, it will never receive the SETTING_UPDATE_ALL even, + // which mean state.settings will not be initialised. So we + // manually call dispatchUpdateAll() to force an update. + Setting.dispatchUpdateAll(); + }); + + addTask('update folders and tags', async () => { + await refreshFolders((action) => this.dispatch(action), ''); + + const tags = await Tag.allWithNotes(); + this.dispatch({ + type: 'TAG_UPDATE_ALL', + items: tags, + }); + }); + + addTask('set up custom CSS', async () => { + await this.setupCustomCss(); + }); // const masterKeys = await MasterKey.all(); @@ -514,188 +524,237 @@ class Application extends BaseApplication { // items: masterKeys, // }); - const getNotesParent = async () => { - let notesParent = parseNotesParent(Setting.value('notesParent'), Setting.value('activeFolderId')); - if (notesParent.type === 'Tag' && !(await Tag.load(notesParent.selectedItemId))) { - notesParent = { - type: 'Folder', - selectedItemId: Setting.value('activeFolderId'), - }; + addTask('send initial selection to redux', async () => { + const getNotesParent = async () => { + let notesParent = parseNotesParent(Setting.value('notesParent'), Setting.value('activeFolderId')); + if (notesParent.type === 'Tag' && !(await Tag.load(notesParent.selectedItemId))) { + notesParent = { + type: 'Folder', + selectedItemId: Setting.value('activeFolderId'), + }; + } + return notesParent; + }; + + const notesParent = await getNotesParent(); + if (notesParent.type === 'SmartFilter') { + this.store().dispatch({ + type: 'SMART_FILTER_SELECT', + id: notesParent.selectedItemId, + }); + } else if (notesParent.type === 'Tag') { + this.store().dispatch({ + type: 'TAG_SELECT', + id: notesParent.selectedItemId, + }); + } else { + this.store().dispatch({ + type: 'FOLDER_SELECT', + id: notesParent.selectedItemId, + }); } - return notesParent; - }; - const notesParent = await getNotesParent(); + this.store().dispatch({ + type: 'FOLDER_SET_COLLAPSED_ALL', + ids: Setting.value('collapsedFolderIds'), + }); - if (notesParent.type === 'SmartFilter') { this.store().dispatch({ - type: 'SMART_FILTER_SELECT', - id: notesParent.selectedItemId, + type: 'NOTE_DEVTOOLS_SET', + value: Setting.value('flagOpenDevTools'), }); - } else if (notesParent.type === 'Tag') { - this.store().dispatch({ - type: 'TAG_SELECT', - id: notesParent.selectedItemId, - }); - } else { - this.store().dispatch({ - type: 'FOLDER_SELECT', - id: notesParent.selectedItemId, - }); - } - - this.store().dispatch({ - type: 'FOLDER_SET_COLLAPSED_ALL', - ids: Setting.value('collapsedFolderIds'), }); - this.store().dispatch({ - type: 'NOTE_DEVTOOLS_SET', - value: Setting.value('flagOpenDevTools'), + addTask('initializeUserFetcher', async () => { + initializeUserFetcher(); + shim.setInterval(() => { void userFetcher(); }, 1000 * 60 * 60); }); - // Always disable on Mac for now - and disable too for the few apps that may have the flag enabled. - // At present, it only seems to work on Windows. - if (shim.isMac()) { - Setting.setValue('featureFlag.autoUpdaterServiceEnabled', false); - } + addTask('updateTray', () => this.updateTray()); - // Note: Auto-update is a misnomer in the code. - // The code below only checks, if a new version is available. - // We only allow Windows and macOS users to automatically check for updates - if (!Setting.value('featureFlag.autoUpdaterServiceEnabled')) { - if (shim.isWindows() || shim.isMac()) { - const runAutoUpdateCheck = () => { - if (Setting.value('autoUpdateEnabled')) { - void checkForUpdates(true, bridge().mainWindow(), { includePreReleases: Setting.value('autoUpdate.includePreReleases') }); - } - }; - - // Initial check on startup - shim.setTimeout(() => { runAutoUpdateCheck(); }, 5000); - // Then every x hours - shim.setInterval(() => { runAutoUpdateCheck(); }, 12 * 60 * 60 * 1000); + addTask('set main window state', () => { + if (Setting.value('startMinimized') && Setting.value('showTrayIcon')) { + bridge().mainWindow().hide(); + } else { + bridge().mainWindow().show(); } - } + }); - initializeUserFetcher(); - shim.setInterval(() => { void userFetcher(); }, 1000 * 60 * 60); + addTask('start maintenance tasks', () => { + // Always disable on Mac for now - and disable too for the few apps that may have the flag enabled. + // At present, it only seems to work on Windows. + if (shim.isMac()) { + Setting.setValue('featureFlag.autoUpdaterServiceEnabled', false); + } - this.updateTray(); + // Note: Auto-update is a misnomer in the code. + // The code below only checks, if a new version is available. + // We only allow Windows and macOS users to automatically check for updates + if (!Setting.value('featureFlag.autoUpdaterServiceEnabled')) { + if (shim.isWindows() || shim.isMac()) { + const runAutoUpdateCheck = () => { + if (Setting.value('autoUpdateEnabled')) { + void checkForUpdates(true, bridge().mainWindow(), { includePreReleases: Setting.value('autoUpdate.includePreReleases') }); + } + }; - shim.setTimeout(() => { - void AlarmService.garbageCollect(); - }, 1000 * 60 * 60); + // Initial check on startup + shim.setTimeout(() => { runAutoUpdateCheck(); }, 5000); + // Then every x hours + shim.setInterval(() => { runAutoUpdateCheck(); }, 12 * 60 * 60 * 1000); + } + } - if (Setting.value('startMinimized') && Setting.value('showTrayIcon')) { - bridge().mainWindow().hide(); - } else { - bridge().mainWindow().show(); - } + shim.setTimeout(() => { + void AlarmService.garbageCollect(); + }, 1000 * 60 * 60); + void ShareService.instance().maintenance(); - void ShareService.instance().maintenance(); + ResourceService.runInBackground(); - ResourceService.runInBackground(); - - if (Setting.value('env') === 'dev') { - void AlarmService.updateAllNotifications(); - } else { - // eslint-disable-next-line promise/prefer-await-to-then -- Old code before rule was applied - void reg.scheduleSync(1000).then(() => { - // Wait for the first sync before updating the notifications, since synchronisation - // might change the notifications. + if (Setting.value('env') === 'dev') { void AlarmService.updateAllNotifications(); + } else { + // eslint-disable-next-line promise/prefer-await-to-then -- Old code before rule was applied + void reg.scheduleSync(1000).then(() => { + // Wait for the first sync before updating the notifications, since synchronisation + // might change the notifications. + void AlarmService.updateAllNotifications(); - void DecryptionWorker.instance().scheduleStart(); - }); - } + void DecryptionWorker.instance().scheduleStart(); + }); + } - const clipperLogger = new Logger(); - clipperLogger.addTarget(TargetType.File, { path: `${Setting.value('profileDir')}/log-clipper.txt` }); - clipperLogger.addTarget(TargetType.Console); - - ClipperServer.instance().initialize(actionApi); - ClipperServer.instance().setEnabled(!Setting.value('altInstanceId')); - ClipperServer.instance().setLogger(clipperLogger); - ClipperServer.instance().setDispatch(this.store().dispatch); - - if (ClipperServer.instance().enabled() && Setting.value('clipperServer.autoStart')) { - void ClipperServer.instance().start(); - } - - ExternalEditWatcher.instance().setLogger(reg.logger()); - ExternalEditWatcher.instance().initialize(bridge, this.store().dispatch); - - ResourceEditWatcher.instance().initialize( - reg.logger(), - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied - (action: any) => { this.store().dispatch(action); }, - (path: string) => bridge().openItem(path), - () => this.store().getState().windowId, - ); - - // Forwards the local event to the global event manager, so that it can - // be picked up by the plugin manager. - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied - ResourceEditWatcher.instance().on('resourceChange', (event: any) => { - eventManager.emit(EventName.ResourceChange, event); + RevisionService.instance().runInBackground(); + this.startRotatingLogMaintenance(Setting.value('profileDir')); }); - RevisionService.instance().runInBackground(); + addTask('set up ClipperServer', () => { + const clipperLogger = new Logger(); + clipperLogger.addTarget(TargetType.File, { path: `${Setting.value('profileDir')}/log-clipper.txt` }); + clipperLogger.addTarget(TargetType.Console); + + ClipperServer.instance().initialize(actionApi); + ClipperServer.instance().setEnabled(!Setting.value('altInstanceId')); + ClipperServer.instance().setLogger(clipperLogger); + ClipperServer.instance().setDispatch(this.store().dispatch); + + if (ClipperServer.instance().enabled() && Setting.value('clipperServer.autoStart')) { + void ClipperServer.instance().start(); + } + }); + + addTask('set up external edit watchers', () => { + ExternalEditWatcher.instance().setLogger(reg.logger()); + ExternalEditWatcher.instance().initialize(bridge, this.store().dispatch); + + ResourceEditWatcher.instance().initialize( + reg.logger(), + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied + (action: any) => { this.store().dispatch(action); }, + (path: string) => bridge().openItem(path), + () => this.store().getState().windowId, + ); + + // Forwards the local event to the global event manager, so that it can + // be picked up by the plugin manager. + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied + ResourceEditWatcher.instance().on('resourceChange', (event: any) => { + eventManager.emit(EventName.ResourceChange, event); + }); + }); // Make it available to the console window - useful to call revisionService.collectRevisions() if (Setting.value('env') === 'dev') { - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied - (window as any).joplin = { - revisionService: RevisionService.instance(), - migrationService: MigrationService.instance(), - decryptionWorker: DecryptionWorker.instance(), - commandService: CommandService.instance(), - pluginService: PluginService.instance(), - bridge: bridge(), - debug: new DebugService(reg.db()), - resourceService: ResourceService.instance(), - searchEngine: SearchEngine.instance(), - ocrService: () => this.ocrService_, - }; + addTask('add debug variables', () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied + (window as any).joplin = { + revisionService: RevisionService.instance(), + migrationService: MigrationService.instance(), + decryptionWorker: DecryptionWorker.instance(), + commandService: CommandService.instance(), + pluginService: PluginService.instance(), + bridge: bridge(), + debug: new DebugService(reg.db()), + resourceService: ResourceService.instance(), + searchEngine: SearchEngine.instance(), + ocrService: () => this.ocrService_, + }; + }); } - bridge().addEventListener('nativeThemeUpdated', this.bridge_nativeThemeUpdated); - bridge().setOnAllowedExtensionsChangeListener((newExtensions) => { - Setting.setValue('linking.extraAllowedExtensions', newExtensions); + addTask('listen for main process events', () => { + bridge().addEventListener('nativeThemeUpdated', this.bridge_nativeThemeUpdated); + bridge().setOnAllowedExtensionsChangeListener((newExtensions) => { + Setting.setValue('linking.extraAllowedExtensions', newExtensions); + }); + + ipcRenderer.on('window-focused', (_event, newWindowId) => { + const currentWindowId = this.store().getState().windowId; + if (newWindowId !== currentWindowId) { + this.dispatch({ + type: 'WINDOW_FOCUS', + windowId: newWindowId, + lastWindowId: currentWindowId, + }); + } + }); }); - ipcRenderer.on('window-focused', (_event, newWindowId) => { - const currentWindowId = this.store().getState().windowId; - if (newWindowId !== currentWindowId) { - this.dispatch({ - type: 'WINDOW_FOCUS', - windowId: newWindowId, - lastWindowId: currentWindowId, - }); - } + addTask('initPluginService', () => this.initPluginService()); + + addTask('setupContextMenu', () => { + this.setupContextMenu(); }); - await this.initPluginService(); - - this.setupContextMenu(); - - await SpellCheckerService.instance().initialize(new SpellCheckerServiceDriverNative()); - - this.startRotatingLogMaintenance(Setting.value('profileDir')); - - await this.setupOcrService(); - - eventManager.on(EventName.OcrServiceResourcesProcessed, async () => { - await ResourceService.instance().indexNoteResources(); + addTask('set up SpellCheckerService', async () => { + await SpellCheckerService.instance().initialize(new SpellCheckerServiceDriverNative()); }); - eventManager.on(EventName.NoteResourceIndexed, async () => { - SearchEngine.instance().scheduleSyncTables(); + addTask('listen for resource events', () => { + eventManager.on(EventName.OcrServiceResourcesProcessed, async () => { + await ResourceService.instance().indexNoteResources(); + }); + + eventManager.on(EventName.NoteResourceIndexed, async () => { + SearchEngine.instance().scheduleSyncTables(); + }); }); - // Used by tests - ipcRenderer.send('startup-finished'); + addTask('setupOcrService', () => this.setupOcrService()); + + return tasks; + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied + public async start(argv: string[], startOptions: StartOptions = null): Promise { + const startupTask = perfLogger.taskStart('start'); + + // If running inside a package, the command line, instead of being "node.exe " is "joplin.exe " so + // insert an extra argument so that they can be processed in a consistent way everywhere. + if (!bridge().electronIsDev()) argv.splice(1, 0, '.'); + + + argv = await super.start(argv, startOptions); + + await this.setupIntegrationTestUtils(); + + bridge().setLogFilePath(Logger.globalLogger.logFilePath()); + await this.applySettingsSideEffects(); + + if (Setting.value('sync.upgradeState') === Setting.SYNC_UPGRADE_STATE_MUST_DO) { + reg.logger().info('app.start: doing upgradeSyncTarget action'); + bridge().mainWindow().show(); + startupTask.onEnd(); + + return { action: 'upgradeSyncTarget' }; + } + + const startupTasks = this.buildStartupTasks_(); + for (const task of startupTasks) { + await perfLogger.track(task.label, async () => task.task()); + } + // setTimeout(() => { // void populateDatabase(reg.db(), { @@ -749,6 +808,10 @@ class Application extends BaseApplication { // await runIntegrationTests(); + // Used by tests + ipcRenderer.send('startup-finished'); + + startupTask.onEnd(); return null; } diff --git a/packages/app-desktop/main-html.ts b/packages/app-desktop/main-html.ts index 6c5b77115c..18900e3722 100644 --- a/packages/app-desktop/main-html.ts +++ b/packages/app-desktop/main-html.ts @@ -31,6 +31,7 @@ import FileApiDriverLocal from '@joplin/lib/file-api-driver-local'; import * as React from 'react'; import nodeSqlite = require('sqlite3'); import initLib from '@joplin/lib/initLib'; +import PerformanceLogger from '@joplin/lib/PerformanceLogger'; const pdfJs = require('pdfjs-dist'); const { isAppleSilicon } = require('is-apple-silicon'); require('@sentry/electron/renderer'); @@ -38,6 +39,8 @@ require('@sentry/electron/renderer'); // Allows components to use React as a global window.React = React; +const perfLogger = PerformanceLogger.create('main-html'); + const main = async () => { // eslint-disable-next-line no-console @@ -106,7 +109,7 @@ const main = async () => { } }; -main().catch((error) => { +perfLogger.track('main', main).catch((error) => { const env = bridge().env(); console.error(error); diff --git a/packages/lib/PerformanceLogger.ts b/packages/lib/PerformanceLogger.ts index ad342f7637..e2eb1bad01 100644 --- a/packages/lib/PerformanceLogger.ts +++ b/packages/lib/PerformanceLogger.ts @@ -125,10 +125,12 @@ export default class PerformanceLogger { } const startTime = performance.now(); + this.lastLogTime_ = startTime; PerformanceLogger.logDebug_(`${name}: Start at ${formatAbsoluteTime(startTime)}`); const onEnd = () => { const now = performance.now(); + this.lastLogTime_ = now; if (hasPerformanceMarkApi) { performance.mark(`${name}-end`); performance.measure(name, `${name}-start`, `${name}-end`); diff --git a/packages/lib/services/ResourceService.ts b/packages/lib/services/ResourceService.ts index cfe04dba3f..4268b7c598 100644 --- a/packages/lib/services/ResourceService.ts +++ b/packages/lib/services/ResourceService.ts @@ -11,8 +11,11 @@ import ItemChangeUtils from './ItemChangeUtils'; import time from '../time'; import eventManager, { EventName } from '../eventManager'; import { ItemChangeEntity } from './database/types'; +import PerformanceLogger from '../PerformanceLogger'; const { sprintf } = require('sprintf-js'); +const perfLogger = PerformanceLogger.create('ResourceService'); + export default class ResourceService extends BaseService { public static isRunningInBackground_ = false; @@ -34,6 +37,7 @@ export default class ResourceService extends BaseService { } this.isIndexing_ = true; + const task = perfLogger.taskStart('indexNoteResources'); try { await ItemChange.waitForAllSaved(); @@ -110,6 +114,7 @@ export default class ResourceService extends BaseService { } this.isIndexing_ = false; + task.onEnd(); eventManager.emit(EventName.NoteResourceIndexed); @@ -123,6 +128,8 @@ export default class ResourceService extends BaseService { public async deleteOrphanResources(expiryDelay: number = null) { if (expiryDelay === null) expiryDelay = Setting.value('revisionService.ttlDays') * 24 * 60 * 60 * 1000; + const task = perfLogger.taskStart('deleteOrphanResources'); + const resourceIds = await NoteResource.orphanResources(expiryDelay); this.logger().info('ResourceService::deleteOrphanResources:', resourceIds); for (let i = 0; i < resourceIds.length; i++) { @@ -138,6 +145,8 @@ export default class ResourceService extends BaseService { await Resource.delete(resourceId, { sourceDescription: 'deleteOrphanResources' }); } } + + task.onEnd(); } private static async autoSetFileSize(resourceId: string, filePath: string, waitTillExists = true) { diff --git a/packages/lib/services/share/ShareService.ts b/packages/lib/services/share/ShareService.ts index 0e4e2ad5f6..1cfd117a6a 100644 --- a/packages/lib/services/share/ShareService.ts +++ b/packages/lib/services/share/ShareService.ts @@ -14,8 +14,10 @@ import { getMasterPassword } from '../e2ee/utils'; import ResourceService from '../ResourceService'; import { addMasterKey, getEncryptionEnabled, localSyncInfo } from '../synchronizer/syncInfoUtils'; import { ShareInvitation, SharePermissions, State, stateRootKey, StateShare } from './reducer'; +import PerformanceLogger from '../../PerformanceLogger'; const logger = Logger.create('ShareService'); +const perfLogger = PerformanceLogger.create('ShareService'); export interface ApiShare { id: string; @@ -514,6 +516,7 @@ export default class ShareService { } public async maintenance() { + const task = perfLogger.taskStart('maintenance'); if (this.enabled) { let hasError = false; @@ -537,6 +540,7 @@ export default class ShareService { // so we can run the clean up function. if (!hasError) await this.updateNoLongerSharedItems(); } + task.onEnd(); } }