Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,18 @@ pub fn create_app() -> tauri::Builder<tauri::Wry> {
});
}

// WebKitGTK's WASM optimizing JIT tier (OMG) leaks native memory when repeatedly
// recompiling wa-sqlite's WASM module (used via IDBBatchAtomicVFS, PowerSync's OPFS
// fallback on this platform - see getPowerSyncOptions in src/db/powersync/database.ts),
// eventually OOM-killing the WebKitWebProcess. Disabling just the OMG tier (baseline
// JIT stays on) avoids it. Must be set before the webview is created, since WebKitGTK
// reads JSC_* env vars at its own init time. Remove once upstream WebKit fixes this
// (tracked at https://bugs.webkit.org/show_bug.cgi?id=319572).
#[cfg(target_os = "linux")]
{
std::env::set_var("JSC_useOMGJIT", "false");
}

builder
}

Expand Down
23 changes: 22 additions & 1 deletion src/db/powersync/database.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import { describe, expect, it } from 'bun:test'
import { WASQLiteOpenFactory } from '@powersync/web'
import { WASQLiteOpenFactory, WASQLiteVFS } from '@powersync/web'

const { getPowerSyncDatabaseConfig, getPowerSyncOptions } = await import('./database')

Expand Down Expand Up @@ -101,5 +101,26 @@ describe('getPowerSyncOptions', () => {
const factory = options.database as WASQLiteOpenFactory
expect(factory.waOptions.dbFilename).toBe('thunderbolt-sync.db')
})

it('uses OPFSCoopSyncVFS when OPFS is available (default)', () => {
const options = getPowerSyncOptions('thunderbolt-sync.db', 'safari-tauri')
const factory = options.database as WASQLiteOpenFactory
expect(factory.waOptions.vfs).toBe(WASQLiteVFS.OPFSCoopSyncVFS)
})

it('falls back to IDBBatchAtomicVFS when OPFS is unavailable', () => {
const options = getPowerSyncOptions('thunderbolt-sync.db', 'safari-tauri', false)
const factory = options.database as WASQLiteOpenFactory
expect(factory.waOptions.vfs).toBe(WASQLiteVFS.IDBBatchAtomicVFS)
})

it('keeps Tauri-safe worker wiring in the OPFS-unavailable fallback', () => {
const options = getPowerSyncOptions('thunderbolt-sync.db', 'safari-tauri', false)
const factory = options.database as WASQLiteOpenFactory
expect(factory.waOptions.worker).toBe('/@powersync/worker/WASQLiteDB.umd.js')
expect(factory.waOptions.flags?.enableMultiTabs).toBe(false)
expect('flags' in options && options.flags).toEqual({ enableMultiTabs: false })
expect(options.sync).toEqual({ worker: '/@powersync/worker/SharedSyncImplementation.umd.js' })
})
})
})
23 changes: 19 additions & 4 deletions src/db/powersync/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,16 @@ const initialSyncTimeoutMs = 10_000
*/
const initialSyncPriority = 1

/** @internal Exported for testing */
export const getPowerSyncOptions = (path: string, config: PowerSyncDatabaseConfig = getPowerSyncDatabaseConfig()) => {
/**
* @internal Exported for testing
* @param opfsAvailable - Whether OPFS (`navigator.storage.getDirectory`) is actually usable on this
* platform. Only affects the 'safari-tauri' config's VFS choice — see the comment below.
*/
export const getPowerSyncOptions = (
path: string,
config: PowerSyncDatabaseConfig = getPowerSyncDatabaseConfig(),
opfsAvailable = true,
) => {
const dbFilename = path.includes('/') ? path.split('/').pop() || 'thunderbolt.db' : path

if (config === 'default') {
Expand Down Expand Up @@ -87,12 +95,16 @@ export const getPowerSyncOptions = (path: string, config: PowerSyncDatabaseConfi
* Explicit UMD worker paths — bypasses import.meta.url which fails under tauri://.
* enableMultiTabs: false — dedicated worker, not SharedWorker (fails under tauri://).
*
* Falls back to IDBBatchAtomicVFS (IndexedDB-backed, no OPFS dependency) when OPFS itself
* is unavailable — e.g. WebKitGTK (Tauri on Linux) doesn't implement `navigator.storage.getDirectory`,
* so OPFSCoopSyncVFS's worker throws on init instead of ever reaching this Asyncify tradeoff.
*
* Docs: https://docs.powersync.com/debugging/troubleshooting#common-issues
*/
return {
database: new WASQLiteOpenFactory({
dbFilename: dbFilename,
vfs: WASQLiteVFS.OPFSCoopSyncVFS,
vfs: opfsAvailable ? WASQLiteVFS.OPFSCoopSyncVFS : WASQLiteVFS.IDBBatchAtomicVFS,
worker: '/@powersync/worker/WASQLiteDB.umd.js',
flags: { enableMultiTabs: false },
}),
Expand Down Expand Up @@ -140,7 +152,10 @@ export class PowerSyncDatabaseImpl implements DatabaseInterface {
return // Already initialized
}

const options = getPowerSyncOptions(path)
// `getDatabasePath` (called before `initialize`) already probes real OPFS availability and
// returns the ':memory:' sentinel when it's unavailable — reuse that instead of re-probing.
const opfsAvailable = path !== ':memory:'
const options = getPowerSyncOptions(path, getPowerSyncDatabaseConfig(), opfsAvailable)

// Always use ThunderboltPowerSyncDatabase with TransformableBucketStorage + encryption middleware.
// The middleware is data-driven (checks __enc: prefix), so it's a no-op when E2EE is disabled.
Expand Down
Loading