Skip to content

Commit

Permalink
refactor migration code
Browse files Browse the repository at this point in the history
  • Loading branch information
a-type committed Jun 20, 2024
1 parent 274e047 commit bf4bfa2
Show file tree
Hide file tree
Showing 12 changed files with 860 additions and 787 deletions.
14 changes: 6 additions & 8 deletions packages/store/src/client/Client.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,25 @@
import {
assert,
debounce,
DocumentBaseline,
EventSubscriber,
Migration,
Operation,
SchemaCollection,
} from '@verdant-web/common';
import { Context } from '../context.js';
import { DocumentManager } from '../DocumentManager.js';
import { EntityStore } from '../entities/EntityStore.js';
import { FileManager, FileManagerConfig } from '../files/FileManager.js';
import { ReturnedFileData } from '../files/FileStorage.js';
import {
closeDatabase,
deleteAllDatabases,
getSizeOfObjectStore,
} from '../idb.js';
import { ExportData, Metadata } from '../metadata/Metadata.js';
import { openDocumentDatabase } from '../migration/openDatabase.js';
import { EntityStore } from '../entities/EntityStore.js';
import { NoSync, ServerSync, ServerSyncOptions, Sync } from '../sync/Sync.js';
import { openQueryDatabase } from '../migration/openQueryDatabase.js';
import { CollectionQueries } from '../queries/CollectionQueries.js';
import { QueryCache } from '../queries/QueryCache.js';
import { ReturnedFileData } from '../files/FileStorage.js';
import { NoSync, ServerSync, ServerSyncOptions, Sync } from '../sync/Sync.js';

