Skip to content

Commit

Permalink
support for low priority
Browse files Browse the repository at this point in the history
  • Loading branch information
aaronshaf committed Nov 24, 2024
1 parent 582f452 commit e4ac35c
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 12 deletions.
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,15 @@ import { IDBCache } from '@instructure/idb-cache';
// Initialize the cache
const cache = new IDBCache({
cacheKey: 'your-secure-key',
cacheBuster: 'unique-cache-buster', // Doubles as salt
// dbName?: string;
cacheBuster: 'unique-cache-buster',
// chunkSize?: number;
// maxTotalChunks?: number
// cleanupInterval?: number;
// pbkdf2Iterations?: number;
// gcTime?: number;
// dbName?: string;
// debug?: boolean,
// gcTime?: number;
// maxTotalChunks?: number
// pbkdf2Iterations?: number;
// priority?: "normal" | "low"
});

// Store an item
Expand Down
16 changes: 14 additions & 2 deletions packages/idb-cache-app/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ const App = () => {
return stored ? Number.parseInt(stored, 10) : DEFAULT_MAX_CHUNKS_STORED;
},
);
const [priority, setPriority] = useState<"normal" | "low">(() => {
const stored = localStorage.priority;
return ["normal", "low"].includes(stored) ? stored : "normal";
});

const [cacheReady, setCacheReady] = useState<boolean>(false);

Expand Down Expand Up @@ -110,6 +114,7 @@ const App = () => {
debug: true,
chunkSize: chunkSize,
maxTotalChunks: maxTotalChunksStored,
priority,
});

if (!isCancelled) {
Expand Down Expand Up @@ -138,12 +143,16 @@ const App = () => {
setCacheReady(false);
}
};
}, [chunkSize, maxTotalChunksStored]);
}, [chunkSize, maxTotalChunksStored, priority]);

useEffect(() => {
localStorage.setItem("maxTotalChunksStored", String(maxTotalChunksStored));
}, [maxTotalChunksStored]);

useEffect(() => {
localStorage.setItem("priority", String(priority));
}, [priority]);

const encryptAndStore = useCallback(async () => {
const cache = cacheRef.current;
if (!cache) {
Expand Down Expand Up @@ -401,7 +410,7 @@ const App = () => {
<WrappedFlexItem>
<RadioInputGroup
name="priority"
defaultValue="normal"
value={priority}
description={
<Flex alignItems="end">
<Flex.Item as="div">
Expand All @@ -419,6 +428,9 @@ const App = () => {
</Flex>
}
variant="toggle"
onChange={(e) => {
setPriority(e.target.value === "low" ? "low" : "normal");
}}
>
<RadioInput label="Normal" value="normal" />
<RadioInput label="Low" value="low" />
Expand Down
30 changes: 26 additions & 4 deletions packages/idb-cache/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ export class IDBCache implements AsyncStorage {
private cacheBuster: string;
private debug: boolean;
private maxTotalChunks?: number;
private priority: "normal" | "low" = "normal";

constructor(config: IDBCacheConfig) {
const {
Expand All @@ -127,6 +128,7 @@ export class IDBCache implements AsyncStorage {
cleanupInterval = CLEANUP_INTERVAL,
pbkdf2Iterations = DEFAULT_PBKDF2_ITERATIONS,
maxTotalChunks,
priority = "normal",
} = config;

this.storeName = "cache";
Expand All @@ -139,6 +141,7 @@ export class IDBCache implements AsyncStorage {
this.pbkdf2Iterations = pbkdf2Iterations;
this.maxTotalChunks = maxTotalChunks;
this.pendingRequests = new Map();
this.priority = priority;

if (!window.indexedDB)
throw new DatabaseError("IndexedDB is not supported.");
Expand Down Expand Up @@ -230,6 +233,9 @@ export class IDBCache implements AsyncStorage {
public async cleanup(): Promise<void> {
try {
const db = await this.dbReadyPromise;
if (this.priority === "low") {
await waitForAnimationFrame();
}
const transaction = db.transaction(this.storeName, "readwrite");
const store = transaction.store;
const timestampIndex = store.index("byTimestamp");
Expand Down Expand Up @@ -407,7 +413,9 @@ export class IDBCache implements AsyncStorage {
if (!this.dbReadyPromise) return null;
await this.ensureWorkerInitialized();

await waitForAnimationFrame();
if (this.priority === "low") {
await waitForAnimationFrame();
}
const db = await this.dbReadyPromise;
const baseKey = await deterministicUUID(`${this.cacheKey}:${itemKey}`);
const now = Date.now();
Expand Down Expand Up @@ -544,11 +552,16 @@ export class IDBCache implements AsyncStorage {
if (!this.dbReadyPromise) return;
await this.ensureWorkerInitialized();

await waitForAnimationFrame();
if (this.priority === "low") {
await waitForAnimationFrame();
}
const db = await this.dbReadyPromise;
const baseKey = await deterministicUUID(`${this.cacheKey}:${itemKey}`);
const expirationTimestamp = Date.now() + this.gcTime;

if (this.priority === "low") {
await waitForAnimationFrame();
}
const existingChunkKeys = await getAllChunkKeysForBaseKey(
db,
this.storeName,
Expand All @@ -570,15 +583,22 @@ export class IDBCache implements AsyncStorage {
const chunk = value.slice(i, i + this.chunkSize);
const chunkIndex = Math.floor(i / this.chunkSize);

if (this.priority === "low") {
await waitForAnimationFrame();
}
const chunkHash = await deterministicUUID(
`${this.cacheKey}:${this.cacheBuster}:${chunk}`
`${this.cacheKey}:${this.cacheBuster}:${chunk}`,
this.priority
);
const chunkKey = generateChunkKey(baseKey, chunkIndex, chunkHash);
newChunkKeys.add(chunkKey);

const isLastChunk = chunkIndex === totalChunks - 1;

if (existingChunkKeysSet.has(chunkKey)) {
if (this.priority === "low") {
await waitForAnimationFrame();
}
const existingChunk = await db.get(this.storeName, chunkKey);
if (
existingChunk &&
Expand Down Expand Up @@ -636,7 +656,9 @@ export class IDBCache implements AsyncStorage {
}

await Promise.all(operationPromises);

if (this.priority === "low") {
await waitForAnimationFrame();
}
await tx.done;

const duration = Date.now() - startTime;
Expand Down
8 changes: 7 additions & 1 deletion packages/idb-cache/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,10 @@ export function generateUUIDFromHash(hashHex: string): string {
* @param itemKey - The item key.
* @returns A deterministic UUID string.
*/
export async function deterministicUUID(key: string): Promise<string> {
export async function deterministicUUID(
key: string,
priority = "normal"
): Promise<string> {
if (uuidCache.has(key)) {
const uuid = uuidCache.get(key);
if (typeof uuid === "string") {
Expand All @@ -61,6 +64,9 @@ export async function deterministicUUID(key: string): Promise<string> {

const encoder = new TextEncoder();
const data = encoder.encode(key);
if (priority === "low") {
await waitForAnimationFrame();
}
// Usually under 1 ms; not outsourced to worker
const hashBuffer = await crypto.subtle.digest("SHA-512", data);
const hashHex = bufferToHex(hashBuffer);
Expand Down

0 comments on commit e4ac35c

Please sign in to comment.