Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
148 commits
Select commit Hold shift + click to select a range
d21a678
loadtest
enisdenjo Feb 6, 2025
bdb2bd2
no url and slope length
enisdenjo Feb 6, 2025
d5d630c
fix loadtest script
enisdenjo Feb 6, 2025
e949f0f
simplify
enisdenjo Feb 6, 2025
d038a13
internal perf
enisdenjo Feb 6, 2025
c39cfea
memtest that actually runs the test
enisdenjo Feb 6, 2025
960a92c
nicer message
enisdenjo Feb 6, 2025
a2da53f
cleanup
enisdenjo Feb 6, 2025
78f02ba
workflow
enisdenjo Feb 6, 2025
1ef464c
no pipe logs
enisdenjo Feb 6, 2025
778d0d4
install k6
enisdenjo Feb 6, 2025
14c447a
skip memtests from examples
enisdenjo Feb 6, 2025
6d4ed44
simplify bench.yml
enisdenjo Feb 6, 2025
5357169
loadtest duration minute
enisdenjo Feb 6, 2025
4216d25
allow more time for test teardown
enisdenjo Feb 6, 2025
9d90be0
stricter threshold
enisdenjo Feb 6, 2025
7777587
stop loadtest if server exits
enisdenjo Feb 6, 2025
3564de3
server exited before loadtest is fail
enisdenjo Feb 6, 2025
9744c02
container wait for exit
enisdenjo Feb 6, 2025
a37784a
debug
enisdenjo Feb 10, 2025
e8baad9
debug module
enisdenjo Feb 10, 2025
411dff7
memtest and better debug
enisdenjo Feb 10, 2025
a905bff
opentelemetry memtest
enisdenjo Feb 10, 2025
d5cba61
memory usage chart in debug mode
enisdenjo Feb 10, 2025
2437ddb
simplify chart
enisdenjo Feb 10, 2025
7581a17
slope has no mb
enisdenjo Feb 10, 2025
6ed7ed3
less slope threshold
enisdenjo Feb 10, 2025
730d74d
draw a trend
enisdenjo Feb 10, 2025
20b2220
loadtest and calmdown
enisdenjo Feb 10, 2025
d8f230d
loadtest and calmdown trends
enisdenjo Feb 10, 2025
50a66a4
idle loadtest calmdown
enisdenjo Feb 10, 2025
4688795
include idle in test timeout and increase calmdown
enisdenjo Feb 10, 2025
75a45eb
check increase and decrease
enisdenjo Feb 10, 2025
f43aaac
multiple expeects
enisdenjo Feb 10, 2025
e925074
memory decrease
enisdenjo Feb 10, 2025
67c8f50
3 min loadtest
enisdenjo Feb 10, 2025
f1f00f4
longer calmdown
enisdenjo Feb 10, 2025
791f024
increase canvas
enisdenjo Feb 10, 2025
1bb942f
ignore snapshot charts
enisdenjo Feb 10, 2025
afcba9b
memory should stop growing at one point
enisdenjo Feb 10, 2025
63e075c
signal adjustments
enisdenjo Feb 10, 2025
6939e01
loadtest already prefixed
enisdenjo Feb 10, 2025
ab5fd73
better title
enisdenjo Feb 10, 2025
a95e47c
disable bun ffor now
enisdenjo Feb 10, 2025
b85218a
more loadtest timeout
enisdenjo Feb 11, 2025
20c9769
now in snapshots
enisdenjo Feb 11, 2025
316d3d1
no file parallelism
enisdenjo Feb 11, 2025
27a802e
charting in memtest on each snap
enisdenjo Feb 11, 2025
855baeb
datapoints only if available
enisdenjo Feb 11, 2025
7ec9490
humanready starttime
enisdenjo Feb 11, 2025
1ec05bd
log is redundant, chart is live
enisdenjo Feb 11, 2025
1d74d26
gitignore actual files
enisdenjo Feb 11, 2025
06eb8fe
send signals
enisdenjo Feb 13, 2025
094c902
extra args in not in proc
enisdenjo Feb 13, 2025
e80fbc8
heap snapshots
enisdenjo Feb 13, 2025
db3b1af
connect dots in chart
enisdenjo Feb 13, 2025
9cff1ee
async writes avoiding backpressure in the message listener
enisdenjo Feb 13, 2025
d028977
incrase message buffer
enisdenjo Feb 13, 2025
49fde6a
allow heapsnapshot
enisdenjo Feb 13, 2025
9877758
begin parseheap
enisdenjo Feb 14, 2025
0e77e44
unnecessary patches and log heap objects
enisdenjo Feb 26, 2025
eaec3a0
use whatwgnode promise helpers
enisdenjo Feb 26, 2025
ec4acf9
top 5
enisdenjo Feb 26, 2025
13f3c36
gc and organise
enisdenjo Feb 26, 2025
3e520c2
call on cdp
enisdenjo Feb 27, 2025
7f15092
collect garbage before calmodn
enisdenjo Feb 27, 2025
dc9ec90
haltsnapshots even during gc
enisdenjo Feb 27, 2025
25d7fa1
memory sample, readiness and things
enisdenjo Feb 27, 2025
8dd888c
no target heapsnapshot
enisdenjo Feb 27, 2025
d7e3e35
fix chart
enisdenjo Feb 27, 2025
c242dd3
onheapsnapshot, write it to temp and in e2e if debug
enisdenjo Feb 27, 2025
e95fc2d
10 nodes
enisdenjo Feb 27, 2025
fbf0ae0
trendlines and colors
enisdenjo Feb 28, 2025
630360d
loadtest with multiple runs and easier charting
enisdenjo Feb 28, 2025
fd1f3d3
better chart
enisdenjo Feb 28, 2025
df24812
readiness is not necessary due to multiple runs
enisdenjo Feb 28, 2025
346ba7d
match slope trend bla
enisdenjo Feb 28, 2025
ad949ed
correct runs check
enisdenjo Feb 28, 2025
d749a7d
less trend and stuff
enisdenjo Feb 28, 2025
04b73c6
no async
enisdenjo Mar 1, 2025
fa49d05
throw if inspector closed
enisdenjo Mar 4, 2025
03a17cd
heap sampling and stability improvements to inspector
enisdenjo Mar 4, 2025
97502d2
use heap sampling and optional heap snaps
enisdenjo Mar 4, 2025
25eff0e
memory-usage
enisdenjo Mar 4, 2025
9a40428
write heapprofile if debug only
enisdenjo Mar 4, 2025
5827944
doesnt have to be immediate message
enisdenjo Mar 4, 2025
0c8160f
perform heap sampling in memtests and stuff
enisdenjo Mar 4, 2025
09f4d85
docs and stuff
enisdenjo Mar 4, 2025
f7ae870
default to 3 runs
enisdenjo Mar 4, 2025
3a0eb6c
todo
enisdenjo Mar 4, 2025
ebe6c0c
correct docs
enisdenjo Mar 4, 2025
562e26b
heapsample everything yeah
enisdenjo Mar 4, 2025
e482e61
loadtest MUST succeed
enisdenjo Mar 4, 2025
07be5a9
off message in defer
enisdenjo Mar 5, 2025
d97f25f
patched speedscope and start analysing profile
enisdenjo Mar 5, 2025
287c5ee
bump speedscope
enisdenjo Mar 6, 2025
c51ed2b
analyse frames and report
enisdenjo Mar 6, 2025
82c86a9
improvements and docs
enisdenjo Mar 6, 2025
1e8fb3f
correctly named func
enisdenjo Mar 6, 2025
6ae6a16
adjust callstack and decrease threshold of heavy frames
enisdenjo Mar 6, 2025
e41a0d6
adaptable threshold
enisdenjo Mar 6, 2025
98184ee
memtest
enisdenjo Mar 6, 2025
d0f35e3
fail if memory snapshotting fails and use heapprofiler
enisdenjo Mar 7, 2025
52844de
local regression calc and expected value
enisdenjo Mar 7, 2025
36515cd
dont report stats if signal aborted
enisdenjo Mar 7, 2025
34c0f7e
fix gitignore after rebase
enisdenjo Mar 7, 2025
7c02618
run tests in parallel
enisdenjo Mar 7, 2025
1930e8a
disable otel plugin for e2e in memtests avoiding a false positive
enisdenjo Mar 7, 2025
d5b80e0
todo
enisdenjo Mar 7, 2025
12d8ee0
no build on ubuntu arm during release
enisdenjo Mar 7, 2025
1a30875
comment
enisdenjo Mar 10, 2025
2cdafa2
write profile to disk on fail
enisdenjo Mar 10, 2025
3426733
nestjs
enisdenjo Mar 10, 2025
3b5badd
expected heavy frames
enisdenjo Mar 10, 2025
3b99cba
auto-type-merging
enisdenjo Mar 10, 2025
2e06d45
loadtest.out on debug
enisdenjo Mar 10, 2025
b90ccb5
30sec each
enisdenjo Mar 10, 2025
84635e1
todo
enisdenjo Mar 10, 2025
878487f
nestjs is not tested because needs build
enisdenjo Mar 10, 2025
fb6c2bf
reduce threshold
enisdenjo Mar 11, 2025
de4b3be
increase mesage wait timeout
enisdenjo Mar 11, 2025
6b1d4d5
no need to skip anything
enisdenjo Mar 11, 2025
328eb73
heapsnapshot also during idle
enisdenjo Mar 11, 2025
2e8a1b3
federation mixed
enisdenjo Mar 12, 2025
afc332c
federation-subscription-passthrough
enisdenjo Mar 13, 2025
ec36023
lockfile update
enisdenjo Mar 13, 2025
69f472f
ignore loadtest output files
enisdenjo Mar 13, 2025
1cf9fd6
tostring body once in loadtestscript
enisdenjo Mar 13, 2025
98a6f99
noauth in subscriptions passthrough when memtest
enisdenjo Mar 13, 2025
d6d81a4
make sure queries work before loadtests
enisdenjo Mar 13, 2025
2257521
display trend value
enisdenjo Mar 13, 2025
7a97d66
publicurl
enisdenjo Mar 13, 2025
082ae54
more tests
enisdenjo Mar 13, 2025
e7b93c4
comment about node
enisdenjo Mar 13, 2025
875134d
no string debug undefined
enisdenjo Mar 13, 2025
e73c74d
typo
enisdenjo Mar 13, 2025
36d5b3c
no forever loop
enisdenjo Mar 13, 2025
f8f6ebc
docs(examples): converted from e2es
theguild-bot Mar 13, 2025
6d8900c
2 runs 2 minutes each is ok
enisdenjo Mar 13, 2025
8333373
inspect port only without jit
enisdenjo Mar 13, 2025
d600d8b
allow failing requests during loadtests
enisdenjo Mar 13, 2025
c2a1fb6
yeah we need more runs
enisdenjo Mar 13, 2025
dd0bedd
expected heavy frames
enisdenjo Mar 13, 2025
7866d3a
docs(examples): converted from e2es
theguild-bot Mar 13, 2025
3af387e
expected heavy frame func is inverted
enisdenjo Mar 13, 2025
d95aab4
comment
enisdenjo Mar 13, 2025
9b340f4
also heavy
enisdenjo Mar 13, 2025
0a6788d
more expected
enisdenjo Mar 13, 2025
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
4 changes: 1 addition & 3 deletions .github/workflows/bench.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ concurrency:

