From f29abb07ca556b593bd55f8a4324e67665d784dc Mon Sep 17 00:00:00 2001 From: Valentin Cocaud Date: Tue, 30 Sep 2025 16:13:43 +0200 Subject: [PATCH 1/2] add benchmark for response caching --- .../gateway-with-cache.config.ts | 12 +++ .../gateway-with-redis.config.ts | 17 +++++ .../gateway-without-cache.config.ts | 5 ++ bench/response-cache/response-cache.bench.ts | 74 +++++++++++++++++++ .../distributed-subscriptions-webhooks.e2e.ts | 4 +- 5 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 bench/response-cache/gateway-with-cache.config.ts create mode 100644 bench/response-cache/gateway-with-redis.config.ts create mode 100644 bench/response-cache/gateway-without-cache.config.ts create mode 100644 bench/response-cache/response-cache.bench.ts diff --git a/bench/response-cache/gateway-with-cache.config.ts b/bench/response-cache/gateway-with-cache.config.ts new file mode 100644 index 000000000..f09c98e8a --- /dev/null +++ b/bench/response-cache/gateway-with-cache.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from '@graphql-hive/gateway'; + +export const gatewayConfig = defineConfig({ + responseCaching: { + ttl: 0, + ttlPerType: { + 'Query.me': 2000, + }, + session: () => null, + }, + maskedErrors: false, +}); diff --git a/bench/response-cache/gateway-with-redis.config.ts b/bench/response-cache/gateway-with-redis.config.ts new file mode 100644 index 000000000..0c2d897c6 --- /dev/null +++ b/bench/response-cache/gateway-with-redis.config.ts @@ -0,0 +1,17 @@ +import { defineConfig } from '@graphql-hive/gateway'; + +export const gatewayConfig = defineConfig({ + cache: { + type: 'redis', + url: process.env['REDIS_URL'], // The URL of the Redis server + lazyConnect: false, + }, + responseCaching: { + ttl: 0, + ttlPerType: { + 'Query.me': 2000, + }, + session: () => null, + }, + maskedErrors: false, +}); diff --git a/bench/response-cache/gateway-without-cache.config.ts b/bench/response-cache/gateway-without-cache.config.ts new file mode 100644 index 000000000..6f84e7157 --- /dev/null +++ b/bench/response-cache/gateway-without-cache.config.ts @@ -0,0 +1,5 @@ +import { defineConfig } from '@graphql-hive/gateway'; + +export const gatewayConfig = defineConfig({ + maskedErrors: false, +}); diff --git a/bench/response-cache/response-cache.bench.ts b/bench/response-cache/response-cache.bench.ts new file mode 100644 index 000000000..48b936c4b --- /dev/null +++ b/bench/response-cache/response-cache.bench.ts @@ -0,0 +1,74 @@ +import { createExampleSetup, createTenv } from '@internal/e2e'; +import { benchConfig } from '@internal/testing'; +import { bench, describe, expect } from 'vitest'; + +describe('Response Cache', async () => { + const { gateway, container } = createTenv(__dirname); + const exampleSetup = createExampleSetup(__dirname, 1000); + + const redis = await container({ + name: 'redis', + healthcheck: ['CMD', 'redis-cli', 'ping'], + env: { + LANG: '', + LC_ALL: '', + }, + image: 'redis', + containerPort: 6379, + }); + + const supergraph = await exampleSetup.supergraph(); + + const { query, operationName, result } = exampleSetup; + + const gatewayWithoutCache = await gateway({ + supergraph, + args: ['-c', 'gateway-without-cache.config.ts'], + }); + bench( + 'Without response cache', + async () => { + const response = await gatewayWithoutCache.execute({ + query, + operationName, + }); + expect(response).toEqual(result); + }, + benchConfig, + ); + + const gatewayWithCache = await gateway({ + supergraph, + args: ['-c', 'gateway-with-cache.config.ts'], + }); + bench( + 'With in memory response cache', + async () => { + const response = await gatewayWithCache.execute({ + query, + operationName, + }); + expect(response).toEqual(result); + }, + benchConfig, + ); + + const gatewayWithRedisCache = await gateway({ + supergraph, + args: ['-c', 'gateway-with-redis.config.ts'], + env: { + REDIS_URL: `redis://localhost:${redis.port}`, + }, + }); + bench( + 'With redis response cache', + async () => { + const response = await gatewayWithRedisCache.execute({ + query, + operationName, + }); + expect(response).toEqual(result); + }, + benchConfig, + ); +}); diff --git a/e2e/distributed-subscriptions-webhooks/distributed-subscriptions-webhooks.e2e.ts b/e2e/distributed-subscriptions-webhooks/distributed-subscriptions-webhooks.e2e.ts index bf19e8754..eb59fd201 100644 --- a/e2e/distributed-subscriptions-webhooks/distributed-subscriptions-webhooks.e2e.ts +++ b/e2e/distributed-subscriptions-webhooks/distributed-subscriptions-webhooks.e2e.ts @@ -23,7 +23,9 @@ beforeAll(async () => { containerPort: 6379, healthcheck: ['CMD-SHELL', 'redis-cli ping'], env: { - LANG: '', // fixes "Failed to configure LOCALE for invalid locale name." + // fixes "Failed to configure LOCALE for invalid locale name." + LANG: '', + LC_ALL: '', }, }); redisEnv.REDIS_HOST = gatewayRunner.includes('docker') From b376fee7ed00580e820de00e2a513898a0fa30e0 Mon Sep 17 00:00:00 2001 From: Valentin Cocaud Date: Thu, 9 Oct 2025 11:24:33 +0200 Subject: [PATCH 2/2] :construction: --- ...ateway-without-auto-invalidation.config.ts | 13 +++ bench/response-cache/response-cache.bench.ts | 110 ++++++++++-------- internal/e2e/src/tenv.ts | 3 +- package.json | 3 +- yarn.lock | 8 +- 5 files changed, 80 insertions(+), 57 deletions(-) create mode 100644 bench/response-cache/gateway-without-auto-invalidation.config.ts diff --git a/bench/response-cache/gateway-without-auto-invalidation.config.ts b/bench/response-cache/gateway-without-auto-invalidation.config.ts new file mode 100644 index 000000000..afcb0ca69 --- /dev/null +++ b/bench/response-cache/gateway-without-auto-invalidation.config.ts @@ -0,0 +1,13 @@ +import { defineConfig } from '@graphql-hive/gateway'; + +export const gatewayConfig = defineConfig({ + responseCaching: { + invalidateViaMutation: false, + ttl: 0, + ttlPerType: { + 'Query.me': 2000, + }, + session: () => null, + }, + maskedErrors: false, +}); diff --git a/bench/response-cache/response-cache.bench.ts b/bench/response-cache/response-cache.bench.ts index 48b936c4b..42f433636 100644 --- a/bench/response-cache/response-cache.bench.ts +++ b/bench/response-cache/response-cache.bench.ts @@ -1,11 +1,10 @@ -import { createExampleSetup, createTenv } from '@internal/e2e'; +import { setTimeout } from 'node:timers/promises'; +import { createExampleSetup, createTenv, GatewayOptions } from '@internal/e2e'; import { benchConfig } from '@internal/testing'; import { bench, describe, expect } from 'vitest'; describe('Response Cache', async () => { - const { gateway, container } = createTenv(__dirname); - const exampleSetup = createExampleSetup(__dirname, 1000); - + const { runBench, container } = await makeRunner(); const redis = await container({ name: 'redis', healthcheck: ['CMD', 'redis-cli', 'ping'], @@ -17,58 +16,67 @@ describe('Response Cache', async () => { containerPort: 6379, }); - const supergraph = await exampleSetup.supergraph(); - - const { query, operationName, result } = exampleSetup; - - const gatewayWithoutCache = await gateway({ - supergraph, - args: ['-c', 'gateway-without-cache.config.ts'], - }); - bench( - 'Without response cache', - async () => { - const response = await gatewayWithoutCache.execute({ - query, - operationName, - }); - expect(response).toEqual(result); - }, - benchConfig, + await runBench( + 'With in memory response cache', + 'gateway-with-cache.config.ts', ); + await runBench('Without response cache', 'gateway-without-cache.config.ts'); - const gatewayWithCache = await gateway({ - supergraph, - args: ['-c', 'gateway-with-cache.config.ts'], - }); - bench( - 'With in memory response cache', - async () => { - const response = await gatewayWithCache.execute({ - query, - operationName, - }); - expect(response).toEqual(result); - }, - benchConfig, + await runBench.skip( + 'Without invalidation cache', + 'gateway-without-auto-invalidation.config.ts', ); - const gatewayWithRedisCache = await gateway({ - supergraph, - args: ['-c', 'gateway-with-redis.config.ts'], - env: { - REDIS_URL: `redis://localhost:${redis.port}`, - }, - }); - bench( + await runBench.skip( 'With redis response cache', - async () => { - const response = await gatewayWithRedisCache.execute({ - query, - operationName, - }); - expect(response).toEqual(result); + 'gateway-with-redis.config.ts', + { + env: { + REDIS_URL: `redis://localhost:${redis.port}`, + }, }, - benchConfig, ); }); + +const makeRunner = async () => { + const { gateway, container } = createTenv(__dirname); + const exampleSetup = createExampleSetup(__dirname, 1000); + + const supergraph = await exampleSetup.supergraph(); + + const { query, operationName, result } = exampleSetup; + + const runBench = async ( + name: string, + configFile: string, + options?: Partial, + ) => { + const { execute } = await gateway({ + supergraph, + ...options, + args: ['-c', configFile, ...(options?.args ?? [])], + }); + return bench( + name, + async () => { + const response = await execute({ + query, + operationName, + }); + expect(response).toEqual(result); + }, + benchConfig, + ); + }; + + runBench.skip = async ( + name: string, + _configFile: string, + _options?: GatewayOptions, + ) => bench.skip(name); + + return { + runBench, + container, + }; +}; diff --git a/internal/e2e/src/tenv.ts b/internal/e2e/src/tenv.ts index 14bec6046..906b324f6 100644 --- a/internal/e2e/src/tenv.ts +++ b/internal/e2e/src/tenv.ts @@ -544,7 +544,8 @@ export function createTenv(cwd: string): Tenv { 'node', // use next available port when starting inspector (note that this does not start inspect, this still needs to be done manually) // it's not set because in JIT mode because it does not work together (why? no clue) - args.includes('--jit') ? null : '--inspect-port=0', + args.includes('--jit') ? null : '--inspect-port=9999', + // '--inspect', '--import', 'tsx', path.resolve(__project, 'packages', 'gateway', 'src', 'bin.ts'), diff --git a/package.json b/package.json index 48cc63d2e..e96de63f0 100644 --- a/package.json +++ b/package.json @@ -81,6 +81,7 @@ "tar-fs": "3.1.1", "tmp": "0.2.4", "tsx": "patch:tsx@npm%3A4.20.3#~/.yarn/patches/tsx-npm-4.20.3-7de67a623f.patch", - "vite": "7.1.7" + "vite": "7.1.7", + "@envelop/response-cache": "8.2.0-alpha-20251003143108-b56597767a1f741fbd6420f60af9abdf439fd4b8" } } diff --git a/yarn.lock b/yarn.lock index 23ee1c372..7ecad9a4e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3659,9 +3659,9 @@ __metadata: languageName: node linkType: hard -"@envelop/response-cache@npm:^8.0.0, @envelop/response-cache@npm:^8.0.2": - version: 8.0.2 - resolution: "@envelop/response-cache@npm:8.0.2" +"@envelop/response-cache@npm:8.2.0-alpha-20251003143108-b56597767a1f741fbd6420f60af9abdf439fd4b8": + version: 8.2.0-alpha-20251003143108-b56597767a1f741fbd6420f60af9abdf439fd4b8 + resolution: "@envelop/response-cache@npm:8.2.0-alpha-20251003143108-b56597767a1f741fbd6420f60af9abdf439fd4b8" dependencies: "@graphql-tools/utils": "npm:^10.0.3" "@whatwg-node/fetch": "npm:^0.10.0" @@ -3672,7 +3672,7 @@ __metadata: peerDependencies: "@envelop/core": ^5.3.2 graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 - checksum: 10c0/061435944d782263b52b85fa0cd49b23caa7a880c4a5fada3c4273f149c2ed25dc6ca4668f92283bf99ec0d13ca044581703fe69e7a5b67fdd6f3893f4ee0e13 + checksum: 10c0/7ff40a8d2b88a15681b3412e467f3a1726d559c4d1fdec72cd46d612504ef6047ea8752d8aa47f9776d0a917d65474f47c9e0cfe5c1973ad3ee551f7e1fbd065 languageName: node linkType: hard