From b7a2f3a058f431d9b848c88fa98af36e02528700 Mon Sep 17 00:00:00 2001 From: NicolasCantu Date: Wed, 26 Feb 2025 09:29:36 +0100 Subject: [PATCH] Refactor database.service --- src/services/database.service.ts | 161 +++++++++++++++---------------- 1 file changed, 78 insertions(+), 83 deletions(-) diff --git a/src/services/database.service.ts b/src/services/database.service.ts index e99370f..a783b9a 100755 --- a/src/services/database.service.ts +++ b/src/services/database.service.ts @@ -8,6 +8,7 @@ export class Database { private serviceWorkerRegistration: ServiceWorkerRegistration | null = null; private messageChannel: MessageChannel | null = null; private messageChannelForGet: MessageChannel | null = null; + private serviceWorkerCheckIntervalId: number | null = null; private storeDefinitions = { AnkLabels: { name: 'labels', @@ -81,12 +82,10 @@ export class Database { }); }; - request.onsuccess = () => { - setTimeout(() => { - this.db = request.result; - this.initServiceWorker(); - resolve(); - }, 500); + request.onsuccess = async () => { + this.db = request.result; + await this.initServiceWorker(); + resolve(); }; request.onerror = () => { @@ -115,43 +114,43 @@ export class Database { if (!('serviceWorker' in navigator)) return; // Ensure service workers are supported try { - // Get existing service worker registrations - const registrations = await navigator.serviceWorker.getRegistrations(); - if (registrations.length === 0) { - // No existing workers: register a new one. - this.serviceWorkerRegistration = await navigator.serviceWorker.register('/src/service-workers/database.worker.js', { type: 'module' }); - console.log('Service Worker registered with scope:', registration.scope); - } else if (registrations.length === 1) { - // One existing worker: update it (restart it) without unregistering. - this.serviceWorkerRegistration = registrations[0]; - await this.serviceWorkerRegistration.update(); - console.log('Service worker updated'); - } else { - // More than one existing worker: unregister them all and register a new one. - console.log('Multiple Service Worker(s) detected. Unregistering all...'); - await Promise.all(registrations.map(reg => reg.unregister())); - console.log('All previous Service Workers unregistered.'); - this.serviceWorkerRegistration = await navigator.serviceWorker.register('/src/service-workers/database.worker.js', { type: 'module' }); - console.log('Service Worker registered with scope:', registration.scope); - } + // Get existing service worker registrations + const registrations = await navigator.serviceWorker.getRegistrations(); + if (registrations.length === 0) { + // No existing workers: register a new one. + this.serviceWorkerRegistration = await navigator.serviceWorker.register('/src/service-workers/database.worker.js', { type: 'module' }); + console.log('Service Worker registered with scope:', this.serviceWorkerRegistration.scope); + } else if (registrations.length === 1) { + // One existing worker: update it (restart it) without unregistering. + this.serviceWorkerRegistration = registrations[0]; + await this.serviceWorkerRegistration.update(); + console.log('Service Worker updated'); + } else { + // More than one existing worker: unregister them all and register a new one. + console.log('Multiple Service Worker(s) detected. Unregistering all...'); + await Promise.all(registrations.map(reg => reg.unregister())); + console.log('All previous Service Workers unregistered.'); + this.serviceWorkerRegistration = await navigator.serviceWorker.register('/src/service-workers/database.worker.js', { type: 'module' }); + console.log('Service Worker registered with scope:', this.serviceWorkerRegistration.scope); + } - await this.checkForUpdates(); + await this.checkForUpdates(); - // Set up the message channels - this.messageChannel = new MessageChannel(); - this.messageChannelForGet = new MessageChannel(); - this.messageChannel.port1.onmessage = this.handleAddObjectResponse; - this.messageChannelForGet.port1.onmessage = this.handleGetObjectResponse; + // Set up a global message listener for responses from the service worker. + navigator.serviceWorker.addEventListener('message', async (event) => { + console.log('Received message from service worker:', event.data); + await this.handleServiceWorkerMessage(event.data); + }); - // Ensure the new service worker is activated before sending messages + // Set up a periodic check to ensure the service worker is active and to send a SYNC message. + this.serviceWorkerCheckIntervalId = window.setInterval(async () => { const activeWorker = this.serviceWorkerRegistration.active || (await this.waitForServiceWorkerActivation(this.serviceWorkerRegistration)); - - activeWorker?.postMessage( - { type: 'START' }, - [this.messageChannel.port2], - ); + const service = await Services.getInstance(); + const payload = await service.getMyProcesses(); + activeWorker?.postMessage({ type: 'SCAN', payload }); + }, 5000); } catch (error) { - console.error('Service Worker registration failed:', error); + console.error('Service Worker registration failed:', error); } } @@ -188,6 +187,46 @@ export class Database { } } + private async handleServiceWorkerMessage(message: any) { + switch (message.type) { + case 'TO_DOWNLOAD': + console.log('Received data to download:', message.data); + await this.handleDownloadList(message.data); + break; + default: + console.warn('Unknown message type received from service worker:', message); + } + } + + private async handleDownloadList(downloadList: string[]): void { + // Download the missing data + let requestedStateId = []; + const service = await Services.getInstance(); + for (const hash of downloadList) { + try { + const valueBytes = await service.fetchValueFromStorage(hash); + if (valueBytes) { + // Save data to db + const blob = new Blob([valueBytes], {type: "application/octet-stream"}); + await service.saveBlobToDb(hash, blob); + } else { + // We first request the data from managers + console.log('Request data from managers of the process'); + // get the diff from db + const diff = await service.getDiffByValue(hash); + const processId = diff.process_id; + const stateId = diff.state_id; + if (!requestedStateId.includes(stateId)) { + await service.requestDataFromPeers(processId, stateId); + requestedStateId.push(stateId); + } + } + } catch (e) { + console.error(e); + } + } + } + private handleAddObjectResponse = async (event: MessageEvent) => { const data = event.data; console.log('Received response from service worker (ADD_OBJECT):', data); @@ -195,6 +234,7 @@ export class Database { if (data.type === 'NOTIFICATIONS') { service.setNotifications(data.data); } else if (data.type === 'TO_DOWNLOAD') { + console.log(`Received missing data ${data}`); // Download the missing data let requestedStateId = []; for (const hash of data.data) { @@ -220,8 +260,6 @@ export class Database { console.error(e); } } - // try to update list of my processes - await service.getMyProcesses(); } }; @@ -267,49 +305,6 @@ export class Database { }); } - public updateMyProcesses(myProcessesId: string[]): Promise { - if (myProcessesId.length === 0) { - return; - } - - return new Promise(async (resolve, reject) => { - if (!this.serviceWorkerRegistration) { - // console.warn('Service worker registration is not ready. Waiting...'); - this.serviceWorkerRegistration = await navigator.serviceWorker.ready; - } - - const activeWorker = await this.waitForServiceWorkerActivation(this.serviceWorkerRegistration); - // Create a message channel for communication - const messageChannel = new MessageChannel(); - - // Handle the response from the service worker - messageChannel.port1.onmessage = (event) => { - if (event.data.status === 'success') { - resolve(); - } else { - const error = event.data.message; - reject(new Error(error || 'Unknown error occurred while scanning our processes')); - } - }; - - try { - const payload = { myProcessesId }; - console.log('Sending UPDATE_PROCESSES msg with payload', payload); - activeWorker?.postMessage( - { - type: 'UPDATE_PROCESSES', - - payload, - }, - [messageChannel.port2], - - ); - } catch (error) { - reject(new Error(`Failed to send message to service worker: ${error}`)); - } - }); - } - public async getObject(storeName: string, key: string): Promise { const db = await this.getDb(); const tx = db.transaction(storeName, 'readonly');