Skip to content

Commit 373fc72

Browse files
committed
fix(benchmark): refactor benchmark runs to include warmup pass and improve iteration calibration
1 parent a93cb5d commit 373fc72

2 files changed

Lines changed: 168 additions & 319 deletions

File tree

benchmark/drivers.ts

Lines changed: 10 additions & 199 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
1-
import { promisify } from "node:util";
21
import { DatabaseSync } from "../src/index";
32

43
// Optional dependencies - loaded lazily to allow running tests without them
54
// Use any types to avoid TypeScript issues with optional deps
65
let Database: any = null;
7-
let deasync: any = null;
8-
let sqlite3Module: any = null;
96

107
// Try to load optional dependencies
118
try {
@@ -14,33 +11,20 @@ try {
1411
// better-sqlite3 not available
1512
}
1613

17-
try {
18-
deasync = require("deasync");
19-
} catch {
20-
// deasync not available
21-
}
22-
23-
try {
24-
sqlite3Module = require("sqlite3");
25-
} catch {
26-
// sqlite3 not available
27-
}
28-
2914
// Track if node:sqlite is available
3015
let nodeSqliteAvailable = false;
3116
let NodeSqliteDatabase: any = null;
3217

33-
// Initialize node:sqlite availability asynchronously
34-
(async () => {
35-
try {
36-
// Try to import node:sqlite
37-
const nodeSqlite = await import("node:sqlite");
38-
NodeSqliteDatabase = nodeSqlite.DatabaseSync;
39-
nodeSqliteAvailable = true;
40-
} catch (e) {
41-
// node:sqlite not available
42-
}
43-
})();
18+
try {
19+
// Use require() for synchronous loading so the driver is available
20+
// when the `drivers` map is built at module-load time.
21+
// eslint-disable-next-line @typescript-eslint/no-require-imports
22+
const nodeSqlite = require("node:sqlite");
23+
NodeSqliteDatabase = nodeSqlite.DatabaseSync;
24+
nodeSqliteAvailable = true;
25+
} catch {
26+
// node:sqlite not available
27+
}
4428

4529
// Types
4630
export interface Statement {
@@ -236,178 +220,6 @@ class NodeSqliteDriver extends BaseDriver {
236220
}
237221
}
238222

239-
// sqlite3 driver (async, needs wrapper)
240-
class Sqlite3Driver extends BaseDriver {
241-
declare protected db: any;
242-
private stmtCache = new Map<string, any>();
243-
private dbRun!: (sql: string, ...params: any[]) => Promise<void>;
244-
private dbGet!: (sql: string, ...params: any[]) => Promise<any>;
245-
private dbAll!: (sql: string, ...params: any[]) => Promise<any[]>;
246-
private dbExec!: (sql: string) => Promise<void>;
247-
248-
constructor() {
249-
super("sqlite3");
250-
}
251-
252-
async initialize(filename: string): Promise<Driver> {
253-
if (!sqlite3Module || !deasync) {
254-
throw new Error(
255-
"sqlite3 and deasync are not available - run 'npm install' in benchmark/",
256-
);
257-
}
258-
const Database = sqlite3Module.verbose().Database;
259-
this.db = new Database(filename);
260-
261-
// Promisify common methods
262-
this.dbRun = promisify(this.db.run.bind(this.db));
263-
this.dbGet = promisify(this.db.get.bind(this.db));
264-
this.dbAll = promisify(this.db.all.bind(this.db));
265-
this.dbExec = promisify(this.db.exec.bind(this.db));
266-
267-
return this;
268-
}
269-
270-
async close(): Promise<void> {
271-
if (this.db) {
272-
// Clean up cached statements
273-
for (const stmt of this.stmtCache.values()) {
274-
await new Promise<void>((resolve) => stmt.finalize(() => resolve()));
275-
}
276-
this.stmtCache.clear();
277-
278-
await new Promise<void>((resolve, reject) => {
279-
this.db.close((err: any) => {
280-
if (err) reject(err);
281-
else resolve();
282-
});
283-
});
284-
this.db = null as any;
285-
}
286-
}
287-
288-
prepare(sql: string): Statement {
289-
// For sqlite3, we'll create a wrapper that makes async operations look sync
290-
// This is not ideal for performance but allows fair comparison
291-
292-
// Cache prepared statements
293-
let stmt = this.stmtCache.get(sql);
294-
if (!stmt) {
295-
stmt = this.db.prepare(sql);
296-
this.stmtCache.set(sql, stmt);
297-
}
298-
299-
return {
300-
get: (...params) => {
301-
// Run synchronously using deasync
302-
let result: any;
303-
let error: Error | null = null;
304-
let done = false;
305-
306-
stmt!.get(...params, (err: Error | null, row: any) => {
307-
error = err;
308-
result = row;
309-
done = true;
310-
});
311-
312-
// Busy wait using deasync
313-
deasync!.loopWhile(() => !done);
314-
315-
if (error) throw error;
316-
return result!;
317-
},
318-
319-
all: (...params) => {
320-
let result: any[];
321-
let error: Error | null = null;
322-
let done = false;
323-
324-
stmt!.all(...params, (err: Error | null, rows: any[]) => {
325-
error = err;
326-
result = rows;
327-
done = true;
328-
});
329-
330-
deasync!.loopWhile(() => !done);
331-
332-
if (error) throw error;
333-
return result!;
334-
},
335-
336-
run: (...params) => {
337-
let result: any;
338-
let error: Error | null = null;
339-
let done = false;
340-
341-
stmt!.run(...params, function (this: any, err: Error | null) {
342-
error = err;
343-
result = { changes: this.changes, lastInsertRowid: this.lastID };
344-
done = true;
345-
});
346-
347-
deasync!.loopWhile(() => !done);
348-
349-
if (error) throw error;
350-
return result!;
351-
},
352-
353-
iterate: function* (...params) {
354-
// sqlite3 doesn't have built-in iterator, simulate with all()
355-
let rows: any[];
356-
let error: Error | null = null;
357-
let done = false;
358-
359-
stmt!.all(...params, (err: Error | null, allRows: any[]) => {
360-
error = err;
361-
rows = allRows;
362-
done = true;
363-
});
364-
365-
deasync!.loopWhile(() => !done);
366-
367-
if (error) throw error;
368-
369-
for (const row of rows!) {
370-
yield row;
371-
}
372-
},
373-
374-
finalize: () => {
375-
// Don't finalize cached statements
376-
},
377-
};
378-
}
379-
380-
transaction<T>(fn: (...args: any[]) => T): (...args: any[]) => T {
381-
// sqlite3 doesn't have built-in transaction support, simulate it
382-
const self = this;
383-
return (...args: any[]) => {
384-
self.exec("BEGIN");
385-
try {
386-
const result = fn(...args);
387-
self.exec("COMMIT");
388-
return result;
389-
} catch (err) {
390-
self.exec("ROLLBACK");
391-
throw err;
392-
}
393-
};
394-
}
395-
396-
exec(sql: string): void {
397-
let error: Error | null = null;
398-
let done = false;
399-
400-
this.db.exec(sql, (err: any) => {
401-
error = err;
402-
done = true;
403-
});
404-
405-
deasync!.loopWhile(() => !done);
406-
407-
if (error) throw error;
408-
}
409-
}
410-
411223
// Driver class map
412224
type DriverConstructor = new () => BaseDriver;
413225

@@ -416,7 +228,6 @@ export const drivers: Record<string, DriverConstructor> = {
416228
"@photostructure/sqlite": PhotostructureDriver,
417229
...(Database ? { "better-sqlite3": BetterSqlite3Driver } : {}),
418230
...(nodeSqliteAvailable ? { "node:sqlite": NodeSqliteDriver } : {}),
419-
...(sqlite3Module && deasync ? { sqlite3: Sqlite3Driver } : {}),
420231
};
421232

422233
// Helper to create driver instance

0 commit comments

Comments
 (0)