1+ import { mkdir , writeFile } from 'node:fs/promises' ;
2+ import { dirname , resolve } from 'node:path' ;
3+
4+ import {
5+ explainGraphqlBenchmarks ,
6+ formatExplainReport
7+ } from '../src/lib/graphql-benchmark/explain' ;
18import { runGraphqlBenchmarks } from '../src/lib/graphql-benchmark/run' ;
29import { graphqlBenchmarkRunFailed } from '../src/lib/graphql-benchmark/suite-failure' ;
310import type { GraphqlBenchmarkSuite } from '../src/lib/graphql-benchmark/types' ;
@@ -26,33 +33,43 @@ const defaultUrl =
2633 : 'https://sub2.quantus.com/v1/graphql' ;
2734const endpoint = process . env . GRAPHQL_BENCH_URL ?? defaultUrl ;
2835const samples = Number ( argValue ( '--samples' ) ?? ( suite === 'mobile' ? 5 : 1 ) ) ;
36+ const group = argValue ( '--group' ) ;
37+ const explain = args . includes ( '--explain' ) ;
38+ const outPath = argValue ( '--out' ) ;
2939
30- async function main ( ) {
31- // eslint-disable-next-line no-console
32- console . error ( `GraphQL bench [${ suite } ] samples=${ samples } → ${ endpoint } \n` ) ;
33- const { results, bootstrapContext, bootstrapRequestFailures } =
34- await runGraphqlBenchmarks ( {
35- endpoint,
36- suite,
37- samples
38- } ) ;
40+ function printBootstrap ( context : unknown , failures : string [ ] ) {
3941 // eslint-disable-next-line no-console
40- console . log ( 'Bootstrap context:' , JSON . stringify ( bootstrapContext , null , 2 ) ) ;
41- if ( bootstrapRequestFailures . length > 0 ) {
42+ console . log ( 'Bootstrap context:' , JSON . stringify ( context , null , 2 ) ) ;
43+ if ( failures . length > 0 ) {
4244 // eslint-disable-next-line no-console
4345 console . log ( '\nBootstrap request failures:' ) ;
44- for ( const failure of bootstrapRequestFailures ) {
46+ for ( const failure of failures ) {
4547 // eslint-disable-next-line no-console
4648 console . log ( ` ${ failure } ` ) ;
4749 }
4850 }
51+ }
52+
53+ async function bench ( ) {
54+ // eslint-disable-next-line no-console
55+ console . error (
56+ `GraphQL bench [${ suite } ${ group ? ` / ${ group } ` : '' } ] samples=${ samples } → ${ endpoint } \n`
57+ ) ;
58+ const { results, bootstrapContext, bootstrapRequestFailures } =
59+ await runGraphqlBenchmarks ( {
60+ endpoint,
61+ suite,
62+ group,
63+ samples
64+ } ) ;
65+ printBootstrap ( bootstrapContext , bootstrapRequestFailures ) ;
4966 // eslint-disable-next-line no-console
5067 console . log ( '\nResults (slowest first):' ) ;
5168 for ( const r of results ) {
52- const group = r . group ? `[${ r . group } ] ` : '' ;
69+ const label = r . group ? `[${ r . group } ] ` : '' ;
5370 if ( r . skipped ) {
5471 // eslint-disable-next-line no-console
55- console . log ( ` ${ group } ${ r . name } SKIPPED ${ r . skipReason ?? '' } ` ) ;
72+ console . log ( ` ${ label } ${ r . name } SKIPPED ${ r . skipReason ?? '' } ` ) ;
5673 } else {
5774 const spread =
5875 r . minMs != null && r . maxMs != null
@@ -61,13 +78,62 @@ async function main() {
6178 const rows = r . rowCount != null ? ` rows=${ r . rowCount } ` : '' ;
6279 // eslint-disable-next-line no-console
6380 console . log (
64- ` ${ group } ${ r . name } ${ r . durationMs } ms${ spread } bytes=${ r . responseBytes ?? '—' } ${ rows } ${ r . errorMessage ?? 'OK' } `
81+ ` ${ label } ${ r . name } ${ r . durationMs } ms${ spread } bytes=${ r . responseBytes ?? '—' } ${ rows } ${ r . errorMessage ?? 'OK' } `
6582 ) ;
6683 }
6784 }
68- process . exit (
69- graphqlBenchmarkRunFailed ( results , bootstrapRequestFailures ) ? 1 : 0
85+ return graphqlBenchmarkRunFailed ( results , bootstrapRequestFailures ) ;
86+ }
87+
88+ async function explainPlans ( ) {
89+ const adminSecret = process . env . HASURA_ADMIN_SECRET ;
90+ if ( ! adminSecret ) {
91+ throw new Error ( '--explain needs HASURA_ADMIN_SECRET in the environment' ) ;
92+ }
93+ // eslint-disable-next-line no-console
94+ console . error (
95+ `GraphQL explain [${ suite } ${ group ? ` / ${ group } ` : '' } ] → ${ endpoint } /explain\n`
96+ ) ;
97+ const generatedAt = new Date ( ) ;
98+ const { results, bootstrapContext, bootstrapRequestFailures } =
99+ await explainGraphqlBenchmarks ( { endpoint, adminSecret, suite, group } ) ;
100+
101+ const file = resolve (
102+ outPath ??
103+ `out/graphql-explain/${ suite } -${ group ?? 'all' } -${ generatedAt
104+ . toISOString ( )
105+ . replace ( / [: .] / g, '-' ) } .md`
106+ ) ;
107+ await mkdir ( dirname ( file ) , { recursive : true } ) ;
108+ await writeFile (
109+ file ,
110+ formatExplainReport ( {
111+ endpoint,
112+ suite,
113+ group,
114+ generatedAt,
115+ bootstrapContext,
116+ bootstrapRequestFailures,
117+ results
118+ } )
70119 ) ;
120+
121+ for ( const r of results ) {
122+ const label = r . group ? `[${ r . group } ] ` : '' ;
123+ let status = 'OK' ;
124+ if ( r . skipped ) status = `SKIPPED ${ r . skipReason ?? '' } ` ;
125+ else if ( r . errorMessage ) status = `ERROR ${ r . errorMessage } ` ;
126+ // eslint-disable-next-line no-console
127+ console . log ( ` ${ label } ${ r . name } ${ status } ` ) ;
128+ }
129+ // eslint-disable-next-line no-console
130+ console . log ( `\nExplain report written to ${ file } ` ) ;
131+ return graphqlBenchmarkRunFailed ( results , bootstrapRequestFailures ) ;
132+ }
133+
134+ async function main ( ) {
135+ const failed = explain ? await explainPlans ( ) : await bench ( ) ;
136+ process . exit ( failed ? 1 : 0 ) ;
71137}
72138
73139main ( ) . catch ( ( e ) => {
0 commit comments