Skip to content

Commit

Permalink
SharedStorage - Add memCache backing
Browse files Browse the repository at this point in the history
  • Loading branch information
Inrixia committed Jun 22, 2024
1 parent e6a9428 commit 3f7886f
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 30 deletions.
19 changes: 10 additions & 9 deletions plugins/_lib/Caches/TrackInfoCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import { ManifestMimeType } from "../trackBytes/getPlaybackInfo";

import { SharedObjectStore } from "../sharedStorage";

import { Tracer } from "../trace";
const tracer = Tracer("TrackInfoCache");

const { parseStream } = <{ parseStream: typeof ParseStreamType }>require("music-metadata/lib/core");

export type TrackInfo = {
Expand All @@ -23,25 +26,23 @@ export type TrackInfo = {
const WEEK = 7 * 24 * 60 * 60 * 1000;
export class TrackInfoCache {
private static readonly _listeners: Map<[TrackInfo["trackId"], AudioQuality], ((trackInfo: TrackInfo) => void)[]> = new Map();
private static readonly _store: SharedObjectStore<[TrackInfo["trackId"], TrackInfo["audioQuality"]], TrackInfo> = new SharedObjectStore("TrackInfoCache", { keyPath: ["trackId", "audioQuality"] });
public static get(trackId: TrackInfo["trackId"], audioQuality: AudioQuality): Promise<TrackInfo | undefined> {
return this._store.get([trackId, audioQuality]);
}

public static test() {
this._store.getAll().then(console.log);
private static readonly _store: SharedObjectStore<[TrackInfo["trackId"], TrackInfo["audioQuality"]], TrackInfo> = new SharedObjectStore("TrackInfoCache", {
keyPath: ["trackId", "audioQuality"],
});
public static async get(trackId: TrackInfo["trackId"], audioQuality: AudioQuality): Promise<TrackInfo | undefined> {
return this._store.getCache([trackId, audioQuality], tracer.err.withContext("get"));
}

public static async register(trackId: TrackInfo["trackId"], audioQuality: AudioQuality, onTrackInfo: (trackInfoP: TrackInfo) => void): Promise<void> {
const listeners = this._listeners.get([trackId, audioQuality]);
if (listeners !== undefined) listeners.push(onTrackInfo);
else this._listeners.set([trackId, audioQuality], [onTrackInfo]);
const trackInfo = await this._store.get([trackId, audioQuality]);
const trackInfo = await this._store.getCache([trackId, audioQuality], tracer.err.withContext("register"));
if (trackInfo !== undefined) onTrackInfo(trackInfo);
}

private static put(trackInfo: TrackInfo): void {
this._store.put(trackInfo);
this._store.putCache(trackInfo, [trackInfo.trackId, trackInfo.audioQuality], tracer.err.withContext("put"));
for (const listener of TrackInfoCache._listeners.get([trackInfo.trackId, trackInfo.audioQuality]) || []) listener(trackInfo);
}

Expand Down
7 changes: 5 additions & 2 deletions plugins/_lib/api/requestCache.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import { rejectNotOk, RequestOptionsWithBody, requestStream, toJson } from "../fetch";
import { SharedObjectStore } from "../sharedStorage";

import { Tracer } from "../trace";
const trace = Tracer("[requestCache]");

export const requestCache = new SharedObjectStore<string, any>("requestCache");
export const requestCached = async <T>(url: string, options?: RequestOptionsWithBody): Promise<T> => {
let apiRes = await requestCache.get(url);
let apiRes = await requestCache.getCache(url, trace.err.withContext("get"));
if (apiRes !== undefined) return <T>apiRes;
apiRes = await requestStream(url, options)
.then(rejectNotOk)
.then(toJson<T>);
await requestCache.put(apiRes, url);
await requestCache.putCache(apiRes, url, trace.err.withContext("put"));
return apiRes;
};
54 changes: 35 additions & 19 deletions plugins/_lib/sharedStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,34 +33,50 @@ export class SharedObjectStore<K extends IDBValidKey, V> {
return this.db.then((db) => db.close());
}

constructor(private readonly storeName: string, storeSchema?: IDBObjectStoreParameters) {
private readonly _memCache = new Map<K, V>();
constructor(private readonly storeName: string, private readonly storeSchema?: IDBObjectStoreParameters) {
SharedObjectStore.openDB(storeName, storeSchema);
}
async add(value: V, key?: K) {
return (await SharedObjectStore.db).add(this.storeName, value, key);
add(value: V, key?: K) {
return SharedObjectStore.db.then((db) => db.add(this.storeName, value, key));
}
async clear() {
return (await SharedObjectStore.db).clear(this.storeName);
clear() {
return SharedObjectStore.db.then((db) => db.clear(this.storeName));
}
async count(key?: K | null) {
return (await SharedObjectStore.db).count(this.storeName, key);
count(key?: K | null) {
return SharedObjectStore.db.then((db) => db.count(this.storeName, key));
}
async delete(key: K) {
return (await SharedObjectStore.db).delete(this.storeName, key);
delete(key: K) {
return SharedObjectStore.db.then((db) => db.delete(this.storeName, key));
}
async get(query: K) {
return (await SharedObjectStore.db).get(this.storeName, query);
getCache(query: K, errorHandler: (err?: Error) => void) {
const value = SharedObjectStore.db
.then((db) => db.get(this.storeName, query))
.then((value) => {
this._memCache.set(query, value);
return value;
})
.catch(errorHandler);
const memValue = this._memCache.get(query);
return memValue ?? value;
}
async getAll(query?: K | null, count?: number) {
return (await SharedObjectStore.db).getAll(this.storeName, query, count);
get(query: K) {
return SharedObjectStore.db.then((db) => db.get(this.storeName, query));
}
async getAllKeys(query?: K | null, count?: number) {
return (await SharedObjectStore.db).getAllKeys(this.storeName, query, count);
getAll(query?: K | null, count?: number) {
return SharedObjectStore.db.then((db) => db.getAll(this.storeName, query, count));
}
async getKey(query: K) {
return (await SharedObjectStore.db).getKey(this.storeName, query);
getAllKeys(query?: K | null, count?: number) {
return SharedObjectStore.db.then((db) => db.getAllKeys(this.storeName, query, count));
}
async put(value: V, key?: K) {
return (await SharedObjectStore.db).put(this.storeName, value, key);
getKey(query: K) {
return SharedObjectStore.db.then((db) => db.getKey(this.storeName, query));
}
putCache(value: V, key: K, errorHandler: (err?: Error) => void) {
this._memCache.set(key, value);
SharedObjectStore.db.then((db) => db.put(this.storeName, value, this.storeSchema?.keyPath !== undefined ? undefined : key)).catch(errorHandler);
}
put(value: V, key?: K) {
return SharedObjectStore.db.then((db) => db.put(this.storeName, value, key));
}
}

0 comments on commit 3f7886f

Please sign in to comment.