-
Notifications
You must be signed in to change notification settings - Fork 94
fix the database connection is closing issue. #748
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
Changes from 3 commits
2946a6f
8aa7ec6
d5bb9b6
e824ef7
f351319
f02de7b
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 | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,26 +1,45 @@ | ||||||||||||||||||||||
| import * as IDB from 'idb-keyval'; | ||||||||||||||||||||||
| import type {UseStore} from 'idb-keyval'; | ||||||||||||||||||||||
| import {logInfo} from '../../../Logger'; | ||||||||||||||||||||||
| import {logAlert, logInfo} from '../../../Logger'; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // This is a copy of the createStore function from idb-keyval, we need a custom implementation | ||||||||||||||||||||||
| // because we need to create the database manually in order to ensure that the store exists before we use it. | ||||||||||||||||||||||
| // If the store does not exist, idb-keyval will throw an error | ||||||||||||||||||||||
| // source: https://github.com/jakearchibald/idb-keyval/blob/9d19315b4a83897df1e0193dccdc29f78466a0f3/src/index.ts#L12 | ||||||||||||||||||||||
| function createStore(dbName: string, storeName: string): UseStore { | ||||||||||||||||||||||
| let dbp: Promise<IDBDatabase> | undefined; | ||||||||||||||||||||||
| let closedBy: 'browser' | 'versionchange' | 'verifyStoreExists' | 'unknown' = 'unknown'; | ||||||||||||||||||||||
|
Krishna2323 marked this conversation as resolved.
Outdated
|
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| const attachHandlers = (db: IDBDatabase) => { | ||||||||||||||||||||||
| // It seems like Safari sometimes likes to just close the connection. | ||||||||||||||||||||||
| // It's supposed to fire this event when that happens. Let's hope it does! | ||||||||||||||||||||||
| // eslint-disable-next-line no-param-reassign | ||||||||||||||||||||||
| db.onclose = () => { | ||||||||||||||||||||||
|
Contributor
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. Coul we improve the comment here to be more explanatory? An external link about it is also appreciated
Contributor
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. Done. Please check. |
||||||||||||||||||||||
| logInfo('IDB connection closed by browser', {dbName, storeName}); | ||||||||||||||||||||||
| closedBy = 'browser'; | ||||||||||||||||||||||
| dbp = undefined; | ||||||||||||||||||||||
| }; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // When another tab triggers a DB version upgrade, we must close the connection | ||||||||||||||||||||||
| // to unblock the upgrade; otherwise the other tab's open request hangs indefinitely. | ||||||||||||||||||||||
| // https://developer.mozilla.org/en-US/docs/Web/API/IDBDatabase/versionchange_event | ||||||||||||||||||||||
| // eslint-disable-next-line no-param-reassign | ||||||||||||||||||||||
| db.onversionchange = () => { | ||||||||||||||||||||||
| logInfo('IDB connection closing due to versionchange', {dbName, storeName}); | ||||||||||||||||||||||
|
Krishna2323 marked this conversation as resolved.
Outdated
|
||||||||||||||||||||||
| closedBy = 'versionchange'; | ||||||||||||||||||||||
| db.close(); | ||||||||||||||||||||||
| dbp = undefined; | ||||||||||||||||||||||
| }; | ||||||||||||||||||||||
| }; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| const getDB = () => { | ||||||||||||||||||||||
| if (dbp) return dbp; | ||||||||||||||||||||||
| const request = indexedDB.open(dbName); | ||||||||||||||||||||||
| request.onupgradeneeded = () => request.result.createObjectStore(storeName); | ||||||||||||||||||||||
| dbp = IDB.promisifyRequest(request); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| dbp.then( | ||||||||||||||||||||||
| (db) => { | ||||||||||||||||||||||
| // It seems like Safari sometimes likes to just close the connection. | ||||||||||||||||||||||
| // It's supposed to fire this event when that happens. Let's hope it does! | ||||||||||||||||||||||
| // eslint-disable-next-line no-param-reassign | ||||||||||||||||||||||
| db.onclose = () => (dbp = undefined); | ||||||||||||||||||||||
| }, | ||||||||||||||||||||||
| attachHandlers, | ||||||||||||||||||||||
| // eslint-disable-next-line @typescript-eslint/no-empty-function | ||||||||||||||||||||||
| () => {}, | ||||||||||||||||||||||
| ); | ||||||||||||||||||||||
|
|
@@ -36,6 +55,7 @@ function createStore(dbName: string, storeName: string): UseStore { | |||||||||||||||||||||
|
|
||||||||||||||||||||||
| logInfo(`Store ${storeName} does not exist in database ${dbName}.`); | ||||||||||||||||||||||
| const nextVersion = db.version + 1; | ||||||||||||||||||||||
| closedBy = 'verifyStoreExists'; | ||||||||||||||||||||||
| db.close(); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| const request = indexedDB.open(dbName, nextVersion); | ||||||||||||||||||||||
|
|
@@ -50,13 +70,34 @@ function createStore(dbName: string, storeName: string): UseStore { | |||||||||||||||||||||
| }; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| dbp = IDB.promisifyRequest(request); | ||||||||||||||||||||||
| // eslint-disable-next-line @typescript-eslint/no-empty-function | ||||||||||||||||||||||
| dbp.then(attachHandlers, () => {}); | ||||||||||||||||||||||
| return dbp; | ||||||||||||||||||||||
| }; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| return (txMode, callback) => | ||||||||||||||||||||||
| getDB() | ||||||||||||||||||||||
| function executeTransaction<T>(txMode: IDBTransactionMode, callback: (store: IDBObjectStore) => T | PromiseLike<T>): Promise<T> { | ||||||||||||||||||||||
| return getDB() | ||||||||||||||||||||||
| .then(verifyStoreExists) | ||||||||||||||||||||||
| .then((db) => callback(db.transaction(storeName, txMode).objectStore(storeName))); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| return (txMode, callback) => | ||||||||||||||||||||||
| executeTransaction(txMode, callback).catch((error) => { | ||||||||||||||||||||||
| if (error instanceof DOMException && error.name === 'InvalidStateError') { | ||||||||||||||||||||||
|
Contributor
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. We should have a comment explaining why we need this check
Contributor
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. Done. Please check.
Comment on lines
+81
to
+85
Contributor
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.
Suggested change
|
||||||||||||||||||||||
| logAlert('IDB InvalidStateError, retrying with fresh connection', { | ||||||||||||||||||||||
| dbName, | ||||||||||||||||||||||
| storeName, | ||||||||||||||||||||||
| txMode, | ||||||||||||||||||||||
| closedBy, | ||||||||||||||||||||||
| errorMessage: error.message, | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
| dbp = undefined; | ||||||||||||||||||||||
| closedBy = 'unknown'; | ||||||||||||||||||||||
| // Retry only once — this call is not wrapped, so if it also fails the error propagates normally. | ||||||||||||||||||||||
| return executeTransaction(txMode, callback); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| throw error; | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| export default createStore; | ||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.