Skip to content

Commit 4b7a7cf

Browse files
authored
Duckdb Benchmarking app (#25)
* Add parquet file and base setup * Benchmarking queries * Adding memory DBM * Fix PR checks * Remove the E2E app
1 parent cfc042b commit 4b7a7cf

31 files changed

+3090
-1
lines changed

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.parquet filter=lfs diff=lfs merge=lfs -text
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"presets": [
3+
[
4+
"@nx/react/babel",
5+
{
6+
"runtime": "automatic"
7+
}
8+
]
9+
],
10+
"plugins": []
11+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"extends": ["plugin:@nx/react", "../../.eslintrc.json"],
3+
"ignorePatterns": ["!**/*"],
4+
"overrides": [
5+
{
6+
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
7+
"rules": {}
8+
},
9+
{
10+
"files": ["*.ts", "*.tsx"],
11+
"rules": {}
12+
},
13+
{
14+
"files": ["*.js", "*.jsx"],
15+
"rules": {}
16+
}
17+
]
18+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/* eslint-disable */
2+
export default {
3+
displayName: 'benchmarking-app',
4+
preset: '../../jest.preset.js',
5+
transform: {
6+
'^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': '@nx/react/plugins/jest',
7+
'^.+\\.[tj]sx?$': ['babel-jest', { presets: ['@nx/react/babel'] }],
8+
},
9+
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
10+
coverageDirectory: '../../coverage/benchmarking/benchmarking-app',
11+
};
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
{
2+
"name": "benchmarking-app",
3+
"$schema": "../../node_modules/nx/schemas/project-schema.json",
4+
"sourceRoot": "benchmarking/benchmarking-app/src",
5+
"projectType": "application",
6+
"targets": {
7+
"build": {
8+
"executor": "@nx/webpack:webpack",
9+
"outputs": ["{options.outputPath}"],
10+
"defaultConfiguration": "production",
11+
"options": {
12+
"compiler": "babel",
13+
"outputPath": "dist/benchmarking/benchmarking-app",
14+
"index": "benchmarking/benchmarking-app/src/index.html",
15+
"baseHref": "/",
16+
"main": "benchmarking/benchmarking-app/src/main.tsx",
17+
"tsConfig": "benchmarking/benchmarking-app/tsconfig.app.json",
18+
"assets": [
19+
"benchmarking/benchmarking-app/src/favicon.ico",
20+
"benchmarking/benchmarking-app/src/assets"
21+
],
22+
"styles": ["benchmarking/benchmarking-app/src/styles.css"],
23+
"scripts": [],
24+
"isolatedConfig": true,
25+
"webpackConfig": "benchmarking/benchmarking-app/webpack.config.js"
26+
},
27+
"configurations": {
28+
"development": {
29+
"extractLicenses": false,
30+
"optimization": false,
31+
"sourceMap": true,
32+
"vendorChunk": true
33+
},
34+
"production": {
35+
"fileReplacements": [
36+
{
37+
"replace": "benchmarking/benchmarking-app/src/environments/environment.ts",
38+
"with": "benchmarking/benchmarking-app/src/environments/environment.prod.ts"
39+
}
40+
],
41+
"optimization": true,
42+
"outputHashing": "all",
43+
"sourceMap": false,
44+
"namedChunks": false,
45+
"extractLicenses": true,
46+
"vendorChunk": false
47+
}
48+
}
49+
},
50+
"serve": {
51+
"executor": "@nx/webpack:dev-server",
52+
"defaultConfiguration": "development",
53+
"options": {
54+
"buildTarget": "benchmarking-app:build",
55+
"hmr": true
56+
},
57+
"configurations": {
58+
"development": {
59+
"buildTarget": "benchmarking-app:build:development"
60+
},
61+
"production": {
62+
"buildTarget": "benchmarking-app:build:production",
63+
"hmr": false
64+
}
65+
}
66+
},
67+
"lint": {
68+
"executor": "@nx/linter:eslint",
69+
"outputs": ["{options.outputFile}"],
70+
"options": {
71+
"lintFilePatterns": [
72+
"benchmarking/benchmarking-app/**/*.{ts,tsx,js,jsx}"
73+
]
74+
}
75+
},
76+
"serve-static": {
77+
"executor": "@nx/web:file-server",
78+
"options": {
79+
"buildTarget": "benchmarking-app:build"
80+
}
81+
},
82+
"test": {
83+
"executor": "@nx/jest:jest",
84+
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
85+
"options": {
86+
"jestConfig": "benchmarking/benchmarking-app/jest.config.ts",
87+
"passWithNoTests": true
88+
},
89+
"configurations": {
90+
"ci": {
91+
"ci": true,
92+
"codeCoverage": true
93+
}
94+
}
95+
}
96+
},
97+
"tags": []
98+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/* Your styles goes here. */
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { Route, BrowserRouter as Router, Routes } from 'react-router-dom';
2+
import { MemoryDBMProvider } from './dbm-context/memory-dbm-context';
3+
import { FileLoader } from './file-loader/file-loader';
4+
import { QueryBenchmarking } from './query-benchmarking/query-benchmarking';
5+
6+
export function App() {
7+
return (
8+
<Router>
9+
<Routes>
10+
<Route
11+
path="/memory-dbm"
12+
element={
13+
<div>
14+
<MemoryDBMProvider>
15+
<FileLoader>
16+
<QueryBenchmarking />
17+
</FileLoader>
18+
</MemoryDBMProvider>
19+
</div>
20+
}
21+
/>
22+
</Routes>
23+
</Router>
24+
);
25+
}
26+
27+
export default App;
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { DBM, FileManagerType, MemoryDBFileManager } from '@devrev/meerkat-dbm';
2+
import React, { useState } from 'react';
3+
import { DBMContext } from '../hooks/dbm-context';
4+
import { useClassicEffect } from '../hooks/use-classic-effect';
5+
import { useAsyncDuckDB } from './use-async-duckdb';
6+
7+
export const MemoryDBMProvider = ({ children }: { children: JSX.Element }) => {
8+
const fileManagerRef = React.useRef<FileManagerType | null>(null);
9+
const [dbm, setdbm] = useState<DBM | null>(null);
10+
11+
const dbState = useAsyncDuckDB();
12+
13+
useClassicEffect(() => {
14+
if (!dbState) {
15+
return;
16+
}
17+
fileManagerRef.current = new MemoryDBFileManager({
18+
db: dbState,
19+
fetchTableFileBuffers: async (table) => {
20+
return [];
21+
},
22+
});
23+
const dbm = new DBM({
24+
db: dbState,
25+
fileManager: fileManagerRef.current,
26+
});
27+
setdbm(dbm);
28+
}, [dbState]);
29+
30+
if (!dbm || !fileManagerRef.current) {
31+
return <div>Loading...</div>;
32+
}
33+
34+
return (
35+
<DBMContext.Provider
36+
value={{
37+
dbm,
38+
fileManager: fileManagerRef.current,
39+
}}
40+
>
41+
{children}
42+
</DBMContext.Provider>
43+
);
44+
};
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { DBM, FileManagerType } from '@devrev/meerkat-dbm';
2+
import * as duckdb from '@duckdb/duckdb-wasm';
3+
import { AsyncDuckDB } from '@duckdb/duckdb-wasm';
4+
import React, { useState } from 'react';
5+
import { useClassicEffect } from '../hooks/use-classic-effect';
6+
const JSDELIVR_BUNDLES = duckdb.getJsDelivrBundles();
7+
8+
export const DBMContext = React.createContext<{
9+
dbm: DBM;
10+
fileManager: FileManagerType;
11+
}>(null as any);
12+
13+
export const useAsyncDuckDB = () => {
14+
const [dbState, setdbState] = useState<AsyncDuckDB | null>(null);
15+
16+
useClassicEffect(() => {
17+
(async () => {
18+
const bundle = await duckdb.selectBundle(JSDELIVR_BUNDLES);
19+
20+
const worker_url = URL.createObjectURL(
21+
new Blob([`importScripts("${bundle.mainWorker!}");`], {
22+
type: 'text/javascript',
23+
})
24+
);
25+
26+
// Instantiate the asynchronus version of DuckDB-wasm
27+
const worker = new Worker(worker_url);
28+
const logger = new duckdb.ConsoleLogger();
29+
const db = new duckdb.AsyncDuckDB(logger, worker);
30+
await db.instantiate(bundle.mainModule, bundle.pthreadWorker);
31+
URL.revokeObjectURL(worker_url);
32+
setdbState(db);
33+
})();
34+
}, []);
35+
return dbState;
36+
};
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import axios from 'axios';
2+
import { useState } from 'react';
3+
import { useDBM } from '../hooks/dbm-context';
4+
import { useClassicEffect } from '../hooks/use-classic-effect';
5+
6+
export const FileLoader = ({ children }: { children: JSX.Element }) => {
7+
const { fileManager } = useDBM();
8+
const [isFileLoader, setIsFileLoader] = useState<boolean>(false);
9+
useClassicEffect(() => {
10+
(async () => {
11+
const file = await axios.get(
12+
'http://localhost:4200/assets/data-sets/fhvhv_tripdata_2023-01.parquet',
13+
{ responseType: 'arraybuffer' }
14+
);
15+
const fileBuffer = file.data;
16+
const fileBufferView = new Uint8Array(fileBuffer);
17+
18+
await fileManager.registerFileBuffer({
19+
tableName: 'taxi',
20+
fileName: 'taxi.parquet',
21+
buffer: fileBufferView,
22+
});
23+
setIsFileLoader(true);
24+
})();
25+
}, []);
26+
27+
if (!isFileLoader) {
28+
return <div>Loading file</div>;
29+
}
30+
31+
return <>{children}</>;
32+
};

0 commit comments

Comments
 (0)