-
-
Notifications
You must be signed in to change notification settings - Fork 0
chore: address PR #414/#415 review feedback #417
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 all commits
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,52 +1,68 @@ | ||
| import '../unit/vscode_mock_setup'; | ||
| import { createNativeDatabaseConnection } from '../../src/nativeWorker'; | ||
| import type { ModificationEntry } from '../../src/core/types'; | ||
| import * as vscode from 'vscode'; | ||
| import * as path from 'path'; | ||
| import * as fs from 'fs/promises'; | ||
|
|
||
| async function runBenchmark() { | ||
| const bundle = await createNativeDatabaseConnection(vscode.Uri.file(process.cwd())); | ||
| const loadResult = await (bundle as any).loadDatabase({ buffer: new Uint8Array() }); | ||
| const db = loadResult.databaseOps; | ||
|
|
||
| // create table | ||
| await db.createTable('test_table', [ | ||
| { name: 'id', type: 'INTEGER', primaryKey: true }, | ||
| ...Array.from({ length: 50 }, (_, i) => ({ name: `col_${i}`, type: 'TEXT' })) | ||
| ]); | ||
|
|
||
| // insert row | ||
| await db.insertRow('test_table', { | ||
| id: 1, | ||
| ...Object.fromEntries(Array.from({ length: 50 }, (_, i) => [`col_${i}`, `val_${i}`])) | ||
| }); | ||
|
|
||
| const mod = { | ||
| modificationType: 'column_drop' as const, | ||
| targetTable: 'test_table', | ||
| deletedColumns: Array.from({ length: 50 }, (_, i) => ({ | ||
| name: `col_${i}`, | ||
| type: 'TEXT', | ||
| data: [{ rowId: 1, value: `val_${i}` }] | ||
| })) | ||
| }; | ||
|
|
||
| console.log('Warming up...'); | ||
|
|
||
| // warmup | ||
| await db.undoModification(mod); | ||
| await db.redoModification(mod); | ||
|
|
||
| console.log('Running benchmark...'); | ||
|
|
||
| const start = performance.now(); | ||
| for (let i = 0; i < 5; i++) { | ||
| // The native API opens a file URI, so the benchmark uses a unique local database and removes it after disposal. | ||
| const databasePath = path.join(process.cwd(), `native_worker_ddl_batch_${process.pid}_${Date.now()}.sqlite`); | ||
|
|
||
| try { | ||
| const { databaseOps: db } = await bundle.establishConnection( | ||
| vscode.Uri.file(databasePath), | ||
| path.basename(databasePath) | ||
| ); | ||
|
|
||
| // create table | ||
| await db.createTable('test_table', [ | ||
| { name: 'id', type: 'INTEGER', primaryKey: true, notNull: false }, | ||
| ...Array.from({ length: 50 }, (_, i) => ({ | ||
| name: `col_${i}`, | ||
| type: 'TEXT', | ||
| primaryKey: false, | ||
| notNull: false | ||
| })) | ||
| ]); | ||
|
|
||
| // insert row | ||
| await db.insertRow('test_table', { | ||
| id: 1, | ||
| ...Object.fromEntries(Array.from({ length: 50 }, (_, i) => [`col_${i}`, `val_${i}`])) | ||
| }); | ||
|
|
||
| const mod = { | ||
| description: 'Drop benchmark columns', | ||
| modificationType: 'column_drop' as const, | ||
| targetTable: 'test_table', | ||
| deletedColumns: Array.from({ length: 50 }, (_, i) => ({ | ||
| name: `col_${i}`, | ||
| type: 'TEXT', | ||
| data: [{ rowId: 1, value: `val_${i}` }] | ||
| })) | ||
| } satisfies ModificationEntry; | ||
|
|
||
| console.log('Warming up...'); | ||
|
|
||
| // warmup | ||
| await db.undoModification(mod); | ||
| await db.redoModification(mod); | ||
| } | ||
| const end = performance.now(); | ||
| console.log(`Time taken: ${(end - start).toFixed(2)}ms`); | ||
|
|
||
| bundle.workerMethods[Symbol.dispose](); | ||
| console.log('Running benchmark...'); | ||
|
|
||
| const start = performance.now(); | ||
| for (let i = 0; i < 5; i++) { | ||
| await db.undoModification(mod); | ||
| await db.redoModification(mod); | ||
| } | ||
| const end = performance.now(); | ||
| console.log(`Time taken: ${(end - start).toFixed(2)}ms`); | ||
| } finally { | ||
| bundle.workerMethods[Symbol.dispose](); | ||
| await fs.rm(databasePath, { force: true }); | ||
| } | ||
| } | ||
|
|
||
| runBenchmark().catch(console.error); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -84,11 +84,19 @@ describe('workerFactory error path tests', () => { | |
| workerTerminated = false; | ||
|
|
||
| // Reset VSCode mock behaviors | ||
| mockVscode.workspace.fs = { | ||
| readFile: async () => new Uint8Array(), | ||
| stat: async () => ({ size: 0 }) | ||
| } as any; | ||
| (mockVscode.Uri as any).joinPath = () => ({ scheme: 'file', fsPath: '/test/path/assets/sqlite3.wasm' }); | ||
| Object.defineProperty(mockVscode.workspace, 'fs', { | ||
| value: { | ||
| readFile: async () => new Uint8Array(), | ||
| stat: async () => ({ size: 0 }) | ||
| }, | ||
| writable: true, | ||
| configurable: true | ||
| }); | ||
| Object.defineProperty(mockVscode.Uri, 'joinPath', { | ||
| value: () => ({ scheme: 'file', fsPath: '/test/path/assets/sqlite3.wasm' }), | ||
| writable: true, | ||
| configurable: true | ||
| }); | ||
|
Comment on lines
+87
to
+99
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. Modifying shared mock objects like Since To prevent this, we should capture the original properties before modifying them and restore them in let originalFs: any;
let originalJoinPath: any;
beforeEach(() => {
connectionFailed = false;
workerTerminated = false;
originalFs = mockVscode.workspace.fs;
originalJoinPath = mockVscode.Uri.joinPath;
// Reset VSCode mock behaviors
Object.defineProperty(mockVscode.workspace, 'fs', {
value: {
readFile: async () => new Uint8Array(),
stat: async () => ({ size: 0 })
},
writable: true,
configurable: true
});
Object.defineProperty(mockVscode.Uri, 'joinPath', {
value: () => ({ scheme: 'file', fsPath: '/test/path/assets/sqlite3.wasm' }),
writable: true,
configurable: true
});
});
afterEach(() => {
// Restore original mock behaviors
if (originalFs) {
Object.defineProperty(mockVscode.workspace, 'fs', {
value: originalFs,
writable: true,
configurable: true
});
}
if (originalJoinPath) {
Object.defineProperty(mockVscode.Uri, 'joinPath', {
value: originalJoinPath,
writable: true,
configurable: true
});
}
// Restore the original require implementation to avoid leaking to other tests
Module.prototype.require = originalRequire;
}); |
||
| }); | ||
|
|
||
| afterEach(() => { | ||
|
|
||
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.
There is a potential race condition when deleting the database file immediately after calling
bundle.workerMethods[Symbol.dispose]().The
Symbol.disposecall triggersworker.stop(), which asynchronously kills the child process (this.process.kill()). Since process termination and file descriptor release by the OS are asynchronous,fs.rmmight execute while the file is still locked by the terminating process (especially on Windows). This can cause the benchmark to fail withEBUSYorEPERMerrors.To make this robust, we can implement a simple retry loop with a small delay to ensure the file is successfully deleted once the lock is released.