From 050999a910837f8a5353b1584af2b03bd8dad93d Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Wed, 13 Jan 2021 00:50:56 +0100 Subject: implement infrastructure for future DB migrations via backup --- packages/taler-wallet-core/src/db.ts | 182 ++++++++++++++++++++++++----------- 1 file changed, 124 insertions(+), 58 deletions(-) (limited to 'packages/taler-wallet-core/src/db.ts') diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts index b13abac57..aed2ce5cb 100644 --- a/packages/taler-wallet-core/src/db.ts +++ b/packages/taler-wallet-core/src/db.ts @@ -1,5 +1,11 @@ -import { Stores } from "./types/dbTypes"; -import { openDatabase, Database, Store, Index } from "./util/query"; +import { MetaStores, Stores } from "./types/dbTypes"; +import { + openDatabase, + Database, + Store, + Index, + AnyStoreMap, +} from "./util/query"; import { IDBFactory, IDBDatabase, @@ -14,7 +20,11 @@ import { Logger } from "./util/logging"; * for all previous versions must be written, which should be * avoided. */ -const TALER_DB_NAME = "taler-wallet-prod-v1"; +const TALER_DB_NAME = "taler-wallet-main-v2"; + +const TALER_META_DB_NAME = "taler-wallet-meta"; + +const CURRENT_DB_CONFIG_KEY = "currentMainDbName"; /** * Current database minor version, should be incremented @@ -23,78 +33,134 @@ const TALER_DB_NAME = "taler-wallet-prod-v1"; * backwards-compatible way or object stores and indices * are added. */ -export const WALLET_DB_MINOR_VERSION = 3; +export const WALLET_DB_MINOR_VERSION = 1; const logger = new Logger("db.ts"); -/** - * Return a promise that resolves - * to the taler wallet db. - */ -export function openTalerDatabase( - idbFactory: IDBFactory, - onVersionChange: () => void, -): Promise { - const onUpgradeNeeded = ( - db: IDBDatabase, - oldVersion: number, - newVersion: number, - upgradeTransaction: IDBTransaction, - ): void => { - if (oldVersion === 0) { - for (const n in Stores) { - if ((Stores as any)[n] instanceof Store) { - const si: Store = (Stores as any)[n]; - const s = db.createObjectStore(si.name, si.storeParams); - for (const indexName in si as any) { - if ((si as any)[indexName] instanceof Index) { - const ii: Index = (si as any)[ - indexName - ]; - s.createIndex(ii.indexName, ii.keyPath, ii.options); - } +function upgradeFromStoreMap( + storeMap: AnyStoreMap, + db: IDBDatabase, + oldVersion: number, + newVersion: number, + upgradeTransaction: IDBTransaction, +): void { + if (oldVersion === 0) { + for (const n in storeMap) { + if ((storeMap as any)[n] instanceof Store) { + const si: Store = (storeMap as any)[n]; + const s = db.createObjectStore(si.name, si.storeParams); + for (const indexName in si as any) { + if ((si as any)[indexName] instanceof Index) { + const ii: Index = (si as any)[indexName]; + s.createIndex(ii.indexName, ii.keyPath, ii.options); } } } - return; - } - if (oldVersion === newVersion) { - return; } - logger.info(`upgrading database from ${oldVersion} to ${newVersion}`); - for (const n in Stores) { - if ((Stores as any)[n] instanceof Store) { - const si: Store = (Stores as any)[n]; - let s: IDBObjectStore; - const storeVersionAdded = si.storeParams?.versionAdded ?? 1; - if (storeVersionAdded > oldVersion) { - s = db.createObjectStore(si.name, si.storeParams); - } else { - s = upgradeTransaction.objectStore(si.name); - } - for (const indexName in si as any) { - if ((si as any)[indexName] instanceof Index) { - const ii: Index = (si as any)[indexName]; - const indexVersionAdded = ii.options?.versionAdded ?? 0; - if ( - indexVersionAdded > oldVersion || - storeVersionAdded > oldVersion - ) { - s.createIndex(ii.indexName, ii.keyPath, ii.options); - } + return; + } + if (oldVersion === newVersion) { + return; + } + logger.info(`upgrading database from ${oldVersion} to ${newVersion}`); + for (const n in Stores) { + if ((Stores as any)[n] instanceof Store) { + const si: Store = (Stores as any)[n]; + let s: IDBObjectStore; + const storeVersionAdded = si.storeParams?.versionAdded ?? 1; + if (storeVersionAdded > oldVersion) { + s = db.createObjectStore(si.name, si.storeParams); + } else { + s = upgradeTransaction.objectStore(si.name); + } + for (const indexName in si as any) { + if ((si as any)[indexName] instanceof Index) { + const ii: Index = (si as any)[indexName]; + const indexVersionAdded = ii.options?.versionAdded ?? 0; + if ( + indexVersionAdded > oldVersion || + storeVersionAdded > oldVersion + ) { + s.createIndex(ii.indexName, ii.keyPath, ii.options); } } } } - }; + } +} - return openDatabase( +function onTalerDbUpgradeNeeded( + db: IDBDatabase, + oldVersion: number, + newVersion: number, + upgradeTransaction: IDBTransaction, +) { + upgradeFromStoreMap(Stores, db, oldVersion, newVersion, upgradeTransaction); +} + +function onMetaDbUpgradeNeeded( + db: IDBDatabase, + oldVersion: number, + newVersion: number, + upgradeTransaction: IDBTransaction, +) { + upgradeFromStoreMap( + MetaStores, + db, + oldVersion, + newVersion, + upgradeTransaction, + ); +} + +/** + * Return a promise that resolves + * to the taler wallet db. + */ +export async function openTalerDatabase( + idbFactory: IDBFactory, + onVersionChange: () => void, +): Promise> { + const metaDbHandle = await openDatabase( + idbFactory, + TALER_META_DB_NAME, + 1, + () => {}, + onMetaDbUpgradeNeeded, + ); + + const metaDb = new Database(metaDbHandle, MetaStores); + let currentMainVersion: string | undefined; + await metaDb.runWithWriteTransaction([MetaStores.metaConfig], async (tx) => { + const dbVersionRecord = await tx.get( + MetaStores.metaConfig, + CURRENT_DB_CONFIG_KEY, + ); + if (!dbVersionRecord) { + currentMainVersion = TALER_DB_NAME; + await tx.put(MetaStores.metaConfig, { + key: CURRENT_DB_CONFIG_KEY, + value: TALER_DB_NAME, + }); + } else { + currentMainVersion = dbVersionRecord.key; + } + }); + + if (currentMainVersion !== TALER_DB_NAME) { + // In the future, the migration logic will be implemented here. + throw Error(`migration from database ${currentMainVersion} not supported`); + } + + const mainDbHandle = await openDatabase( idbFactory, TALER_DB_NAME, WALLET_DB_MINOR_VERSION, onVersionChange, - onUpgradeNeeded, + onTalerDbUpgradeNeeded, ); + + return new Database(mainDbHandle, Stores); } export function deleteTalerDatabase(idbFactory: IDBFactory): void { -- cgit v1.2.3