tinyplex / tinybase

The reactive data store for local‑first apps.
https://tinybase.org
MIT License
3.23k stars 65 forks source link

Create of IndexedDB persister is slow when database doesn't exist #123

Closed fspoettel closed 5 months ago

fspoettel commented 6 months ago

Describe the bug

I'm running the issue that initializing the IndexedDB persister is very slow, while it's fast on subsequent loads:

init_indexed_db_persister: 1858ms - timer ended
starting initial sync...
initial sync finished.
---
init_indexed_db_persister: 16ms - timer ended
store is initialized, skipping.

I thought this was IndexedDB being slow, but I then switched to pre-creating the database with the same layout tinybase expects and the whole thing is then very fast consistently in both cases:

ensure_database: 8ms - timer ended
init_indexed_db_persister: 20ms - timer ended
starting initial sync...
initial sync finished.
---
pre-creating database
ensure_database: 6ms - timer ended
init_indexed_db_persister: 24ms - timer ended
store is initialized, skipping

The init function does this:

async function initPersister(persister) {
      if (persister) {
        console.time("init_indexed_db_persister");
        // console.log("pre-creating database");
        // console.time("ensure_database");
        // await ensureDatabase("card-data");
        // console.timeEnd("ensure_database")
        await persister.startAutoLoad();
        await persister.startAutoSave();
        setStoreInitialized(true);
        console.timeEnd("init_indexed_db_persister");
      }
    }

The ensureDatabase function does this:

export function ensureDatabase(name: string) {
  return new Promise((resolve, reject) => {
    const request = window.indexedDB.open(name, 2);

    request.onerror = (event) => reject("Database error: " + event.target.error);
    request.onsuccess = (event) => resolve(event.target.result);

    request.onupgradeneeded = (event) => {
      const db = event.target.result;
      db.createObjectStore("t", { keyPath: "k" });
      db.createObjectStore("v", { keyPath: "k" });
      resolve(db);
    };
  });
}

The slowdown seems to be caused by awaitingstartAutoSave, it might be something related to the scheduling logic, I'm not sure though.

Your Example Website or App

No response

Steps to Reproduce the Bug or Issue

Create an indexedDBPersister and time how long it takes until startAutoSave resolves.

Expected behavior

No response

Screenshots or Videos

No response

Platform

Additional context

No response

jamesgpearce commented 6 months ago

Thanks! I'll take a look at this.

jamesgpearce commented 6 months ago

Hm, on both Chrome and Firefox the autosave never takes more than 10ms, regardless of whether the database is there or not beforehand.

The autoLoad seems to be the problem.

jamesgpearce commented 6 months ago

OK, just documenting the analysis here. All with no database present:

console.time('save');
persister.save().then(() => {
  console.timeEnd('save'); // fast
});
console.time('load');
persister.load().then(() => {
  console.timeEnd('load'); // fast
});

BUT...

console.time('load');
persister.load().then(() => {
  console.timeEnd('load');
  console.time('save');
  persister.save().then(() => console.timeEnd('save')); // slow
});

and yet...

console.time('save');
persister.save().then(() => {
  console.timeEnd('save');
  console.time('load');
  persister.load().then(() => console.timeEnd('load')); // fast
});

🤔

jamesgpearce commented 6 months ago

I think I have a fix. The connection is not closed when the first load fails, so the save has to wait for it to timeout.

jamesgpearce commented 6 months ago

OK, the fix is in v4.6.1. Wanna try it out?

jamesgpearce commented 5 months ago

Assuming it's all good, closing out.

fspoettel commented 5 months ago

hey sorry, forgot to answer. yes, it's fixed on my end as well. thanks! :)