Skip to content

Instantly share code, notes, and snippets.

@ritalin
Last active September 2, 2024 03:54
Show Gist options
  • Save ritalin/4fcffc8ffdf5fcfe31dc94ab853fe917 to your computer and use it in GitHub Desktop.
Save ritalin/4fcffc8ffdf5fcfe31dc94ab853fe917 to your computer and use it in GitHub Desktop.
IndexedDBでWASM版Duckdbをキャッシュ
import * as duckdb from '@duckdb/duckdb-wasm'
import { AsyncDuckDB, type DuckDBBundle } from '@duckdb/duckdb-wasm'
export const initAsyncDb = async (): Promise<AsyncDuckDB> => {
const JSDELIVR_BUNDLES = duckdb.getJsDelivrBundles();
const bundle = await duckdb.selectBundle(JSDELIVR_BUNDLES);
const cache = await openDatabase("cache.duckdb", 3, bundle)
return readFromCache(cache, bundle)
}
type CacheEntry = {
key: string,
wasm: Blob,
worker: Blob,
}
const openDatabase = (name: string, version: number, resource: DuckDBBundle): Promise<IDBDatabase> => {
return new Promise((resolve: ((instance: IDBDatabase) => void), reject: ((reaqson?: any) => void)) => {
const req_create = indexedDB.open(name, version)
req_create.onerror = (ev: Event) => {
console.error(`Can not open cache database (${ev})`)
reject(ev)
}
req_create.onupgradeneeded = async (ev: IDBVersionChangeEvent) => {
console.log("(Re)creating database...")
const db = req_create.result
if (db.objectStoreNames.contains("cache")) {
db.deleteObjectStore("cache");
}
const storage = db.createObjectStore("cache", {keyPath: 'key'})
const res_wasm = await fetch(resource.mainModule)
const buf_wasm = await res_wasm.blob()
const res_worker = await fetch(resource.mainWorker!)
const buf_worker = await res_worker.blob()
const transaction = db.transaction("cache", "readwrite")
const store = transaction.objectStore("cache")
store.add({key: "wasm", wasm: buf_wasm, worker: buf_worker})
transaction.oncomplete = (ev: Event) => {
console.log('Add cache entry successfully');
}
transaction.onerror = (ev) => {
console.error('Error adding cache entry:', (ev.target as IDBRequest).error);
reject(ev)
};
}
req_create.onsuccess = (_) => {
resolve(req_create.result)
}
})
}
const readFromCache = (cache: IDBDatabase, resource: DuckDBBundle): Promise<AsyncDuckDB> => {
return new Promise((resolve: ((instance: AsyncDuckDB) => void), reject: ((reaqson?: any) => void)) => {
const storage = cache.transaction("cache", "readonly").objectStore("cache")
const req_get = storage.get("wasm")
req_get.onerror = (ev: Event) => {
console.error(`Can not get cache entry... (${ev})`)
reject(ev)
}
req_get.onsuccess = async (ev: Event) => {
const req = ev.target as IDBRequest
const entry = req.result as CacheEntry
const wasm_url = URL.createObjectURL(entry.wasm)
const worker_url = URL.createObjectURL(entry.worker);
const logger = import.meta.env.PROD ? new duckdb.VoidLogger(): new duckdb.ConsoleLogger()
const worker = new Worker(worker_url!)
const instance = new AsyncDuckDB(logger, worker)
await instance.instantiate(wasm_url, resource.pthreadWorker)
URL.revokeObjectURL(url_worker)
URL.revokeObjectURL(url_wasm)
resolve(instance)
}
})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment