Skip to content
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

feat: add benchmark for graphql-tools-mocking #5

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions benchmark-result-linux-with-server.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
| Command | Mean [s] | Min [s] | Max [s] | Relative |
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know these generated files will cause issues with linting, but I'm not sure if we want to commit them or not.
On one hand it will be easier to reference/point to the data, on the other it is generated and thus not needed to be part of the repo.

|:---|---:|---:|---:|---:|
| `ts-node -T graphql-tools-mocking/benchmark.ts graphql-tools-mocking.graphql` | 2.735 ± 0.071 | 2.657 | 2.890 | 1.00 |
| `ts-node -T graphql-tools-mocking/benchmark.ts ts-deco-fe.federated.graphql` | 3.785 ± 0.052 | 3.708 | 3.888 | 1.38 ± 0.04 |
| `ts-node -T graphql-tools-mocking/benchmark.ts voyager-api.federated.graphql` | 5.238 ± 0.081 | 5.134 | 5.392 | 1.92 ± 0.06 |
5 changes: 5 additions & 0 deletions benchmark-result-linux-without-server.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
| Command | Mean [s] | Min [s] | Max [s] | Relative |
|:---|---:|---:|---:|---:|
| `ts-node -T graphql-tools-mocking/benchmark.ts graphql-tools-mocking.graphql --no-server` | 2.645 ± 0.037 | 2.596 | 2.701 | 1.00 |
| `ts-node -T graphql-tools-mocking/benchmark.ts ts-deco-fe.federated.graphql --no-server` | 3.651 ± 0.060 | 3.584 | 3.749 | 1.38 ± 0.03 |
| `ts-node -T graphql-tools-mocking/benchmark.ts voyager-api.federated.graphql --no-server` | 5.238 ± 0.070 | 5.131 | 5.358 | 1.98 ± 0.04 |
5 changes: 5 additions & 0 deletions benchmark-result-m1-with-server.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
| Command | Mean [s] | Min [s] | Max [s] | Relative |
|:---|---:|---:|---:|---:|
| `ts-node -T graphql-tools-mocking/benchmark.ts graphql-tools-mocking.graphql` | 1.493 ± 0.068 | 1.443 | 1.672 | 1.00 |
| `ts-node -T graphql-tools-mocking/benchmark.ts ts-deco-fe.federated.graphql` | 2.200 ± 0.082 | 2.127 | 2.356 | 1.47 ± 0.09 |
| `ts-node -T graphql-tools-mocking/benchmark.ts voyager-api.federated.graphql` | 2.813 ± 0.062 | 2.750 | 2.949 | 1.88 ± 0.09 |
5 changes: 5 additions & 0 deletions benchmark-result-m1-without-server.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
| Command | Mean [s] | Min [s] | Max [s] | Relative |
|:---|---:|---:|---:|---:|
| `ts-node -T graphql-tools-mocking/benchmark.ts graphql-tools-mocking.graphql --no-server` | 1.522 ± 0.067 | 1.445 | 1.646 | 1.00 |
| `ts-node -T graphql-tools-mocking/benchmark.ts ts-deco-fe.federated.graphql --no-server` | 2.101 ± 0.031 | 2.067 | 2.160 | 1.38 ± 0.06 |
| `ts-node -T graphql-tools-mocking/benchmark.ts voyager-api.federated.graphql --no-server` | 2.773 ± 0.024 | 2.739 | 2.819 | 1.82 ± 0.08 |
32 changes: 32 additions & 0 deletions experiments/graphql-tools-mocking.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,35 @@ You should be able to see the result of the query that was executed directly.
This demo also includes a running server at http://0.0.0.0:4000/graphql which
is running GraphiQL. This allows you to explore the original schema while
querying with the same fake data.

## Benchmark

Install hyperfine with brew or similar:

```sh
$ brew install hyperfine
```

Copy the schemas that you want to test against to `schemas/` folder.