env:
NODE_NO_WARNINGS: 1
CI: true
Copy link
Member Author

Choose a reason for hiding this comment

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


jobs:
bench:
Expand All @@ -22,7 +21,7 @@ jobs:
- 10
- 100
- 1000
name: Benchmark / ${{matrix.e2e_runner}} / ${{matrix.products_size}} items
Copy link
Member Author

Choose a reason for hiding this comment

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

Looks nicer in the checks overview.

- Bench / Benchmark / node / 10 items
+ Bench / node / 10 items

name: ${{matrix.e2e_runner}} / ${{matrix.products_size}} items
runs-on: ubuntu-latest
steps:
- name: Checkout
Expand All @@ -36,4 +35,3 @@ jobs:
env:
PRODUCTS_SIZE: ${{matrix.products_size}}
E2E_GATEWAY_RUNNER: ${{matrix.e2e_runner}}
CI: true
44 changes: 44 additions & 0 deletions .github/workflows/memtest.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: Memtest
on:
push:
branches:
- main
pull_request:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

env:
NODE_NO_WARNINGS: 1
K6_VERSION: v0.56.0

jobs:
memtest:
strategy:
matrix:
e2e_runner:
- node
# - bun TODO: get memory snaps and heap sampling for bun. is it even necessary?
name: ${{matrix.e2e_runner}}
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install k6
run: |
mkdir -p "$HOME/.local/bin"
cd "$HOME/.local/bin"
curl https://github.com/grafana/k6/releases/download/${{ env.K6_VERSION }}/k6-${{ env.K6_VERSION }}-linux-amd64.tar.gz -L | tar xvz --strip-components 1
echo "$PWD" >> $GITHUB_PATH
- name: Set up env
uses: the-guild-org/shared-config/setup@v1
with:
# TODO: should we test more node versions? we usually always recommend upgrading to
# latest when people suspect leaks - latest is always the most stable
node-version-file: .node-version
- name: Test
run: yarn test:mem
# TODO: publish heap allocation sampling profile to artifact
env:
E2E_GATEWAY_RUNNER: ${{matrix.e2e_runner}}
2 changes: 2 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,8 @@ jobs:
uses: the-guild-org/shared-config/setup@v1
with:
node-version-file: .node-version
# we skip-build on ubuntu arm because the "canvas" package wont build and there are no prebuilts
install-command: ${{ matrix.os == 'ubuntu-24.04-arm' && 'yarn install --immutable --mode=skip-build' || '' }}
- name: Build
run: yarn build
- name: Bundle
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,7 @@ hive-gateway
/examples/**/*/supergraph.graphql
.helix/config.toml
.helix/languages.toml
/e2e/**/*/memtest-memory-usage_*.svg
/e2e/**/*/*.heapsnapshot
/e2e/**/*/*.heapprofile
/e2e/**/*/loadtest.out
5 changes: 5 additions & 0 deletions DEPS_RESOLUTIONS_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,8 @@ Here we collect reasons and write explanations about why some resolutions or pat
### vitest-tsconfig-paths

