-
Notifications
You must be signed in to change notification settings - Fork 59
fix: Potential Multitab Transaction Issues With IndexedDB #775
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
Merged
Merged
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
21385c6
fix: improve OPFS multi-tab connection recovery
stevensJourney e6960da
wip: indexeddb holds
stevensJourney 1e03046
fix proxy
stevensJourney 07d5420
add changeset
stevensJourney ffeec7e
fix tests
stevensJourney 2717b7a
Code cleanup
stevensJourney 4740853
Add unit tests and autocommit check for tests
stevensJourney 5c6d92d
improve connection closing
stevensJourney 1ddf6be
Update changelog
stevensJourney 4804531
Code cleanup
stevensJourney df07223
maybe make node sync tests more stable
stevensJourney 8fc41a8
conditionally use holds
stevensJourney File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| --- | ||
| '@powersync/web': patch | ||
| --- | ||
|
|
||
| - Fixed an issue where IndexedDB could cause "cannot start a transaction within a transaction" errors. | ||
| - Improved reconnect logic when multiple tabs are closed. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,131 @@ | ||
| import { ILogger } from '@powersync/common'; | ||
| import { | ||
| AsyncDatabaseConnection, | ||
| OnTableChangeCallback, | ||
| ProxiedQueryResult | ||
| } from '../../db/adapters/AsyncDatabaseConnection'; | ||
| import { ResolvedWebSQLOpenOptions } from '../../db/adapters/web-sql-flags'; | ||
|
|
||
| /** | ||
| * Keeps track of open DB connections and the clients which | ||
| * are using it. | ||
| */ | ||
| export type SharedDBWorkerConnection = { | ||
| clientIds: Set<number>; | ||
| db: AsyncDatabaseConnection; | ||
| }; | ||
|
|
||
| export type SharedWASQLiteConnectionOptions = { | ||
| dbMap: Map<string, SharedDBWorkerConnection>; | ||
| dbFilename: string; | ||
| clientId: number; | ||
| logger: ILogger; | ||
| }; | ||
|
|
||
| export class SharedWASQLiteConnection implements AsyncDatabaseConnection { | ||
| protected isClosing: boolean; | ||
| // Keeps track if this current hold if the shared connection has a hold | ||
| protected activeHoldId: string | null; | ||
|
|
||
| constructor(protected options: SharedWASQLiteConnectionOptions) { | ||
| // Add this client ID to the set of known clients | ||
| this.clientIds.add(options.clientId); | ||
| this.isClosing = false; | ||
| this.activeHoldId = null; | ||
| } | ||
|
|
||
| protected get logger() { | ||
| return this.options.logger; | ||
| } | ||
|
|
||
| protected get dbEntry() { | ||
| return this.options.dbMap.get(this.options.dbFilename)!; | ||
| } | ||
|
|
||
| protected get connection() { | ||
| return this.dbEntry.db; | ||
| } | ||
|
|
||
| protected get clientIds() { | ||
| return this.dbEntry.clientIds; | ||
| } | ||
|
|
||
| async init(): Promise<void> { | ||
| // No-op since the connection is already initialized when it was created | ||
| } | ||
|
|
||
| async markHold(): Promise<string> { | ||
| this.activeHoldId = await this.connection.markHold(); | ||
| return this.activeHoldId; | ||
| } | ||
|
|
||
| async releaseHold(id: string): Promise<void> { | ||
| try { | ||
| await this.connection.releaseHold(id); | ||
| } finally { | ||
| this.activeHoldId = null; | ||
| } | ||
| } | ||
|
|
||
| async isAutoCommit(): Promise<boolean> { | ||
| return this.connection.isAutoCommit(); | ||
| } | ||
|
|
||
| /** | ||
| * Handles closing of a shared connection. | ||
| * The connection is only closed if there are no active clients using it. | ||
| */ | ||
| async close(): Promise<void> { | ||
| // This prevents further statements on this connection from being executed | ||
| this.isClosing = true; | ||
| const { clientIds, logger } = this; | ||
| const { clientId, dbFilename, dbMap } = this.options; | ||
| logger.debug(`Close requested from client ${clientId} of ${[...clientIds]}`); | ||
| clientIds.delete(clientId); | ||
|
|
||
| if (this.activeHoldId) { | ||
| // We can't cleanup here since we're not in a lock context. | ||
| // The cleanup will occur once a new hold is acquired. | ||
| this.logger.info( | ||
| `Hold ${this.activeHoldId} was still active when the connection was closed. Cleanup will occur once a new hold is acquired.` | ||
| ); | ||
| } | ||
|
|
||
| if (clientIds.size == 0) { | ||
| logger.debug(`Closing connection to ${this.options}.`); | ||
| const connection = this.connection; | ||
| dbMap.delete(dbFilename); | ||
| await connection.close(); | ||
| return; | ||
| } | ||
| logger.debug(`Connection to ${dbFilename} not closed yet due to active clients.`); | ||
| return; | ||
| } | ||
|
|
||
| protected async withClosing<T>(action: () => Promise<T>) { | ||
| if (this.isClosing) { | ||
| throw new Error('Connection is closing'); | ||
| } | ||
| return action(); | ||
| } | ||
|
|
||
| async execute(sql: string, params?: any[]): Promise<ProxiedQueryResult> { | ||
| return this.withClosing(() => this.connection.execute(sql, params)); | ||
| } | ||
|
|
||
| async executeRaw(sql: string, params?: any[]): Promise<any[][]> { | ||
| return this.withClosing(() => this.connection.executeRaw(sql, params)); | ||
| } | ||
|
|
||
| executeBatch(sql: string, params?: any[] | undefined): Promise<ProxiedQueryResult> { | ||
| return this.withClosing(() => this.connection.executeBatch(sql, params)); | ||
| } | ||
|
|
||
| registerOnTableChange(callback: OnTableChangeCallback): Promise<() => void> { | ||
| return this.connection.registerOnTableChange(callback); | ||
| } | ||
|
|
||
| getConfig(): Promise<ResolvedWebSQLOpenOptions> { | ||
| return this.connection.getConfig(); | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.