Changeset View
Changeset View
Standalone View
Standalone View
web/database/worker/db-worker.js
// @flow | // @flow | ||||||||||||||||||
import localforage from 'localforage'; | import localforage from 'localforage'; | ||||||||||||||||||
import _throttle from 'lodash/throttle.js'; | |||||||||||||||||||
import initSqlJs, { type SqliteDatabase } from 'sql.js'; | import initSqlJs, { type SqliteDatabase } from 'sql.js'; | ||||||||||||||||||
import type { | import type { | ||||||||||||||||||
ClientDBDraftStoreOperation, | ClientDBDraftStoreOperation, | ||||||||||||||||||
DraftStoreOperation, | DraftStoreOperation, | ||||||||||||||||||
} from 'lib/types/draft-types.js'; | } from 'lib/types/draft-types.js'; | ||||||||||||||||||
import type { ClientDBStore } from 'lib/types/store-ops-types.js'; | import type { ClientDBStore } from 'lib/types/store-ops-types.js'; | ||||||||||||||||||
Show All 16 Lines | |||||||||||||||||||
import { getMetadata, setMetadata } from '../queries/metadata-queries.js'; | import { getMetadata, setMetadata } from '../queries/metadata-queries.js'; | ||||||||||||||||||
import { | import { | ||||||||||||||||||
getPersistStorageItem, | getPersistStorageItem, | ||||||||||||||||||
removePersistStorageItem, | removePersistStorageItem, | ||||||||||||||||||
setPersistStorageItem, | setPersistStorageItem, | ||||||||||||||||||
} from '../queries/storage-engine-queries.js'; | } from '../queries/storage-engine-queries.js'; | ||||||||||||||||||
import { | import { | ||||||||||||||||||
CURRENT_USER_ID_KEY, | CURRENT_USER_ID_KEY, | ||||||||||||||||||
DB_PERSIST_THROTTLE_WAIT_MS, | |||||||||||||||||||
SQLITE_CONTENT, | SQLITE_CONTENT, | ||||||||||||||||||
SQLITE_ENCRYPTION_KEY, | SQLITE_ENCRYPTION_KEY, | ||||||||||||||||||
} from '../utils/constants.js'; | } from '../utils/constants.js'; | ||||||||||||||||||
import { | import { | ||||||||||||||||||
decryptDatabaseFile, | decryptDatabaseFile, | ||||||||||||||||||
encryptDatabaseFile, | encryptDatabaseFile, | ||||||||||||||||||
generateDatabaseCryptoKey, | generateDatabaseCryptoKey, | ||||||||||||||||||
} from '../utils/worker-crypto-utils.js'; | } from '../utils/worker-crypto-utils.js'; | ||||||||||||||||||
const localforageConfig: PartialConfig = { | const localforageConfig: PartialConfig = { | ||||||||||||||||||
driver: localforage.INDEXEDDB, | driver: localforage.INDEXEDDB, | ||||||||||||||||||
name: 'comm', | name: 'comm', | ||||||||||||||||||
storeName: 'commStorage', | storeName: 'commStorage', | ||||||||||||||||||
description: 'Comm encrypted database storage', | description: 'Comm encrypted database storage', | ||||||||||||||||||
version: '1.0', | version: '1.0', | ||||||||||||||||||
}; | }; | ||||||||||||||||||
localforage.config(localforageConfig); | localforage.config(localforageConfig); | ||||||||||||||||||
let sqliteDb: ?SqliteDatabase = null; | let sqliteDb: ?SqliteDatabase = null; | ||||||||||||||||||
let encryptionKey: ?CryptoKey = null; | let encryptionKey: ?CryptoKey = null; | ||||||||||||||||||
let persistNeeded: boolean = false; | |||||||||||||||||||
let persistInProgress: boolean = false; | |||||||||||||||||||
async function initDatabase(sqljsFilePath: string, sqljsFilename: ?string) { | async function initDatabase(sqljsFilePath: string, sqljsFilename: ?string) { | ||||||||||||||||||
encryptionKey = await localforage.getItem(SQLITE_ENCRYPTION_KEY); | encryptionKey = await localforage.getItem(SQLITE_ENCRYPTION_KEY); | ||||||||||||||||||
if (!encryptionKey) { | if (!encryptionKey) { | ||||||||||||||||||
const cryptoKey = await generateDatabaseCryptoKey(); | const cryptoKey = await generateDatabaseCryptoKey(); | ||||||||||||||||||
await localforage.setItem(SQLITE_ENCRYPTION_KEY, cryptoKey); | await localforage.setItem(SQLITE_ENCRYPTION_KEY, cryptoKey); | ||||||||||||||||||
} | } | ||||||||||||||||||
const encryptedContent = await localforage.getItem(SQLITE_CONTENT); | const encryptedContent = await localforage.getItem(SQLITE_CONTENT); | ||||||||||||||||||
▲ Show 20 Lines • Show All 62 Lines • ▼ Show 20 Lines | return { | ||||||||||||||||||
drafts: getAllDrafts(sqliteDb), | drafts: getAllDrafts(sqliteDb), | ||||||||||||||||||
messages: [], | messages: [], | ||||||||||||||||||
threads: [], | threads: [], | ||||||||||||||||||
messageStoreThreads: [], | messageStoreThreads: [], | ||||||||||||||||||
}; | }; | ||||||||||||||||||
} | } | ||||||||||||||||||
async function persist() { | async function persist() { | ||||||||||||||||||
persistInProgress = true; | |||||||||||||||||||
if (!sqliteDb) { | if (!sqliteDb) { | ||||||||||||||||||
throw new Error('Database not initialized'); | throw new Error('Database not initialized'); | ||||||||||||||||||
} | } | ||||||||||||||||||
if (!encryptionKey) { | if (!encryptionKey) { | ||||||||||||||||||
encryptionKey = await localforage.getItem(SQLITE_ENCRYPTION_KEY); | encryptionKey = await localforage.getItem(SQLITE_ENCRYPTION_KEY); | ||||||||||||||||||
} | } | ||||||||||||||||||
while (true) { | |||||||||||||||||||
tomek: Why do we need to use `while(true)` and break? Can we change the code so that `while` condition… | |||||||||||||||||||
const dbData = sqliteDb.export(); | const dbData = sqliteDb.export(); | ||||||||||||||||||
if (!encryptionKey) { | if (!encryptionKey) { | ||||||||||||||||||
throw new Error('Encryption key is missing'); | throw new Error('Encryption key is missing'); | ||||||||||||||||||
} | } | ||||||||||||||||||
const encryptedData = await encryptDatabaseFile(dbData, encryptionKey); | const encryptedData = await encryptDatabaseFile(dbData, encryptionKey); | ||||||||||||||||||
await localforage.setItem(SQLITE_CONTENT, encryptedData); | await localforage.setItem(SQLITE_CONTENT, encryptedData); | ||||||||||||||||||
} | |||||||||||||||||||
const throttledPersist = _throttle(persist, DB_PERSIST_THROTTLE_WAIT_MS); | if (persistNeeded) { | ||||||||||||||||||
persistNeeded = false; | |||||||||||||||||||
} else { | |||||||||||||||||||
persistInProgress = false; | |||||||||||||||||||
persistNeeded = false; | |||||||||||||||||||
tomekUnsubmitted Not Done Inline ActionsEntering this if branch means that persistNeeded = false - why are we setting this again? tomek: Entering this `if` branch means that `persistNeeded = false` - why are we setting this again? | |||||||||||||||||||
break; | |||||||||||||||||||
} | |||||||||||||||||||
} | |||||||||||||||||||
} | |||||||||||||||||||
async function processAppRequest( | async function processAppRequest( | ||||||||||||||||||
message: WorkerRequestMessage, | message: WorkerRequestMessage, | ||||||||||||||||||
): Promise<?WorkerResponseMessage> { | ): Promise<?WorkerResponseMessage> { | ||||||||||||||||||
// non-database operations | // non-database operations | ||||||||||||||||||
if (message.type === workerRequestMessageTypes.PING) { | if (message.type === workerRequestMessageTypes.PING) { | ||||||||||||||||||
return { | return { | ||||||||||||||||||
type: workerResponseMessageTypes.PONG, | type: workerResponseMessageTypes.PONG, | ||||||||||||||||||
▲ Show 20 Lines • Show All 61 Lines • ▼ Show 20 Lines | ): Promise<?WorkerResponseMessage> { | ||||||||||||||||||
) { | ) { | ||||||||||||||||||
setPersistStorageItem(sqliteDb, message.key, message.item); | setPersistStorageItem(sqliteDb, message.key, message.item); | ||||||||||||||||||
} else if ( | } else if ( | ||||||||||||||||||
message.type === workerRequestMessageTypes.REMOVE_PERSIST_STORAGE_ITEM | message.type === workerRequestMessageTypes.REMOVE_PERSIST_STORAGE_ITEM | ||||||||||||||||||
) { | ) { | ||||||||||||||||||
removePersistStorageItem(sqliteDb, message.key); | removePersistStorageItem(sqliteDb, message.key); | ||||||||||||||||||
} | } | ||||||||||||||||||
throttledPersist(); | if (persistInProgress) { | ||||||||||||||||||
// persist process is running, it will need to be scheduled again | |||||||||||||||||||
persistNeeded = true; | |||||||||||||||||||
} else { | |||||||||||||||||||
// persist process is not running, scheduling for the first time | |||||||||||||||||||
persist(); | |||||||||||||||||||
} | |||||||||||||||||||
tomekUnsubmitted Not Done Inline Actions
After we change the loop to while(persistNeeded) we should modify this code tomek: After we change the loop to `while(persistNeeded)` we should modify this code | |||||||||||||||||||
return undefined; | return undefined; | ||||||||||||||||||
} | } | ||||||||||||||||||
function connectHandler(event: SharedWorkerMessageEvent) { | function connectHandler(event: SharedWorkerMessageEvent) { | ||||||||||||||||||
if (!event.ports.length) { | if (!event.ports.length) { | ||||||||||||||||||
return; | return; | ||||||||||||||||||
} | } | ||||||||||||||||||
const port: MessagePort = event.ports[0]; | const port: MessagePort = event.ports[0]; | ||||||||||||||||||
Show All 28 Lines |
Why do we need to use while(true) and break? Can we change the code so that while condition is used instead, e.g. while(persistNeeded)? Then we should set this flag to false as the first operation in this loop.