1. Resolve tsconfig paths in modules that have been [inlined](https://vitest.dev/config/#server-deps-inline).

### @memlab/core

1. Define package.json#export for `@memlab/core/Types`
1. Define package.json#export for `@memlab/core/Utils`
39 changes: 39 additions & 0 deletions e2e/auto-type-merging/auto-type-merging.memtest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { createTenv, type Container } from '@internal/e2e';
import { memtest } from '@internal/perf/memtest';
import { beforeAll } from 'vitest';

const cwd = __dirname;
const { service, gateway, container } = createTenv(cwd);

let petstore!: Container;
beforeAll(async () => {
petstore = await container({
name: 'petstore',
image: 'swaggerapi/petstore3:1.0.7',
containerPort: 8080,
healthcheck: ['CMD-SHELL', 'wget --spider http://localhost:8080'],
});
});

memtest(
{
cwd,
query: /* GraphQL */ `
query GetPet {
getPetById(petId: 1) {
__typename
id
name
vaccinated
}
}
`,
},
async () =>
gateway({
supergraph: {
with: 'mesh',
services: [petstore, await service('vaccination')],
},
}),
);
24 changes: 12 additions & 12 deletions e2e/cloudflare-workers/cloudflare-workers.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,18 +78,18 @@ describe.skipIf(gatewayRunner !== 'node')('Cloudflare Workers', () => {
OTLP_SERVICE_NAME: string;
}) {
const port = await getAvailablePort();
await spawn('yarn wrangler', {
args: [
'dev',
'--port',
port.toString(),
'--var',
'OTLP_EXPORTER_URL:' + env.OTLP_EXPORTER_URL,
'--var',
'OTLP_SERVICE_NAME:' + env.OTLP_SERVICE_NAME,
...(isDebug() ? ['--var', 'DEBUG:1'] : []),
],
});
await spawn([
'yarn',
'wrangler',
'dev',
'--port',
port.toString(),
'--var',
'OTLP_EXPORTER_URL:' + env.OTLP_EXPORTER_URL,
'--var',
'OTLP_SERVICE_NAME:' + env.OTLP_SERVICE_NAME,
...(isDebug() ? ['--var', 'DEBUG:1'] : []),
]);
const hostname = await getLocalhost(port);
return {
url: `${hostname}:${port}`,
Expand Down
18 changes: 18 additions & 0 deletions e2e/federation-example/federation-example.memtest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { createExampleSetup, createTenv } from '@internal/e2e';
import { memtest } from '@internal/perf/memtest';

const cwd = __dirname;

const { gateway } = createTenv(cwd);
const { supergraph, query } = createExampleSetup(cwd);

memtest(
{
cwd,
query,
},
async () =>
gateway({
supergraph: await supergraph(),
}),
);
26 changes: 26 additions & 0 deletions e2e/federation-mixed/federation-mixed.memtest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { createExampleSetup, createTenv } from '@internal/e2e';
import { memtest } from '@internal/perf/memtest';

const cwd = __dirname;

const { service, gateway } = createTenv(cwd);
const exampleSetup = createExampleSetup(cwd);

memtest(
{
cwd,
query: exampleSetup.query,
},
async () =>
gateway({
supergraph: {
with: 'mesh',
services: [
await service('accounts'),
await exampleSetup.service('inventory'),
await exampleSetup.service('products'),
await exampleSetup.service('reviews'),
],
},
}),
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { createTenv } from '@internal/e2e';
import { memtest } from '@internal/perf/memtest';
import { getAvailablePort } from '@internal/testing';
import { describe } from 'vitest';

const cwd = __dirname;

const { service, gateway } = createTenv(cwd);

describe('upstream subscriptions via websockets', () => {
memtest(
{
cwd,
query: /* GraphQL */ `
subscription {
newProduct {
id
name
price
reviews {
score
}
}
}
`,
},
async () =>
gateway({
supergraph: {
with: 'apollo',
services: [
await service('products', { env: { MEMTEST: 1 } }),
await service('reviews'),
],
},
}),
);
});

describe('upstream subscriptions via http callbacks', () => {
memtest(
{
cwd,
query: /* GraphQL */ `
subscription {
newReview {
score
}
}
`,
expectedHeavyFrame: (frame) =>
// these frames are not leaks and have been confirmed to be stable analysing the heap snapshots (they do allocate a lot, but they all of their memory gets freed)
[
'delete',
'get pathname',
'onRequest',
'Repeater.next',
'Set',
].includes(frame.name),
},
async () => {
const availablePort = await getAvailablePort();
const publicUrl = `http://0.0.0.0:${availablePort}`;
return gateway({
supergraph: {
with: 'apollo',
services: [
await service('products', { env: { MEMTEST: 1 } }),
await service('reviews'),
],
},
port: availablePort,
env: {
PUBLIC_URL: publicUrl,
},
});
},
);
});
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ const schema = buildSubgraphSchema([
clearInterval(interval);
}),
},
newProduct: {
async *subscribe() {
for (const product of products) {
yield { newProduct: product };
}
},
},
},
},
},
Expand All @@ -76,6 +83,10 @@ const graphqlWsServer = useServer(
console.error('Multiple WebSocket connections attempted');
process.exit(1);
}
if (process.env['MEMTEST']) {
// no need to authenticate in memtests
return true;
}
// make sure the authorization header is propagated by the gateway
if (connectionParams?.['token'] !== TOKEN) {
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ extend type Query {

type Subscription {
productPriceChanged: Product!
newProduct: Product!
}

type Product @key(fields: "id") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ const schema = buildSubgraphSchema([
},
resolve: (count: number) => count,
},
newReview: {
async *subscribe() {
for (const review of reviews) {
yield { newReview: review };
}
},
},
},
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ type Review {

type Subscription {
countdown(from: Int!): Int
newReview: Review!
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { createTenv } from '@internal/e2e';
import { memtest } from '@internal/perf/memtest';

const cwd = __dirname;

const { gateway, service } = createTenv(cwd);

memtest(
{
cwd,
query: /* GraphQL */ `
query {
node(id: "1") {
id
... on User {
name
}
self {
id
... on User {
name
}
}
}
}
`,
},
async () =>
await gateway({
supergraph: {
with: 'mesh',
services: [await service('Test')],
},
}),
);
1 change: 1 addition & 0 deletions e2e/nestjs/nestjs.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { expect, it } from 'vitest';
const { service } = createTenv(__dirname);
const { supergraph, query, result } = createExampleSetup(__dirname);

// TODO: run tests without needing to build the project
it.todo('executes the query', async () => {
const supergraphPath = await supergraph();
const { port } = await service('nestjs', {
Expand Down
7 changes: 6 additions & 1 deletion e2e/opentelemetry/gateway.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,10 @@ export const gatewayConfig = defineConfig({
],
serviceName: process.env['OTLP_SERVICE_NAME'],
},
plugins: () => [useOnFetchTracer()],
plugins: () =>
process.env['MEMTEST']
? [
// disable the plugin in memtests because the upstreamCallHeaders will grew forever reporting a false positive leak
]
: [useOnFetchTracer()],
});
Loading
Loading