Skip to content

Commit 003667c

Browse files
authored
feat: parallel indexed dbm (#100)
* add: parallel indexedb runner * update: handler * add: working changes * chore: cleanup * update: runner indexedb * update: parallel indexeddb * update: runner * update: tests * update: indexedb runner * update: test route * fix: benchmarking test * increase: timeout * update: test
1 parent bb824e5 commit 003667c

File tree

13 files changed

+329
-15
lines changed

13 files changed

+329
-15
lines changed

benchmarking/public/runner/indexeddb-runner.html

Lines changed: 71 additions & 0 deletions
Large diffs are not rendered by default.

benchmarking/src/app/app.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Route, BrowserRouter as Router, Routes } from 'react-router-dom';
22
import { IndexedDBMProvider } from './dbm-context/indexed-dbm-context';
33
import { MemoryDBMProvider } from './dbm-context/memory-dbm-context';
4+
import { ParallelIndexedDBMProvider } from './dbm-context/parallel-indexed-dbm-context';
45
import { ParallelMemoryDBMProvider } from './dbm-context/parallel-memory-dbm-context';
56
import { RawDBMProvider } from './dbm-context/raw-dbm-context';
67
import { FileLoader } from './file-loader/file-loader';
@@ -63,6 +64,19 @@ export function App() {
6364
</div>
6465
}
6566
/>
67+
<Route
68+
path="/parallel-indexed-dbm"
69+
element={
70+
<div>
71+
<h1>Parallel Indexed DuckDB</h1>
72+
<ParallelIndexedDBMProvider>
73+
<FileLoader bufferType="uint8Array">
74+
<QueryBenchmarking />
75+
</FileLoader>
76+
</ParallelIndexedDBMProvider>
77+
</div>
78+
}
79+
/>
6680
</Routes>
6781
</Router>
6882
);
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import {
2+
DBMParallel,
3+
IFrameRunnerManager,
4+
ParallelIndexedDBFileManager,
5+
} from '@devrev/meerkat-dbm';
6+
import log from 'loglevel';
7+
import { useRef, useState } from 'react';
8+
import { DBMContext } from '../hooks/dbm-context';
9+
import { useClassicEffect } from '../hooks/use-classic-effect';
10+
import { InstanceManager } from './instance-manager';
11+
import { useAsyncDuckDB } from './use-async-duckdb';
12+
13+
export const ParallelIndexedDBMProvider = ({
14+
children,
15+
}: {
16+
children: JSX.Element;
17+
}) => {
18+
const [dbm, setdbm] = useState<DBMParallel | null>(null);
19+
const instanceManagerRef = useRef<InstanceManager>(new InstanceManager());
20+
const fileManagerRef = useRef<ParallelIndexedDBFileManager>(
21+
new ParallelIndexedDBFileManager({
22+
instanceManager: instanceManagerRef.current,
23+
fetchTableFileBuffers: async (table) => {
24+
return [];
25+
},
26+
logger: log,
27+
onEvent: (event) => {
28+
console.info(event);
29+
},
30+
})
31+
);
32+
33+
const dbState = useAsyncDuckDB();
34+
35+
useClassicEffect(() => {
36+
if (!dbState) {
37+
return;
38+
}
39+
const iframeManager = new IFrameRunnerManager({
40+
runnerURL: 'http://localhost:4200/runner/indexeddb-runner.html',
41+
origin: 'http://localhost:4200',
42+
totalRunners: 4,
43+
fetchTableFileBuffers: async (table) => {
44+
return [];
45+
},
46+
fetchPreQuery: () => {
47+
return [];
48+
},
49+
onEvent: (event) => {
50+
console.info(event);
51+
},
52+
logger: log,
53+
});
54+
55+
const dbm = new DBMParallel({
56+
instanceManager: instanceManagerRef.current,
57+
fileManager: fileManagerRef.current,
58+
onEvent: (event) => {
59+
console.info(event);
60+
},
61+
logger: log,
62+
options: {
63+
shutdownInactiveTime: 1000,
64+
},
65+
iFrameRunnerManager: iframeManager,
66+
});
67+
68+
setdbm(dbm);
69+
}, [dbState]);
70+
71+
if (!dbm || !fileManagerRef.current) {
72+
return <div>Loading...</div>;
73+
}
74+
75+
return (
76+
<DBMContext.Provider
77+
value={{
78+
dbm,
79+
fileManager: fileManagerRef.current as any,
80+
}}
81+
>
82+
{children}
83+
</DBMContext.Provider>
84+
);
85+
};