Run the tests in your environment and export it to MD file like:

```sh
$ hyperfine --warmup 3 -r 10 \
'ts-node -T graphql-tools-mocking/benchmark.ts graphql-tools-mocking.graphql' \
'ts-node -T graphql-tools-mocking/benchmark.ts <api-1>.graphql' \
'ts-node -T graphql-tools-mocking/benchmark.ts <api-2>.graphql' \
--export-markdown benchmark-result-<architecture>-<with-server/without-server>.md
Comment on lines +38 to +42
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What exactly are we measuring? Is it time to boot + mock 1 query?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, in this case it is warm boot tho. The point here was not to measure the performance of mocking 1 query, but test the performance impact of big schemas and, as they grow, will this cause problems for our development productivity.

```

If you want to test without booting the GQL server - currently using
[Yoga](https://www.the-guild.dev/graphql/yoga-server) - use the `--no-server`
option:

```sh
$ hyperfine --warmup 3 -r 10 \
'ts-node -T graphql-tools-mocking/benchmark.ts graphql-tools-mocking.graphql --no-server' \
'ts-node -T graphql-tools-mocking/benchmark.ts <api-1>.graphql --no-server' \
'ts-node -T graphql-tools-mocking/benchmark.ts <api-2>.graphql --no-server' \
--export-markdown benchmark-result-<architecture>-<with-server/without-server>.md
```
112 changes: 112 additions & 0 deletions graphql-tools-mocking/benchmark.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { makeExecutableSchema } from '@graphql-tools/schema';
import { addMocksToSchema } from '@graphql-tools/mock';
import { graphql } from 'graphql';
import { faker } from '@faker-js/faker';
import { createServer } from '@graphql-yoga/node';
import fs from 'fs';
import path from 'path';
import sanitize from 'sanitize-filename';

const options = { withServer: true };
const inputs: string[] = [];
process.argv.slice(2).forEach((arg) => {
switch (arg) {
case '--no-server':
options.withServer = false;
break;
default:
inputs.push(arg);
}
});

const providedSchemaPath = inputs[0];

const providedSchema = fs.readFileSync(
path.join('schemas', sanitize(providedSchemaPath)),
'utf-8'
);
const extendedSchema = fs
.readFileSync(path.join('schemas', 'graphql-tools-mocking.graphql'), 'utf-8')
// extend query since other schema above has it already defined
.replace('type Query {', 'extend type Query {');

const schemaString = `
${providedSchema}
${extendedSchema}
`;

// Make a GraphQL schema with no resolvers
const schema = makeExecutableSchema({ typeDefs: schemaString });

// Create a new schema with mocks
const schemaWithMocks = addMocksToSchema({
schema,
mocks: {
Date: () => new Date().toISOString(),

BookAuthor: () => {
const firstName = faker.name.firstName();
const lastName = faker.name.lastName();
return {
firstName,
lastName,
fullName: faker.name.fullName({ firstName, lastName }),
};
},

Query: {
books: () => {
// always load only 1 for consistent benchmark results
return [...new Array(1)];
},
},
},
});

if (options.withServer) {
// benchmark with optional GQL server
const server = createServer({
schema: schemaWithMocks,
});

server.start().then(() => {
// stop the server after we start for clean exit of the process
server.stop();
});
}

const query = /* GraphQL */ `
query Book {
book(id: 6) {
id
description
date
author {
id
firstName
lastName
fullName
}
}

# this limit input is ignored by the mocker
books(limit: 1) {
id
description
date
author {
id
firstName
lastName
fullName
}
}
}
`;

graphql({
schema: schemaWithMocks,
source: query,
}).then((result) => {
console.log('Got result %o', result);
});
53 changes: 26 additions & 27 deletions graphql-tools-mocking/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,16 @@ import { addMocksToSchema } from '@graphql-tools/mock';
import { graphql } from 'graphql';
import { faker } from '@faker-js/faker';
import { createServer } from '@graphql-yoga/node';
import fs from 'node:fs';
import path from 'node:path';

// Fill this in with the schema string
// This can be based on introspection too:
// https://www.graphql-tools.com/docs/mocking#mocking-a-schema-using-introspection
const schemaString = `
scalar Date

type Book {
id: ID!
description: String
date: Date
author: BookAuthor
}

type BookAuthor {
id: ID!
firstName: String
lastName: String
fullName: String
}

type Query {
book(id: ID!): Book
books(limit: Int, skip: Int, sort_field: String, sort_order: String): [Book]
}

type Mutation {
createBook(body: String): Book
deleteBook(id: ID!): Book
}
`;
const schemaString = fs.readFileSync(
path.join('schemas', 'graphql-tools-mocking.graphql'),
'utf-8'
);

// Make a GraphQL schema with no resolvers
const schema = makeExecutableSchema({ typeDefs: schemaString });
Expand All @@ -55,6 +34,13 @@ const schemaWithMocks = addMocksToSchema({
// bla: 'example',
};
},

Query: {
books: () => {
// we can't get the input args from the query 😞
return [...new Array(faker.datatype.number({ min: 2, max: 6 }))];
},
},
},
});