interface ClientConfig<Presence = any> {
syncConfig?: ServerSyncOptions<Presence>;
Expand Down Expand Up @@ -382,7 +380,7 @@ export class Client<Presence = any, Profile = any> extends EventSubscriber<{
}
// now open the document DB empty at the specified version
// and initialize it from the meta DB
this.context.documentDb = await openDocumentDatabase({
this.context.documentDb = await openQueryDatabase({
meta: this.meta,
migrations: this.config.migrations,
context: this.context,
Expand All @@ -402,7 +400,7 @@ export class Client<Presence = any, Profile = any> extends EventSubscriber<{
this.context.log('Migrating up to latest schema...');
// put the schema back
this.context.schema = currentSchema;
this.context.documentDb = await openDocumentDatabase({
this.context.documentDb = await openQueryDatabase({
meta: this.meta,
migrations: this.config.migrations,
context: this.context,
Expand Down
10 changes: 4 additions & 6 deletions packages/store/src/client/ClientDescriptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,7 @@ import {
openMetadataDatabase,
openWIPMetadataDatabase,
} from '../metadata/openMetadataDatabase.js';
import {
openDocumentDatabase,
openWIPDocumentDatabase,
} from '../migration/openDatabase.js';
import { openWIPDatabase } from '../migration/openWIPDatabase.js';
import { ServerSyncOptions } from '../sync/Sync.js';
import { UndoHistory } from '../UndoHistory.js';
import { Client } from './Client.js';
Expand All @@ -26,6 +23,7 @@ import {
} from '../idb.js';
import { FakeWeakRef } from '../FakeWeakRef.js';
import { METADATA_VERSION_KEY } from './constants.js';
import { openQueryDatabase } from '../migration/openQueryDatabase.js';

export interface ClientDescriptorOptions<Presence = any, Profile = any> {
/** The schema used to create this client */
Expand Down Expand Up @@ -193,7 +191,7 @@ export class ClientDescriptor<
getNow: () => meta.now,
});

const documentDb = await openDocumentDatabase({
const documentDb = await openQueryDatabase({
context: contextWithNow,
version: init.schema.version,
meta,
Expand Down Expand Up @@ -261,7 +259,7 @@ export class ClientDescriptor<
// verify schema integrity
await meta.updateSchema(init.schema, init.overrideSchemaConflict);

const documentDb = await openWIPDocumentDatabase({
const documentDb = await openWIPDatabase({
context: contextWithNow,
version: init.schema.version,
meta,
Expand Down
10 changes: 8 additions & 2 deletions packages/store/src/entities/EntityStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,12 @@ export type EntityStoreEvents = {
resetAll: WeakEvent<EntityStore, void>;
};

type IncomingData = {
export interface IncomingData {
operations?: Operation[];
baselines?: DocumentBaseline[];
reset?: boolean;
isLocal?: boolean;
};
}

export class EntityStore extends Disposable {
private ctx;
Expand Down Expand Up @@ -137,6 +137,12 @@ export class EntityStore extends Disposable {
await this.processData(data);
};

empty = async () => {
await this.queryableStorage.reset();
this.events.resetAll.invoke(this);
this.cache.clear();
};

private resetData = async () => {
if (this.disposed) {
this.ctx.log('warn', 'EntityStore is disposed, not resetting local data');
Expand Down
21 changes: 20 additions & 1 deletion packages/store/src/idb.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { roughSizeOfObject } from '@verdant-web/common';

export const globalIDB =
typeof window !== 'undefined' ? window.indexedDB : (undefined as any);

export function isAbortError(err: unknown) {
return err instanceof Error && err.name === 'AbortError';
}
Expand Down Expand Up @@ -109,7 +112,7 @@ export async function closeDatabase(db: IDBDatabase) {

export async function deleteAllDatabases(
namespace: string,
indexedDB: IDBFactory = window.indexedDB,
indexedDB: IDBFactory = globalIDB,
) {
const req1 = indexedDB.deleteDatabase([namespace, 'meta'].join('_'));
const req2 = indexedDB.deleteDatabase([namespace, 'collections'].join('_'));
Expand Down Expand Up @@ -163,3 +166,19 @@ export function createAbortableTransaction(
}
return tx;
}

/**
* Empties all data in a database without changing
* its structure.
*/
export function emptyDatabase(db: IDBDatabase) {
const storeNames = Array.from(db.objectStoreNames);
const tx = db.transaction(storeNames, 'readwrite');
for (const storeName of storeNames) {
tx.objectStore(storeName).clear();
}
return new Promise<void>((resolve, reject) => {
tx.oncomplete = () => resolve();
tx.onerror = () => reject(tx.error);
});
}
82 changes: 62 additions & 20 deletions packages/store/src/migration/db.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { closeDatabase, globalIDB, storeRequestPromise } from '../idb.js';
import { OpenDocumentDbContext } from './types.js';

export async function getDatabaseVersion(
indexedDB: IDBFactory,
namespace: string,
Expand Down Expand Up @@ -42,12 +45,6 @@ export async function getDatabaseVersion(
return currentVersion;
}

export async function closeDatabase(db: IDBDatabase) {
db.close();
// FIXME: this isn't right!!!!
await new Promise<void>((resolve) => resolve());
}

/**
* Upgrades the database to the given version, using the given upgrader function.
*/
Expand All @@ -61,8 +58,11 @@ export async function upgradeDatabase(
event: IDBVersionChangeEvent,
) => void,
log?: (...args: any[]) => void,
): Promise<void> {
function openAndUpgrade(resolve: () => void, reject: (err: Error) => void) {
): Promise<IDBDatabase> {
function openAndUpgrade(
resolve: (db: IDBDatabase) => void,
reject: (err: Error) => void,
) {
const request = indexedDb.open(
[namespace, 'collections'].join('_'),
version,
Expand All @@ -74,9 +74,8 @@ export async function upgradeDatabase(
wasUpgraded = true;
};
request.onsuccess = (event) => {
request.result.close();
if (wasUpgraded) {
resolve();
resolve(request.result);
} else {
reject(
new Error(
Expand All @@ -95,7 +94,7 @@ export async function upgradeDatabase(
// }, 200);
};
}
return new Promise(openAndUpgrade);
return new Promise<IDBDatabase>(openAndUpgrade);
}

export async function acquireLock(
Expand All @@ -110,23 +109,28 @@ export async function acquireLock(
}
}

export async function openDatabase(
indexedDb: IDBFactory,
namespace: string,
version: number,
log?: (...args: any[]) => void,
): Promise<IDBDatabase> {
log?.('debug', 'Opening database', namespace, 'at version', version);
export async function openDatabase({
indexedDB = globalIDB,
namespace,
version,
context,
}: {
indexedDB?: IDBFactory;
namespace: string;
version: number;
context: OpenDocumentDbContext;
}): Promise<IDBDatabase> {
context.log('debug', 'Opening database', namespace, 'at version', version);
const db = await new Promise<IDBDatabase>((resolve, reject) => {
const request = indexedDb.open(
const request = indexedDB.open(
[namespace, 'collections'].join('_'),
version,
);
request.onupgradeneeded = async (event) => {
const transaction = request.transaction!;
transaction.abort();

log?.(
context.log(
'error',
'Database upgrade needed, but not expected',
'Expected',
Expand Down Expand Up @@ -158,3 +162,41 @@ export async function openDatabase(

return db;
}

export async function copyAll(
sourceDatabase: IDBDatabase,
targetDatabase: IDBDatabase,
) {
// DOMStringList... doesn't have iterable... why
const sourceStoreNames = new Array<string>();
for (let i = 0; i < sourceDatabase.objectStoreNames.length; i++) {
sourceStoreNames.push(sourceDatabase.objectStoreNames[i]);
}

const copyFromTransaction = sourceDatabase.transaction(
sourceStoreNames,
'readonly',
);
const copyFromStores = sourceStoreNames.map((name) =>
copyFromTransaction.objectStore(name),
);
const allObjects = await Promise.all(
copyFromStores.map((store) => storeRequestPromise(store.getAll())),
);

const copyToTransaction = targetDatabase.transaction(
sourceStoreNames,
'readwrite',
);
const copyToStores = sourceStoreNames.map((name) =>
copyToTransaction.objectStore(name),
);

for (let i = 0; i < copyToStores.length; i++) {
await Promise.all(
allObjects[i].map((obj) => {
return storeRequestPromise(copyToStores[i].put(obj));
}),
);
}
}
Loading

0 comments on commit bf4bfa2

Please sign in to comment.