diff --git a/.github/workflows/federation-v1.workflow.yaml b/.github/workflows/federation-v1.workflow.yaml index 153da207b..6b6f96908 100644 --- a/.github/workflows/federation-v1.workflow.yaml +++ b/.github/workflows/federation-v1.workflow.yaml @@ -29,23 +29,24 @@ jobs: directory: - apollo-server - apollo-router + - hive-gateway - cosmo - mercurius - wundergraph - - mesh-supergraph - - mesh-supergraph-bun + # - mesh-supergraph + # - mesh-supergraph-bun # - grafbase uses: ./.github/workflows/benchmark.template.yaml with: gateway: ${{ matrix.directory }} - vu: ${{ github.event_name == 'pull_request' && 100 || 300 }} + vu: 300 time: ${{ github.event_name == 'pull_request' && '30s' || '600s' }} baseDir: federation-v1 scenarioDir: constant-vus-over-time scenarioName: constant-vus-over-time runner: ${{ needs.decide-runner.outputs.runner }} - cpuLimit: ${{ github.event_name == 'pull_request' && '1' || '2' }} - memoryLimit: ${{ github.event_name == 'pull_request' && '2gb' || '4gb' }} + cpuLimit: 2 + memoryLimit: 4gb constant-vus-over-time-report: needs: constant-vus-over-time @@ -66,25 +67,26 @@ jobs: directory: - apollo-server - apollo-router + - hive-gateway - cosmo - mercurius - wundergraph # - mesh # - mesh-bun - - mesh-supergraph - - mesh-supergraph-bun + # - mesh-supergraph + # - mesh-supergraph-bun # - grafbase uses: ./.github/workflows/benchmark.template.yaml with: gateway: ${{ matrix.directory }} - vu: ${{ github.event_name == 'pull_request' && 100 || 300 }} + vu: 300 time: ${{ github.event_name == 'pull_request' && '30s' || '600s' }} baseDir: federation-v1 scenarioDir: constant-vus-over-time scenarioName: constant-vus-subgraphs-delay runner: ${{ needs.decide-runner.outputs.runner }} - cpuLimit: ${{ github.event_name == 'pull_request' && '1' || '2' }} - memoryLimit: ${{ github.event_name == 'pull_request' && '2gb' || '4gb' }} + cpuLimit: 2 + memoryLimit: 4gb subgraphDelayRange: "40~150" constant-vus-subgraphs-delay-report: @@ -106,25 +108,26 @@ jobs: directory: - apollo-server - apollo-router + - hive-gateway - cosmo - mercurius - wundergraph # - mesh # - mesh-bun - - mesh-supergraph - - mesh-supergraph-bun + # - mesh-supergraph + # - mesh-supergraph-bun # - grafbase uses: ./.github/workflows/benchmark.template.yaml with: gateway: ${{ matrix.directory }} - vu: ${{ github.event_name == 'pull_request' && 100 || 500 }} + vu: 500 time: ${{ github.event_name == 'pull_request' && '30s' || '600s' }} baseDir: federation-v1 scenarioDir: constant-vus-over-time scenarioName: constant-vus-subgraphs-delay-resources runner: ${{ needs.decide-runner.outputs.runner }} - cpuLimit: ${{ github.event_name == 'pull_request' && '2' || '4' }} - memoryLimit: ${{ github.event_name == 'pull_request' && '2gb' || '8gb' }} + cpuLimit: 4 + memoryLimit: 8gb subgraphDelayRange: "40~150" constant-vus-subgraphs-delay-resources-report: @@ -146,25 +149,26 @@ jobs: directory: - apollo-server - apollo-router + - hive-gateway - cosmo # - mercurius - wundergraph # - mesh # - mesh-bun - - mesh-supergraph - - mesh-supergraph-bun + # - mesh-supergraph + # - mesh-supergraph-bun # - grafbase uses: ./.github/workflows/benchmark.template.yaml with: gateway: ${{ matrix.directory }} - vu: ${{ github.event_name == 'pull_request' && 500 || 2000 }} + vu: 2000 time: ${{ github.event_name == 'pull_request' && '60s' || '600s' }} baseDir: federation-v1 scenarioDir: ramping-vus scenarioName: ramping-vus runner: ${{ needs.decide-runner.outputs.runner }} - cpuLimit: ${{ github.event_name == 'pull_request' && '2' || '4' }} - memoryLimit: ${{ github.event_name == 'pull_request' && '2gb' || '8gb' }} + cpuLimit: 4 + memoryLimit: 8gb ramping-vus-report: needs: ramping-vus diff --git a/federation-v1/gateways/hive-gateway/docker-compose.yaml b/federation-v1/gateways/hive-gateway/docker-compose.yaml new file mode 100644 index 000000000..0432d7f71 --- /dev/null +++ b/federation-v1/gateways/hive-gateway/docker-compose.yaml @@ -0,0 +1,45 @@ +services: + gateway: + image: ghcr.io/ardatan/hive-gateway:1.3.0 + container_name: gateway + networks: + - test + ports: + - "0.0.0.0:4000:4000" + environment: + - NODE_ENV=production + - JIT=1 + depends_on: + accounts: + condition: service_healthy + inventory: + condition: service_healthy + products: + condition: service_healthy + reviews: + condition: service_healthy + volumes: + - type: bind + source: federation-v1/gateways/hive-gateway/supergraph.graphql + target: /serve/supergraph.graphql + healthcheck: + test: + [ + "CMD", + "/usr/lib/apt/apt-helper", + "download-file", + "http://127.0.0.1:4000/readiness", + "/tmp/health", + ] + interval: 3s + timeout: 5s + retries: 10 + command: ["supergraph"] + deploy: + resources: + limits: + cpus: ${CPU_LIMIT:-1} + memory: ${MEM_LIMIT:-1gb} +networks: + test: + name: test diff --git a/federation-v1/gateways/hive-gateway/supergraph.graphql b/federation-v1/gateways/hive-gateway/supergraph.graphql new file mode 100644 index 000000000..c7995b32f --- /dev/null +++ b/federation-v1/gateways/hive-gateway/supergraph.graphql @@ -0,0 +1,89 @@ +schema + @link(url: "https://specs.apollo.dev/link/v1.0") + @link(url: "https://specs.apollo.dev/join/v0.3", for: EXECUTION) +{ + query: Query +} + +directive @join__enumValue(graph: join__Graph!) repeatable on ENUM_VALUE + +directive @join__field(graph: join__Graph, requires: join__FieldSet, provides: join__FieldSet, type: String, external: Boolean, override: String, usedOverridden: Boolean) repeatable on FIELD_DEFINITION | INPUT_FIELD_DEFINITION + +directive @join__graph(name: String!, url: String!) on ENUM_VALUE + +directive @join__implements(graph: join__Graph!, interface: String!) repeatable on OBJECT | INTERFACE + +directive @join__type(graph: join__Graph!, key: join__FieldSet, extension: Boolean! = false, resolvable: Boolean! = true, isInterfaceObject: Boolean! = false) repeatable on OBJECT | INTERFACE | UNION | ENUM | INPUT_OBJECT | SCALAR + +directive @join__unionMember(graph: join__Graph!, member: String!) repeatable on UNION + +directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA + +scalar join__FieldSet + +enum join__Graph { + ACCOUNTS @join__graph(name: "accounts", url: "http://accounts:4001/graphql") + INVENTORY @join__graph(name: "inventory", url: "http://inventory:4002/graphql") + PRODUCTS @join__graph(name: "products", url: "http://products:4003/graphql") + REVIEWS @join__graph(name: "reviews", url: "http://reviews:4004/graphql") +} + +scalar link__Import + +enum link__Purpose { + """ + `SECURITY` features provide metadata necessary to securely resolve fields. + """ + SECURITY + + """ + `EXECUTION` features provide metadata necessary for operation execution. + """ + EXECUTION +} + +type Product + @join__type(graph: INVENTORY, key: "upc") + @join__type(graph: PRODUCTS, key: "upc") + @join__type(graph: REVIEWS, key: "upc") +{ + upc: String! + weight: Int @join__field(graph: INVENTORY, external: true) @join__field(graph: PRODUCTS) + price: Int @join__field(graph: INVENTORY, external: true) @join__field(graph: PRODUCTS) + inStock: Boolean @join__field(graph: INVENTORY) + shippingEstimate: Int @join__field(graph: INVENTORY, requires: "price weight") + name: String @join__field(graph: PRODUCTS) + reviews: [Review] @join__field(graph: REVIEWS) +} + +type Query + @join__type(graph: ACCOUNTS) + @join__type(graph: INVENTORY) + @join__type(graph: PRODUCTS) + @join__type(graph: REVIEWS) +{ + me: User @join__field(graph: ACCOUNTS) + user(id: ID!): User @join__field(graph: ACCOUNTS) + users: [User] @join__field(graph: ACCOUNTS) + topProducts(first: Int = 5): [Product] @join__field(graph: PRODUCTS) +} + +type Review + @join__type(graph: REVIEWS, key: "id") +{ + id: ID! + body: String + product: Product + author: User @join__field(graph: REVIEWS, provides: "username") +} + +type User + @join__type(graph: ACCOUNTS, key: "id") + @join__type(graph: REVIEWS, key: "id") +{ + id: ID! + name: String @join__field(graph: ACCOUNTS) + username: String @join__field(graph: ACCOUNTS) @join__field(graph: REVIEWS, external: true) + birthday: Int @join__field(graph: ACCOUNTS) + reviews: [Review] @join__field(graph: REVIEWS) +} \ No newline at end of file diff --git a/federation-v1/scenarios/compose-supergraph.ts b/federation-v1/scenarios/compose-supergraph.ts index 4b292bf0a..cbaa0fad2 100644 --- a/federation-v1/scenarios/compose-supergraph.ts +++ b/federation-v1/scenarios/compose-supergraph.ts @@ -65,6 +65,7 @@ async function main() { } else { console.log("Got SDL:\n", supergraphSdl); writeFileSync(__dirname + '/../gateways/apollo-router/supergraph.graphql', supergraphSdl!); + writeFileSync(__dirname + '/../gateways/hive-gateway/supergraph.graphql', supergraphSdl!); writeFileSync(__dirname + '/../gateways/grafbase/supergraph.graphql', supergraphSdl!); writeFileSync(__dirname + '/../gateways/apollo-server/supergraph.graphql', supergraphSdl!); writeFileSync(__dirname + '/../gateways/apollo-server-node16/supergraph.graphql', supergraphSdl!); diff --git a/federation-v1/scenarios/constant-vus-over-time/benchmark.k6.js b/federation-v1/scenarios/constant-vus-over-time/benchmark.k6.js index 6e7293f6c..47035908a 100644 --- a/federation-v1/scenarios/constant-vus-over-time/benchmark.k6.js +++ b/federation-v1/scenarios/constant-vus-over-time/benchmark.k6.js @@ -1,4 +1,8 @@ -import { makeGraphQLRequest, handleBenchmarkSummary } from '../k6.shared.js' +import { + makeGraphQLRequest, + handleBenchmarkSummary, + sendGraphQLRequest, +} from "../k6.shared.js"; const vus = __ENV.BENCH_VUS ? parseInt(__ENV.BENCH_VUS) : 100; const time = __ENV.BENCH_OVER_TIME || "30s"; @@ -8,6 +12,12 @@ export const options = { duration: time, }; +export function setup() { + for (let i = 0; i < 20; i++) { + sendGraphQLRequest(); + } +} + export default function() { makeGraphQLRequest() } diff --git a/federation-v1/scenarios/k6.shared.js b/federation-v1/scenarios/k6.shared.js index 884ceef65..6b4b2bf78 100644 --- a/federation-v1/scenarios/k6.shared.js +++ b/federation-v1/scenarios/k6.shared.js @@ -99,7 +99,7 @@ export function handleBenchmarkSummary(data, additionalContext = {}) { return out; } -export function makeGraphQLRequest() { +export function sendGraphQLRequest() { const res = http.post( __ENV.GATEWAY_ENDPOINT || "http://localhost:4000/graphql", graphqlRequest.payload, @@ -110,6 +110,11 @@ export function makeGraphQLRequest() { console.log(`‼️ Failed to run HTTP request:`, res); } + return res; +} + +export function makeGraphQLRequest() { + const res = sendGraphQLRequest(); check(res, { "response code was 200": (res) => res.status == 200, "no graphql errors": (resp) => { diff --git a/federation-v1/scenarios/ramping-vus/benchmark.k6.js b/federation-v1/scenarios/ramping-vus/benchmark.k6.js index 124e2eb04..ea8d893e7 100644 --- a/federation-v1/scenarios/ramping-vus/benchmark.k6.js +++ b/federation-v1/scenarios/ramping-vus/benchmark.k6.js @@ -1,4 +1,4 @@ -import { makeGraphQLRequest, handleBenchmarkSummary } from '../k6.shared.js' +import { makeGraphQLRequest, handleBenchmarkSummary, sendGraphQLRequest } from '../k6.shared.js' const vus = __ENV.BENCH_VUS ? parseInt(__ENV.BENCH_VUS) : 500; const time = __ENV.BENCH_OVER_TIME || "30s"; @@ -20,6 +20,12 @@ export const options = { }, }; +export function setup() { + for (let i = 0; i < 20; i++) { + sendGraphQLRequest(); + } +} + export default function() { makeGraphQLRequest() }