Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Log which db stores are created and deleted #57

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion src/IndexedDbProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ export class IndexedDbProvider extends DbProvider {
// Avoid clearing object stores when event.oldVersion returns 0.
// oldVersion returns 0 if db doesn't exist yet: https://developer.mozilla.org/en-US/docs/Web/API/IDBVersionChangeEvent/oldVersion
if (event.oldVersion) {
const deletedStores: string[] = [];
if (
schema.lastUsableVersion &&
event.oldVersion < schema.lastUsableVersion
Expand All @@ -208,17 +209,24 @@ export class IndexedDbProvider extends DbProvider {
);
each(db.objectStoreNames, (name) => {
db.deleteObjectStore(name);
deletedStores.push(name);
});
}

// Delete dead stores
each(db.objectStoreNames, (storeName) => {
if (!some(schema.stores, (store) => store.name === storeName)) {
db.deleteObjectStore(storeName);
deletedStores.push(storeName);
}
});

this.logWriter.log(`Deleted all object stores`, { dbName });
this.logWriter.log(`Deleted all object stores`, {
dbName,
deletedStores,
oldVersion: event.oldVersion,
newVersion: event.newVersion ?? undefined,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is the use of doing ?? undefined? Doesn't this just return undefined?

});
}

// Create all stores
Expand All @@ -227,6 +235,7 @@ export class IndexedDbProvider extends DbProvider {
`Creating stores after db wipe due to lastUsableVersion change`
);
}
const createdStores: string[] = [];
each(schema.stores, (storeSchema) => {
let store: IDBObjectStore;
const storeExistedBefore = includes(
Expand All @@ -245,6 +254,7 @@ export class IndexedDbProvider extends DbProvider {
store = db.createObjectStore(storeSchema.name, {
keyPath: primaryKeyPath,
} as any);
createdStores.push(storeSchema.name);
} else {
// store exists, might need to update indexes and migrate the data
store = trans.objectStore(storeSchema.name);
Expand Down Expand Up @@ -428,6 +438,10 @@ export class IndexedDbProvider extends DbProvider {
);
}
});

if (createdStores.length > 0) {
this.logWriter.log(`Created stores`, { dbName, createdStores });
}
};

const promise = IndexedDbProvider.WrapRequest<IDBDatabase>(dbOpen);
Expand Down
4 changes: 4 additions & 0 deletions src/LogWriter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ export interface ILoggerContext {
dbName?: string;
storeName?: string;
indexName?: string;
deletedStores?: string[];
createdStores?: string[];
oldVersion?: number;
newVersion?: number;
}

