Skip to content

nhrones/BuenoCache-JS

Repository files navigation

BuenoCache

A LocalFirst example application

  • Pure vanilla HTML, CSS, javascript application - no frameworks
  • Local async IDB persistence service
  • Zero dependencies -- no Node, no Deno, no Bun, no Typescript, None, Nada
  • Zero network requirements, statically served from Github Pages alt text

100K user objects

Instantaneous ordering, filtering, pagination

BuenoCache is an extremely performant in-memory data service.
This example serves a collection of 100k user-objects persisted in a local IndexedDB.

  • Note: this cache service will work with almost any persitence layer. I've used localStorage, IndexedDB, SQLite, and DenoKv. To insure a consistent cache, all DB-mutations must be wrapped in a transaction.

alt text

Click the link below to run the above demo app:

  • This will run the app from Github Pages.
  • The app will first build then hydrate an IndexedDB -> workDB -> ObjectStore
  • Any mutation of the bueno-cache will be consistently persisted to this ObjectStore

About this Proof Of Concept demo

  • All data is persisted and hydrated as a single key-value record in IndexedDB.
  • The IndexedDB is managed by a worker thread. See: ./js/idbWorker.js
  • Data hydrates to an es6-Map (bueno-cache) using JSON.parse()
  • The bueno-cache data is persisted in IndexedDB using JSON.stringyfy()
  • Any mutation to bueno-cache triggers a flush of the full dataset to IndexedDB.
  • You'll notice a very resposive UI, as IDB ops are on a worker thread.
  • I've tested with 5 million records with no IDB or UI issues.

This example app demonstrates full CRUD of the 100k user objects:

/** a `User` object ...*/
User = {
    id: number,     // index
    first: string,  // ~ 6 char 
    last: string,   // ~ 6 char 
    age: number     // 10 - 70
} 

/**
 * Hydrate from the IndexedDB worker 
 * one hundred thousand `stringyfied` User objects
 * @ param hundredK =~ 6 million chars - 7.6 MB
 */
worker.onmessage(hundredK) =>
   buenoCache = new Map([...JSON.parse(hundredK)])

// Persist to the IndexedDB worker
worker postMessage(id, value = JSON.stringify([...buenoCache.entries()]))

Observed performance

alt text

You can appreciate the performance of this persisted cache, by deleting the IndexedDB dataset while the app is running.
On the next mutation operation of bueno-cache, the app will reconstruct the IndexedDB.
This is imperceptible to the UX, as this is mostly off-UI-thread.

If you once again delete the IndexedDB, and then refresh the page, you'll see a
creation message on restart. It will take < 250ms to recreate and persist
a new set of (100,000 user objects, ~ 7.6 MB in IDB).

With the app running:

Open dev-tools
Open the Application tab
Select the storage/indexedDB -> workDB -> ObjectStore
You'll see one or more keys Users-xxxx (xxxx = number of user objects)
You may then right-click on a key and select delete to remove it.
This will not impact the running app. Any Create, Update, or Delete op,
will force a flush of the buenoCache to the IndexedDB record.
A flush takes < 100ms for 100k user objects, most of this time is in the worker.

Note: Table cells are editable.
Any cell-blur, forces a mutation of the buenoCache. This mutation then forces a DB flush. Note that the id cell (primary key) is not editable.

Whenever you select a cell in a row, the row and cell will be highlighted, and a delete button (X) will show at the botton on the table.
You can click this button to delete the selected row.

About the UI

The table headers allow ordering and filtering on each column.
Please note the performance of the es6-Map-based cache.
Ordering and filtering are always applied to the full 100,000 user records.
Because all cache mutations are immediately flushed to IndexedDB, the buenoCache remains consistant.
Have fun! I learned quite a bit building this.

What I've learned:

The thing that impressed me the most, is how incredibly fast V8-JSON is!
I was also impressed with how well es6-Maps work as a database cache.
I've tried many different data transfer methods between the ui-thread and the worker, and was surprised that transfering / persisting a single json string is extremely efficient.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published