From 74e21311dc2cdf9a662fe60352e05e903ab3dcde Mon Sep 17 00:00:00 2001 From: Himself65 Date: Sat, 22 Apr 2023 17:25:25 -0500 Subject: [PATCH] refactor(y-indexeddb): move migrate function separate (#2086) --- packages/y-indexeddb/src/index.ts | 118 +-------------------------- packages/y-indexeddb/src/shared.ts | 2 +- packages/y-indexeddb/src/utils.ts | 124 ++++++++++++++++++++++++++++- 3 files changed, 128 insertions(+), 116 deletions(-) diff --git a/packages/y-indexeddb/src/index.ts b/packages/y-indexeddb/src/index.ts index f20196ada0..743bc8ed20 100644 --- a/packages/y-indexeddb/src/index.ts +++ b/packages/y-indexeddb/src/index.ts @@ -1,22 +1,20 @@ import { openDB } from 'idb'; -import type { IDBPDatabase } from 'idb/build/entry'; import { applyUpdate, diffUpdate, Doc, encodeStateAsUpdate, encodeStateVector, - mergeUpdates, UndoManager, } from 'yjs'; import type { BlockSuiteBinaryDB, IndexedDBProvider, - OldYjsDB, WorkspaceMilestone, } from './shared'; import { dbVersion, DEFAULT_DB_NAME, upgradeDB } from './shared'; +import { tryMigrate } from './utils'; const indexeddbOrigin = Symbol('indexeddb-provider-origin'); const snapshotOrigin = Symbol('snapshot-origin'); @@ -27,23 +25,6 @@ export function setMergeCount(count: number) { mergeCount = count; } -async function databaseExists(name: string): Promise { - return new Promise(resolve => { - const req = indexedDB.open(name); - let existed = true; - req.onsuccess = function () { - req.result.close(); - if (!existed) { - indexedDB.deleteDatabase(name); - } - resolve(existed); - }; - req.onupgradeneeded = function () { - existed = false; - }; - }); -} - export function revertUpdate( doc: Doc, snapshotUpdate: Uint8Array, @@ -120,7 +101,7 @@ export const markMilestone = async ( export const getMilestones = async ( id: string, - dbName = DEFAULT_DB_NAME + dbName: string = DEFAULT_DB_NAME ): Promise => { const dbPromise = openDB(dbName, dbVersion, { upgrade: upgradeDB, @@ -136,12 +117,10 @@ export const getMilestones = async ( return milestone.milestone; }; -let allDb: IDBDatabaseInfo[]; - export const createIndexedDBProvider = ( id: string, doc: Doc, - dbName = DEFAULT_DB_NAME + dbName: string = DEFAULT_DB_NAME ): IndexedDBProvider => { let resolve: () => void; let reject: (reason?: unknown) => void; @@ -217,96 +196,7 @@ export const createIndexedDBProvider = ( doc.on('destroy', handleDestroy); // only run promise below, otherwise the logic is incorrect const db = await dbPromise; - do { - if (!allDb || localStorage.getItem(`${dbName}-migration`) !== 'true') { - try { - allDb = await indexedDB.databases(); - } catch { - // in firefox, `indexedDB.databases` is not exist - if (await databaseExists(id)) { - await openDB>(id, 1).then(async oldDB => { - if (!oldDB.objectStoreNames.contains('updates')) { - return; - } - const t = oldDB - .transaction('updates', 'readonly') - .objectStore('updates'); - const updates = await t.getAll(); - if ( - !Array.isArray(updates) || - !updates.every(update => update instanceof Uint8Array) - ) { - return; - } - const update = mergeUpdates(updates); - const workspaceTransaction = db - .transaction('workspace', 'readwrite') - .objectStore('workspace'); - const data = await workspaceTransaction.get(id); - if (!data) { - console.log('upgrading the database'); - await workspaceTransaction.put({ - id, - updates: [ - { - timestamp: Date.now(), - update, - }, - ], - }); - } - }); - break; - } - } - // run the migration - await Promise.all( - allDb.map(meta => { - if (meta.name && meta.version === 1) { - const name = meta.name; - const version = meta.version; - return openDB>(name, version).then( - async oldDB => { - if (!oldDB.objectStoreNames.contains('updates')) { - return; - } - const t = oldDB - .transaction('updates', 'readonly') - .objectStore('updates'); - const updates = await t.getAll(); - if ( - !Array.isArray(updates) || - !updates.every(update => update instanceof Uint8Array) - ) { - return; - } - const update = mergeUpdates(updates); - const workspaceTransaction = db - .transaction('workspace', 'readwrite') - .objectStore('workspace'); - const data = await workspaceTransaction.get(name); - if (!data) { - console.log('upgrading the database'); - await workspaceTransaction.put({ - id: name, - updates: [ - { - timestamp: Date.now(), - update, - }, - ], - }); - } - } - ); - } - }) - ); - localStorage.setItem(`${dbName}-migration`, 'true'); - break; - } - // eslint-disable-next-line no-constant-condition - } while (false); + await tryMigrate(db, id, dbName); const store = db .transaction('workspace', 'readwrite') .objectStore('workspace'); diff --git a/packages/y-indexeddb/src/shared.ts b/packages/y-indexeddb/src/shared.ts index a79452b5b4..98ad6673d6 100644 --- a/packages/y-indexeddb/src/shared.ts +++ b/packages/y-indexeddb/src/shared.ts @@ -1,7 +1,7 @@ import type { DBSchema, IDBPDatabase } from 'idb/build/entry'; export const dbVersion = 1; -export const DEFAULT_DB_NAME = 'affine-local' as const; +export const DEFAULT_DB_NAME = 'affine-local'; export function upgradeDB(db: IDBPDatabase) { db.createObjectStore('workspace', { keyPath: 'id' }); diff --git a/packages/y-indexeddb/src/utils.ts b/packages/y-indexeddb/src/utils.ts index f1cd74cecb..8c54aded7c 100644 --- a/packages/y-indexeddb/src/utils.ts +++ b/packages/y-indexeddb/src/utils.ts @@ -1,9 +1,131 @@ import { openDB } from 'idb'; +import type { IDBPDatabase } from 'idb/build/entry'; import { mergeUpdates } from 'yjs'; -import type { BlockSuiteBinaryDB, UpdateMessage } from './shared'; +import type { BlockSuiteBinaryDB, OldYjsDB, UpdateMessage } from './shared'; import { dbVersion, DEFAULT_DB_NAME, upgradeDB } from './shared'; +let allDb: IDBDatabaseInfo[]; + +async function databaseExists(name: string): Promise { + return new Promise(resolve => { + const req = indexedDB.open(name); + let existed = true; + req.onsuccess = function () { + req.result.close(); + if (!existed) { + indexedDB.deleteDatabase(name); + } + resolve(existed); + }; + req.onupgradeneeded = function () { + existed = false; + }; + }); +} + +/** + * try to migrate the old database to the new database + * this function will be removed in the future + * since we don't need to support the old database + */ +export async function tryMigrate( + db: IDBPDatabase, + id: string, + dbName = DEFAULT_DB_NAME +) { + do { + if (!allDb || localStorage.getItem(`${dbName}-migration`) !== 'true') { + try { + allDb = await indexedDB.databases(); + } catch { + // in firefox, `indexedDB.databases` is not existed + if (await databaseExists(id)) { + await openDB>(id, 1).then(async oldDB => { + if (!oldDB.objectStoreNames.contains('updates')) { + return; + } + const t = oldDB + .transaction('updates', 'readonly') + .objectStore('updates'); + const updates = await t.getAll(); + if ( + !Array.isArray(updates) || + !updates.every(update => update instanceof Uint8Array) + ) { + return; + } + const update = mergeUpdates(updates); + const workspaceTransaction = db + .transaction('workspace', 'readwrite') + .objectStore('workspace'); + const data = await workspaceTransaction.get(id); + if (!data) { + console.log('upgrading the database'); + await workspaceTransaction.put({ + id, + updates: [ + { + timestamp: Date.now(), + update, + }, + ], + }); + } + }); + break; + } + } + // run the migration + await Promise.all( + allDb.map(meta => { + if (meta.name && meta.version === 1) { + const name = meta.name; + const version = meta.version; + return openDB>(name, version).then( + async oldDB => { + if (!oldDB.objectStoreNames.contains('updates')) { + return; + } + const t = oldDB + .transaction('updates', 'readonly') + .objectStore('updates'); + const updates = await t.getAll(); + if ( + !Array.isArray(updates) || + !updates.every(update => update instanceof Uint8Array) + ) { + return; + } + const update = mergeUpdates(updates); + const workspaceTransaction = db + .transaction('workspace', 'readwrite') + .objectStore('workspace'); + const data = await workspaceTransaction.get(name); + if (!data) { + console.log('upgrading the database'); + await workspaceTransaction.put({ + id: name, + updates: [ + { + timestamp: Date.now(), + update, + }, + ], + }); + } + } + ); + } + }) + ); + localStorage.setItem(`${dbName}-migration`, 'true'); + break; + } + // eslint-disable-next-line no-constant-condition + } while (false); +} + export async function downloadBinary( id: string, dbName = DEFAULT_DB_NAME