export class LogWriter {
Expand Down
187 changes: 182 additions & 5 deletions src/tests/ObjectStoreProvider.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ import {
FullTextTermResolution,
IDBCloseConnectionPayload,
OnCloseHandler,
IObjectStoreProviderLogger,
} from "../ObjectStoreProvider";

import { InMemoryProvider } from "../InMemoryProvider";
import { IndexedDbProvider } from "../IndexedDbProvider";

import { serializeValueToOrderableString } from "../ObjectStoreProviderUtils";
import { MockLogger, TestLogger } from "./ObjectStoreProviderLogger.mock";

type TestObj = { id?: string; val: string };

Expand All @@ -26,19 +28,25 @@ function openProvider(
schema: DbSchema,
wipeFirst: boolean,
handleOnClose?: OnCloseHandler,
supportsRollback?: boolean
supportsRollback?: boolean,
logger?: IObjectStoreProviderLogger
) {
let provider: DbProvider;
if (providerName === "memory-rbtree") {
provider = new InMemoryProvider("red-black-tree", supportsRollback);
} else if (providerName === "memory-btree") {
provider = new InMemoryProvider("b+tree", supportsRollback);
} else if (providerName === "indexeddb") {
provider = new IndexedDbProvider();
provider = new IndexedDbProvider(undefined, undefined, undefined, logger);
} else if (providerName === "indexeddbfakekeys") {
provider = new IndexedDbProvider(undefined, false);
provider = new IndexedDbProvider(undefined, false, undefined, logger);
} else if (providerName === "indexeddbonclose") {
provider = new IndexedDbProvider(undefined, undefined, handleOnClose);
provider = new IndexedDbProvider(
undefined,
undefined,
handleOnClose,
logger
);
} else {
throw new Error("Provider not found for name: " + providerName);
}
Expand Down Expand Up @@ -2704,6 +2712,10 @@ describe("ObjectStoreProvider", function () {

if (provName.indexOf("memory") === -1) {
describe("Schema Upgrades", () => {
let mockLogger: TestLogger;
beforeEach(() => {
mockLogger = new MockLogger();
});
it("Opening an older DB version", (done) => {
openProvider(
provName,
Expand Down Expand Up @@ -2766,7 +2778,10 @@ describe("ObjectStoreProvider", function () {
},
],
},
true
true,
undefined,
undefined,
mockLogger
)
.then((prov) => {
return prov
Expand Down Expand Up @@ -3054,6 +3069,168 @@ describe("ObjectStoreProvider", function () {
);
});

it("logs no deleted stores on db upgrade when no stores are deleted", (done) => {
openProvider(
provName,
{
version: 1,
stores: [
{
name: "test",
primaryKeyPath: "id",
},
],
},
true
)
.then((prov) => {
prov.close();
return openProvider(
provName,
{
version: 2,
stores: [
{
name: "test",
primaryKeyPath: "id",
},
],
},
false,
undefined,
undefined,
mockLogger
).then((prov) => {
try {
assert(
mockLogger.hasLoggedMessageContaining("deletedStores: ,")
);
return Promise.resolve();
} catch (e) {
return Promise.reject(e);
} finally {
prov.close();
}
});
})
.then(
() => done(),
(err) => done(err)
);
});
it("logs deleted stores on db upgrade when stores are deleted", (done) => {
openProvider(
provName,
{
version: 1,
stores: [
{
name: "test",
primaryKeyPath: "id",
},
{
name: "storeToDelete",
primaryKeyPath: "id",
},
{
name: "storeToDelete2",
primaryKeyPath: "id",
},
],
},
true
)
.then((prov) => {
prov.close();

return openProvider(
provName,
{
version: 2,
stores: [
{
name: "test",
primaryKeyPath: "id",
},
],
},
false,
undefined,
undefined,
mockLogger
).then((prov) => {
try {
assert(
mockLogger.hasLoggedMessageContaining(
"deletedStores: storeToDelete,storeToDelete2,"
)
);
return;
} catch (e) {
return Promise.reject(e);
} finally {
prov.close();
}
});
})
.then(
() => done(),
(err) => done(err)
);
});
it("logs created stores on db upgrade", (done) => {
openProvider(
provName,
{
version: 1,
stores: [
{
name: "test",
primaryKeyPath: "id",
},
],
},
true
)
.then((prov) => {
prov.close();

return openProvider(
provName,
{
version: 2,
stores: [
{
name: "newStore",
primaryKeyPath: "id",
},
],
},
false,
undefined,
undefined,
mockLogger
).then((prov) => {
try {
assert(
mockLogger.hasLoggedMessageContaining(
"createdStores: newStore"
)
);
return;
} catch (e) {
return Promise.reject(e);
} finally {
prov.close();
}
});
})
.then(
() => done(),
(err) => done(err)
);
});

function testBatchUpgrade(itemByteSize: number): Promise<void> {
const recordCount = 5000;
const data: { [id: string]: { id: string; tt: string } } = {};
Expand Down
46 changes: 46 additions & 0 deletions src/tests/ObjectStoreProviderLogger.mock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { IObjectStoreProviderLogger } from "../ObjectStoreProvider";

export class MockLogger implements TestLogger {
loggedMessages: string[] = [];
loggedWarnings: string[] = [];
loggedErrors: string[] = [];

log(message: string): void {
this.loggedMessages.push(message);
}
warn(message: string): void {
this.loggedWarnings.push(message);
}
error(message: string): void {
this.loggedErrors.push(message);
}

hasLoggedMessageContaining(messageToSearch: string): boolean {
return this.loggedMessages.some((log) => log.indexOf(messageToSearch) >= 0);
}

hasLoggedWarningContaining(messageToSearch: string): boolean {
return this.loggedWarnings.some((log) => log.indexOf(messageToSearch) >= 0);
}
hasLoggedErrorContaining(messageToSearch: string): boolean {
return this.loggedErrors.some((log) => log.indexOf(messageToSearch) >= 0);
}
}

export interface TestLogger extends IObjectStoreProviderLogger {
/**
* Returns whether messageToSearch is contained in any of the messages that were logged to logger.log
* @param messageToSearch
*/
hasLoggedMessageContaining(messageToSearch: string): boolean;
/**
* Returns whether messageToSearch is contained in any of the messages that were logged to logger.warn
* @param messageToSearch
*/
hasLoggedWarningContaining(messageToSearch: string): boolean;
/**
* Returns whether messageToSearch is contained in any of the messages that were logged to logger.error
* @param messageToSearch
*/
hasLoggedErrorContaining(messageToSearch: string): boolean;
}
Loading