Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
5 changes: 5 additions & 0 deletions .changeset/friendly-queens-invite.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@powersync/node': patch
---

[`node:sqlite`] Prevent `database is locked` errors when instantiating the database.
16 changes: 9 additions & 7 deletions packages/node/src/db/WorkerConnectionPool.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
import * as Comlink from 'comlink';
import fs from 'node:fs/promises';
import * as path from 'node:path';
import { Worker } from 'node:worker_threads';
import * as Comlink from 'comlink';

import {
BaseObserver,
BatchedUpdateNotification,
DBAdapter,
DBAdapterListener,
LockContext,
Transaction,
DBLockOptions,
QueryResult
LockContext,
QueryResult,
Transaction
} from '@powersync/common';
import { Remote } from 'comlink';
import { AsyncResource } from 'node:async_hooks';
import { isBundledToCommonJs } from '../utils/modules.js';
import { AsyncDatabase, AsyncDatabaseOpener } from './AsyncDatabase.js';
import { RemoteConnection } from './RemoteConnection.js';
import { NodeDatabaseImplementation, NodeSQLOpenOptions } from './options.js';
import { isBundledToCommonJs } from '../utils/modules.js';

export type BetterSQLite3LockContext = LockContext & {
executeBatch(query: string, params?: any[][]): Promise<QueryResult>;
Expand Down Expand Up @@ -135,10 +135,12 @@ export class WorkerConnectionPool extends BaseObserver<DBAdapterListener> implem
if (this.options.initializeConnection) {
await this.options.initializeConnection(connection, isWriter);
}

await connection.execute('pragma journal_mode = WAL');
if (!isWriter) {
await connection.execute('pragma query_only = true');
} else {
// We only need to enable this on the writer connection.
// We can get `database is locked` errors if we enable this on concurrently opening read connections.
await connection.execute('pragma journal_mode = WAL');
}

return connection;
Expand Down
26 changes: 23 additions & 3 deletions packages/node/tests/PowerSyncDatabase.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import * as path from 'node:path';
import { Worker } from 'node:worker_threads';

import { vi, expect, test } from 'vitest';
import { AppSchema, databaseTest, tempDirectoryTest } from './utils';
import { LockContext } from '@powersync/common';
import { randomUUID } from 'node:crypto';
import { expect, test, vi } from 'vitest';
import { CrudEntry, CrudTransaction, PowerSyncDatabase } from '../lib';
import { WorkerOpener } from '../lib/db/options';
import { LockContext } from '@powersync/common';
import { AppSchema, databaseTest, tempDirectoryTest } from './utils';

test('validates options', async () => {
await expect(async () => {
Expand Down Expand Up @@ -203,3 +204,22 @@ databaseTest('getCrudTransactions', async ({ database }) => {
const remainingTransaction = await database.getNextCrudTransaction();
expect(remainingTransaction?.crud).toHaveLength(15);
});

tempDirectoryTest('should not present database is locked errors on startup', async ({ tmpdir }) => {
for (let i = 0; i < 10; i++) {
const database = new PowerSyncDatabase({
schema: AppSchema,
database: {
dbFilename: `${randomUUID()}.sqlite`,
dbLocation: tmpdir,
implementation: {
type: 'node:sqlite'
}
}
});

// This should not throw
await database.waitForReady();
await database.close();
}
});
23 changes: 23 additions & 0 deletions pnpm-workspace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,26 @@ packages:
- packages/*
- tools/*
- docs/

ignoredBuiltDependencies:
- '@journeyapps/wa-sqlite'
- '@parcel/watcher'
- '@swc/core'
- core-js
- core-js-pure
- detox
- dtrace-provider
- electron
- electron-winstaller
- esbuild
- lmdb
- lzma-native
- msgpackr-extract
- react-native-elements
- supabase
- unrs-resolver
- vue-demi

onlyBuiltDependencies:
- better-sqlite3
- better-sqlite3-multiple-ciphers
Loading