Expand All @@ -78,6 +64,19 @@ const query = /* GraphQL */ `
fullName
}
}

# this limit input is ignored by the mocker
books(limit: 1) {
id
description
date
author {
id
firstName
lastName
fullName
}
}
}
`;

Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@
"graphql": "^16.5.0",
"graphql-faker": "^2.0.0-rc.25",
"graphql-mocks": "^0.8.4",
"prettier": "^2.7.1"
"prettier": "^2.7.1",
"sanitize-filename": "^1.6.3"
},
"dependencies": {
"typescript": "^4.8.2"
Expand Down
25 changes: 25 additions & 0 deletions schemas/graphql-tools-mocking.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
scalar Date

type Book {
id: ID!
description: String
date: Date
author: BookAuthor
}

type BookAuthor {
id: ID!
firstName: String
lastName: String
fullName: String
}

type Query {
book(id: ID!): Book
books(limit: Int, skip: Int, sort_field: String, sort_order: String): [Book]
}

type Mutation {
createBook(body: String): Book
deleteBook(id: ID!): Book
}
19 changes: 19 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2975,6 +2975,13 @@ [email protected]:
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==

sanitize-filename@^1.6.3:
version "1.6.3"
resolved "https://registry.yarnpkg.com/sanitize-filename/-/sanitize-filename-1.6.3.tgz#755ebd752045931977e30b2025d340d7c9090378"
integrity sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==
dependencies:
truncate-utf8-bytes "^1.0.0"

[email protected]:
version "3.0.5"
resolved "https://registry.yarnpkg.com/seedrandom/-/seedrandom-3.0.5.tgz#54edc85c95222525b0c7a6f6b3543d8e0b3aa0a7"
Expand Down Expand Up @@ -3268,6 +3275,13 @@ tr46@~0.0.3:
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==

truncate-utf8-bytes@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz#405923909592d56f78a5818434b0b78489ca5f2b"
integrity sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==
dependencies:
utf8-byte-length "^1.0.1"

ts-node@^10.5.0:
version "10.9.1"
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b"
Expand Down Expand Up @@ -3358,6 +3372,11 @@ update-browserslist-db@^1.0.5:
escalade "^3.1.1"
picocolors "^1.0.0"

utf8-byte-length@^1.0.1:
version "1.0.4"
resolved "https://registry.yarnpkg.com/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz#f45f150c4c66eee968186505ab93fcbb8ad6bf61"
integrity sha512-4+wkEYLBbWxqTahEsWrhxepcoVOJ+1z5PGIjPZxRkytcdSUaNjIjBM7Xn8E+pdSuV7SzvWovBFA54FO0JSoqhA==

[email protected]:
version "1.0.1"
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
Expand Down