Skip to content

Commit

Permalink
wasm-benchmarks: Fix potential collision of sql in recordings (#4689)
Browse files Browse the repository at this point in the history
* Fix JSON serialization of bigints

* wasm-benchmarks: apply lateral joins when possible, and return rel fields in results  (#4691)

* Store recordings if requested and use joins

* More traversal and serialization

* Create recordings for all the different engines as they might be using different SQL queries.

* Remove stale comment
  • Loading branch information
Miguel Fernández authored Feb 13, 2024
1 parent 9878210 commit 346196f
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 30 deletions.
38 changes: 33 additions & 5 deletions query-engine/driver-adapters/executor/bench/queries.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[
{
"description": "movies.findMany() (all - 25000)",
"description": "movies.findMany() (all - ~50K)",
"query": {
"action": "findMany",
"modelName": "Movie",
Expand Down Expand Up @@ -59,7 +59,12 @@
"cast": true
},
"selection": {
"$scalars": true
"$scalars": true,
"cast": {
"selection": {
"$scalars": true
}
}
}
}
}
Expand All @@ -82,7 +87,12 @@
"cast": true
},
"selection": {
"$scalars": true
"$scalars": true,
"cast": {
"selection": {
"$scalars": true
}
}
}
}
}
Expand All @@ -104,7 +114,16 @@
}
},
"selection": {
"$scalars": true
"$scalars": true,
"cast": {
"selection": {
"person": {
"selection": {
"$scalars": true
}
}
}
}
}
}
}
Expand All @@ -131,7 +150,16 @@
}
},
"selection": {
"$scalars": true
"$scalars": true,
"cast": {
"selection": {
"person": {
"selection": {
"$scalars": true
}
}
}
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion query-engine/driver-adapters/executor/bench/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ datasource db {

generator foo {
provider = "prisma-client-js"
previewFeatures = ["driverAdapters"]
previewFeatures = ["driverAdapters", "relationJoins"]
}

model Movie {
Expand Down
47 changes: 34 additions & 13 deletions query-engine/driver-adapters/executor/src/bench.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,18 @@ async function main(): Promise<void> {
const withErrorCapturing = bindAdapter(pg);

// We build two decorators for recording and replaying db queries.
const { recorder, replayer } = recording(withErrorCapturing);
const { recorder, replayer, recordings } = recording(withErrorCapturing);

// We exercise the queries recording them
await recordQueries(recorder, datamodel, prismaQueries);

// Dump recordings if requested
if (process.env.BENCH_RECORDINGS_FILE != null) {
const recordingsJson = JSON.stringify(recordings.data(), null, 2);
await fs.writeFile(process.env.BENCH_RECORDINGS_FILE, recordingsJson);
debug(`Recordings written to ${process.env.BENCH_RECORDINGS_FILE}`);
}

// Then we benchmark the execution of the queries but instead of hitting the DB
// we fetch results from the recordings, thus isolating the performance
// of the engine + driver adapter code from that of the DB IO.
Expand All @@ -61,23 +68,37 @@ async function recordQueries(
datamodel: string,
prismaQueries: any
): Promise<void> {
const qe = await initQeWasmBaseLine(adapter, datamodel);
await qe.connect("");
// Different engines might have made different SQL queries to complete the same Prisma Query,
// so we record the results of all engines for the benchmarking phase.
const napi = await initQeNapiCurrent(adapter, datamodel);
await napi.connect("");
const wasmCurrent = await initQeWasmCurrent(adapter, datamodel);
await wasmCurrent.connect("");
const wasmBaseline = await initQeWasmBaseLine(adapter, datamodel);
await wasmBaseline.connect("");
const wasmLatest = await initQeWasmLatest(adapter, datamodel);
await wasmLatest.connect("");

try {
for (const prismaQuery of prismaQueries) {
const { description, query } = prismaQuery;
const res = await qe.query(JSON.stringify(query), "", undefined);

const errors = JSON.parse(res).errors;
if (errors != null && errors.length > 0) {
throw new Error(
`Query failed for ${description}: ${JSON.stringify(res)}`
);
for (const qe of [napi, wasmCurrent, wasmBaseline, wasmLatest]) {
for (const prismaQuery of prismaQueries) {
const { description, query } = prismaQuery;
const res = await qe.query(JSON.stringify(query), "", undefined);
console.log(res[9]);

const errors = JSON.parse(res).errors;
if (errors != null) {
throw new Error(
`Query failed for ${description}: ${JSON.stringify(res)}`
);
}
}
}
} finally {
await qe.disconnect("");
await napi.disconnect("");
await wasmCurrent.disconnect("");
await wasmBaseline.disconnect("");
await wasmLatest.disconnect("");
}
}

Expand Down
28 changes: 17 additions & 11 deletions query-engine/driver-adapters/executor/src/recording.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export function recording(adapter: DriverAdapter) {
return {
recorder: recorder(adapter, recordings),
replayer: replayer(adapter, recordings),
recordings: recordings,
};
}

Expand All @@ -31,9 +32,7 @@ function recorder(adapter: DriverAdapter, recordings: Recordings) {
return result;
},
executeRaw: async (params) => {
const result = await adapter.executeRaw(params);
recordings.addCommandResults(params, result);
return result;
throw new Error("Not implemented");
},
};
}
Expand Down Expand Up @@ -61,18 +60,25 @@ function createInMemoryRecordings() {
const queryResults: Map<string, Result<ResultSet>> = new Map();
const commandResults: Map<string, Result<number>> = new Map();

// Recording is currently only used in benchmarks. Before we used to serialize the whole query
// (sql + args) but since bigints are not serialized by JSON.stringify, and we didn’t really need
// (sql + args) but since bigints are not serialized by JSON.stringify, and we didn't really need
// args for benchmarks, we just serialize the sql part.
//
// If this ever changes (we reuse query recording in tests) we need to make sure to serialize the
// args as well.
const queryToKey = (params: Query) => {
return JSON.stringify(params.sql);
var sql = params.sql;
params.args.forEach((arg: any, i) => {
sql = sql.replace("$" + (i + 1), arg.toString());
});
return sql;
};

return {
data: (): Map<string, ResultSet> => {
const map = new Map();
for (const [key, value] of queryResults.entries()) {
value.map((resultSet) => {
map[key] = resultSet;
});
}
return map;
},

addQueryResults: (params: Query, result: Result<ResultSet>) => {
const key = queryToKey(params);
queryResults.set(key, result);
Expand Down

0 comments on commit 346196f

Please sign in to comment.