-
Notifications
You must be signed in to change notification settings - Fork 2.1k
@uppy/golden-retriever: cache blob-store size to avoid O(n²) getSize scans #6293
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
354434e
1157bae
b54037e
21d151a
e612d4b
ed50d93
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -100,6 +100,16 @@ let cleanedUp = false | |
| class IndexedDBStore { | ||
| #ready: Promise<IDBDatabase> | IDBDatabase | ||
|
|
||
| /** | ||
| * In-memory size cache eliminates the O(n) cursor walk inside getSize() | ||
| * on every put(), making sequential puts O(1) instead of O(n²). | ||
| * null means "not yet initialised"; the first getSize() call populates it. | ||
| * Incremented on successful put, reset to null on delete (since we don't | ||
| * track deleted-file sizes — the next getSize() rebuilds from scratch, | ||
| * which is fast because the store shrinks after deletions). | ||
| */ | ||
| #cachedSize: number | null = null | ||
|
|
||
| opts: Required<IndexedDBStoreOptions> | ||
|
|
||
| name: string | ||
|
|
@@ -173,8 +183,13 @@ class IndexedDBStore { | |
|
|
||
| /** | ||
| * Get the total size of all stored files. | ||
| * | ||
| * Uses an in-memory cache after the first call so that sequential puts() | ||
| * are O(1) rather than O(n) per call — without it, a batch of N puts | ||
| * cursor-walks the entire store N times (O(n²) reads overall). | ||
| */ | ||
| async getSize(): Promise<number> { | ||
| if (this.#cachedSize !== null) return this.#cachedSize | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Returning Useful? React with 👍 / 👎.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @codex check if we can use this API to detect multiple instances on tab switch and then trigger a re-scan ? https://developer.mozilla.org/en-US/docs/Web/API/Broadcast_Channel_API There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The new Useful? React with 👍 / 👎. |
||
| const db = await this.#ready | ||
| const transaction = db.transaction([STORE_NAME], 'readonly') | ||
| const store = transaction.objectStore(STORE_NAME) | ||
|
|
@@ -187,6 +202,7 @@ class IndexedDBStore { | |
| size += cursor.value.data.size | ||
| cursor.continue() | ||
| } else { | ||
| this.#cachedSize = size | ||
| resolve(size) | ||
| } | ||
| } | ||
|
|
@@ -216,7 +232,15 @@ class IndexedDBStore { | |
| expires: Date.now() + this.opts.expires, | ||
| data: file.data, | ||
| }) | ||
| return waitForRequest(request) | ||
| const result = await waitForRequest<T>(request) | ||
| // Only update the cache if it is still live. If a concurrent delete() | ||
| // invalidated it (set it to null) while this put() was in flight, leave | ||
| // it null so the next getSize() does a fresh scan — resurrecting it from | ||
| // 0 here would silently drop the pre-existing total and the deletion. | ||
| if (this.#cachedSize !== null) { | ||
| this.#cachedSize += file.data.size | ||
| } | ||
| return result | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -226,7 +250,12 @@ class IndexedDBStore { | |
| const db = await this.#ready | ||
| const transaction = db.transaction([STORE_NAME], 'readwrite') | ||
| const request = transaction.objectStore(STORE_NAME).delete(this.key(fileID)) | ||
| return waitForRequest(request) | ||
| const result = await waitForRequest(request) | ||
| // We don't track the deleted file's size, so reset the cache so the | ||
| // next getSize() does a fresh scan (which will be fast after deletions | ||
| // since the store has shrunk). | ||
| this.#cachedSize = null | ||
| return result | ||
| } | ||
|
|
||
| /** | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
check if we can use this API to detect multiple instances on tab switch ? https://developer.mozilla.org/en-US/docs/Web/API/Broadcast_Channel_API