Skip to content

Commit

Permalink
update db version if required tables do not exist yet
Browse files Browse the repository at this point in the history
  • Loading branch information
cnoelle committed Sep 19, 2024
1 parent 1fb49f8 commit d48410d
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 11 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "lru-cache-idb",
"version": "0.2.0",
"version": "0.3.0",
"description": "A least-recently-used (LRU) cache for web applications based on IndexedDB.",
"type": "module",
"types": "./dist/cache.d.ts",
Expand Down
53 changes: 44 additions & 9 deletions src/impl/CacheImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ export class LruCacheIndexedDBImpl<T> implements LruCacheIndexedDB<T> {
readonly #indexedDB: IDBFactory;
readonly #IDBKeyRange: any;
readonly #database: string;
readonly #dbVersion: number = 1;
readonly #itemsStorage: string;
readonly #accessTimesStorage: string;
readonly #config: LruIdbConfig;
readonly #items: Table<T>;
readonly #accessTimes: Table<{t: MillisecondsSinceEpoch}>;
readonly #persistenceOrchestrator: PersistenceOrchestrator|undefined;
readonly #dbLoader: (options?: CacheRequestOptions) => Promise<IDBDatabase>;
#dbInitialized: boolean = false;

readonly #eviction: PeriodicTask|undefined;
// TODO interruptible?
Expand Down Expand Up @@ -342,15 +342,50 @@ export class LruCacheIndexedDBImpl<T> implements LruCacheIndexedDB<T> {
keysSorted.forEach(key => this.#memory!.delete(key));
}

readonly #openDb = (options?: CacheRequestOptions): Promise<IDBDatabase> => {
// must only be called from onupgradeneeded callback
#createObjectStores(db: IDBDatabase) {
const objectStores = db.objectStoreNames;
const hasItems = objectStores.contains(this.#itemsStorage);
const hasAccessTimes = objectStores.contains(this.#accessTimesStorage);
if (!hasItems)
db.createObjectStore(this.#itemsStorage);
if (!hasAccessTimes) {
const timesStore = db.createObjectStore(this.#accessTimesStorage);
timesStore.createIndex("time", "t", {unique: false});
}
this.#dbInitialized = true;
}

#initializeDb = async () => {
let nextDbVersion: number|undefined = undefined;
while (!this.#dbInitialized) {
const initPromise: Promise<[number, boolean]> = new Promise<[number, boolean]>((resolve, reject) => {
const request: IDBOpenDBRequest = this.#indexedDB.open(this.#database, nextDbVersion); // open without explicit version initially, to get the latest
request.onupgradeneeded = (event) => this.#createObjectStores((event.target as any).result);
request.onerror = reject;
request.onsuccess = event => {
const db = (event.target as any).result as IDBDatabase;
if (!this.#dbInitialized) {
const objectStores = db.objectStoreNames;
const hasItems = objectStores.contains(this.#itemsStorage);
const hasAccessTimes = objectStores.contains(this.#accessTimesStorage);
if (hasItems && hasAccessTimes)
this.#dbInitialized = true;
}
resolve([db.version, this.#dbInitialized]);
};
});
const [dbVersion, initialized] = await initPromise;
nextDbVersion = dbVersion + 1;
}
}


readonly #openDb = async (options?: CacheRequestOptions): Promise<IDBDatabase> => {
if (!this.#dbInitialized)
await this.#initializeDb();
const dbPromise: Promise<IDBDatabase> = new Promise((resolve, reject) => {
const request: IDBOpenDBRequest = this.#indexedDB.open(this.#database, this.#dbVersion);
request.onupgradeneeded = (event) => {
const db: IDBDatabase = (event.target as any).result;
const objectStore = db.createObjectStore(this.#itemsStorage /*, { keyPath: "key" }*/);
const timesStore = db.createObjectStore(this.#accessTimesStorage);
timesStore.createIndex("time", "t", {unique: false});
};
const request: IDBOpenDBRequest = this.#indexedDB.open(this.#database /*, this.#dbVersion*/); // open without explicit version, to get the latest
request.onerror = reject;
options?.signal?.addEventListener("abort", reject, {once: true});
request.onsuccess = event => {
Expand Down
2 changes: 1 addition & 1 deletion test/testCacheWithMemory.js
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ test("peek does not affect access times for default cache and immediate persiste
await defaultCache.close();
});

test.only("Order of items works with in-memory updates in memory cache", async t => {
test("Order of items works with in-memory updates in memory cache", async t => {
const persistencePeriod = 50;
const defaultCache = createFakeIdb({persistencePeriod: persistencePeriod, memoryConfig: { maxItemsInMemory: 10 }});
const obj1 = {a: "test1", b: 1};
Expand Down

0 comments on commit d48410d

Please sign in to comment.