benchmarking/src/app/dbm-context/parallel-memory-dbm-context.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export const ParallelMemoryDBMProvider = ({
1515
}: {
1616
children: JSX.Element;
1717
}) => {
18-
const [dbm, setdbm] = useState<DBMParallel | null>(null);
18+
const [dbm, setdbm] = useState<DBMParallel<SharedArrayBuffer> | null>(null);
1919
const instanceManagerRef = useRef<InstanceManager>(new InstanceManager());
2020
const fileManagerRef = useRef<ParallelMemoryFileManager>(
2121
new ParallelMemoryFileManager({
@@ -37,7 +37,7 @@ export const ParallelMemoryDBMProvider = ({
3737
return;
3838
}
3939
const iframeManager = new IFrameRunnerManager({
40-
runnerURL: 'http://localhost:4200/runner/index.html',
40+
runnerURL: 'http://localhost:4200/runner/memory-runner.html',
4141
origin: 'http://localhost:4200',
4242
totalRunners: 4,
4343
fetchTableFileBuffers: async (table) => {

benchmarking/src/app/hooks/dbm-context.tsx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
import { DBM, DBMParallel, FileManagerType } from '@devrev/meerkat-dbm';
22
import React from 'react';
33

4-
export const DBMContext = React.createContext<{
5-
dbm: DBM | DBMParallel;
4+
export type DBMContextType<T> = {
5+
dbm: DBM | DBMParallel<T>;
66
fileManager: FileManagerType;
7-
}>(null as any);
7+
};
8+
9+
export const DBMContext = React.createContext<DBMContextType<any> | undefined>(
10+
undefined
11+
);
812

9-
export const useDBM = () => {
10-
const context = React.useContext(DBMContext);
13+
export const useDBM = <T,>() => {
14+
const context = React.useContext(DBMContext) as DBMContextType<T> | undefined;
1115
if (context === undefined) {
1216
throw new Error('useDBM must be used within a DBMProvider');
1317
}

meerkat-dbm/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@devrev/meerkat-dbm",
3-
"version": "0.1.1",
3+
"version": "0.1.2",
44
"dependencies": {
55
"tslib": "^2.3.0",
66
"@devrev/duckdb-wasm": "1.14.3",

meerkat-dbm/src/dbm/__test__/dbm-parallel.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ iFrameRunnerManager.iFrameManagers.set('1', runnerMock as any);
3737
iFrameRunnerManager.iFrameManagers.set('2', runnerMock as any);
3838

3939
describe('DBMParallel', () => {
40-
let dbmParallel: DBMParallel;
40+
let dbmParallel: DBMParallel<SharedArrayBuffer>;
4141
let fileManager: FileManagerType<SharedArrayBuffer>;
4242
let instanceManager: InstanceManager;
4343

meerkat-dbm/src/dbm/dbm-parallel/dbm-parallel.ts

Lines changed: 71 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,12 @@ import {
44
BROWSER_RUNNER_TYPE,
55
BrowserRunnerExecQueryMessageResponse,
66
} from '../../window-communication/runner-types';
7-
import { DBMConstructorOptions, QueryOptions, TableConfig } from '../types';
7+
import {
8+
DBMConstructorOptions,
9+
QueryOptions,
10+
TableConfig,
11+
TableLock,
12+
} from '../types';
813
import { IFrameRunnerManager } from './runner-manager';
914

1015
//Round Robin for multiple runners like 10
@@ -16,9 +21,11 @@ const roundRobin = (counter: number, maxValue: number): number => {
1621
return counter + 1;
1722
};
1823

19-
export class DBMParallel {
20-
private fileManager: FileManagerType<SharedArrayBuffer>;
24+
export class DBMParallel<BufferType = Uint8Array> {
25+
private fileManager: FileManagerType<BufferType>;
2126
private logger: DBMLogger;
27+
private tableLockRegistry: Record<string, TableLock> = {};
28+
2229
private onEvent?: (event: DBMEvent) => void;
2330
private options: DBMConstructorOptions['options'];
2431
private onDuckDBShutdown?: () => void;
@@ -35,7 +42,7 @@ export class DBMParallel {
3542
instanceManager,
3643
onDuckDBShutdown,
3744
iFrameRunnerManager,
38-
}: DBMConstructorOptions<SharedArrayBuffer> & {
45+
}: DBMConstructorOptions<BufferType> & {
3946
iFrameRunnerManager: IFrameRunnerManager;
4047
}) {
4148
this.fileManager = fileManager;
@@ -84,6 +91,66 @@ export class DBMParallel {
8491
}, this.options.shutdownInactiveTime);
8592
}
8693

94+
async lockTables(tableNames: string[]): Promise<void> {
95+
const promises = [];
96+
97+
for (const tableName of tableNames) {
98+
const tableLock = this.tableLockRegistry[tableName];
99+
100+
// If the table lock doesn't exist, create a new lock
101+
if (!tableLock) {
102+
this.tableLockRegistry[tableName] = {
103+
isLocked: true,
104+
promiseQueue: [],
105+
};
106+
continue;
107+
}
108+
109+
// If the table is already locked, add the promise to the queue
110+
if (tableLock.isLocked) {
111+
const promise = new Promise<void>((resolve, reject) => {
112+
tableLock.promiseQueue.push({ reject, resolve });
113+
});
114+
promises.push(promise);
115+
}
116+
117+
// Set the table as locked
118+
tableLock.isLocked = true;
119+
}
120+
121+
// Wait for all promises to resolve (locks to be acquired)
122+
await Promise.all(promises);
123+
}
124+
125+
async unlockTables(tableNames: string[]): Promise<void> {
126+
for (const tableName of tableNames) {
127+
const tableLock = this.tableLockRegistry[tableName];
128+
129+
// If the table lock doesn't exist, create a new lock
130+
if (!tableLock) {
131+
this.tableLockRegistry[tableName] = {
132+
isLocked: false,
133+
promiseQueue: [],
134+
};
135+
}
136+
137+
const nextPromiseInQueue = tableLock?.promiseQueue?.shift();
138+
139+
// If there is a promise in the queue, resolve it and keep the table as locked
140+
if (nextPromiseInQueue) {
141+
tableLock.isLocked = true;
142+
nextPromiseInQueue.resolve();
143+
} else {
144+
// If there are no promises in the queue, set the table as unlocked
145+
tableLock.isLocked = false;
146+
}
147+
}
148+
}
149+
150+
isTableLocked(tableName: string): boolean {
151+
return this.tableLockRegistry[tableName]?.isLocked ?? false;
152+
}
153+
87154
public async queryWithTables({
88155
query,
89156
tables,

meerkat-dbm/src/file-manager/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
export * from './file-manager-type';
22
export * from './indexed-db/indexed-db-file-manager';
3+
export * from './indexed-db/parallel-indexed-db-file-manager';
4+
export * from './indexed-db/runner-indexed-db-file-manager';
35
export * from './memory/memory-file-manager';
46
export * from './memory/parallel-memory-file-manager';
57
export * from './memory/runner-memory-file-manager';

0 commit comments

Comments
 (0)