Skip to content

Commit edf528b

Browse files
authored
fix(schema): always treat network errors as schema publish rejections (#6779)
1 parent 2db96af commit edf528b

File tree

18 files changed

+255
-90
lines changed

18 files changed

+255
-90
lines changed

deployment/services/schema.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ export function deploySchema({
4242
SCHEMA_CACHE_POLL_INTERVAL_MS: '150',
4343
SCHEMA_CACHE_TTL_MS: '65000' /* 65s */,
4444
SCHEMA_CACHE_SUCCESS_TTL_MS: String(hourInMS * 2),
45-
SCHEMA_COMPOSITION_TIMEOUT_MS: String(minuteInMS),
4645
OPENTELEMETRY_COLLECTOR_ENDPOINT:
4746
observability.enabled && observability.tracingEndpoint
4847
? observability.tracingEndpoint

integration-tests/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
"@hive/schema": "workspace:*",
2222
"@hive/server": "workspace:*",
2323
"@hive/storage": "workspace:*",
24-
"@theguild/federation-composition": "0.18.1",
24+
"@theguild/federation-composition": "0.18.3",
2525
"@trpc/client": "10.45.2",
2626
"@trpc/server": "10.45.2",
2727
"@types/async-retry": "1.4.8",

integration-tests/tests/api/schema/external-composition.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ test.concurrent('a timeout error should be visible to the user', async ({ expect
367367
total: 1,
368368
nodes: [
369369
{
370-
message: expect.stringMatching(/timeout/i),
370+
message: expect.stringMatching(/The schema composition timed out. Please try again./i),
371371
},
372372
],
373373
},

packages/libraries/cli/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
"@oclif/core": "^3.26.6",
6060
"@oclif/plugin-help": "6.0.22",
6161
"@oclif/plugin-update": "4.2.13",
62-
"@theguild/federation-composition": "0.18.1",
62+
"@theguild/federation-composition": "0.18.3",
6363
"colors": "1.4.0",
6464
"env-ci": "7.3.0",
6565
"graphql": "^16.8.1",

packages/services/api/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
"@sentry/node": "7.120.2",
3838
"@sentry/types": "7.120.2",
3939
"@slack/web-api": "7.8.0",
40-
"@theguild/federation-composition": "0.18.1",
40+
"@theguild/federation-composition": "0.18.3",
4141
"@trpc/client": "10.45.2",
4242
"@trpc/server": "10.45.2",
4343
"@types/bcryptjs": "2.4.6",

packages/services/api/src/modules/schema/providers/models/composite.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,21 @@ export class CompositeModel {
456456
})) ?? null,
457457
});
458458

459+
if (
460+
compositionCheck.status === 'failed' &&
461+
(compositionCheck.reason.includesNetworkError || compositionCheck.reason.includesException)
462+
) {
463+
return {
464+
conclusion: SchemaPublishConclusion.Reject,
465+
reasons: [
466+
{
467+
code: PublishFailureReasonCode.CompositionFailure,
468+
compositionErrors: compositionCheck.reason.errorsBySource.composition,
469+
},
470+
],
471+
};
472+
}
473+
459474
if (
460475
compositionCheck.status === 'failed' &&
461476
compositionCheck.reason.errorsBySource.graphql.length > 0 &&

packages/services/api/src/modules/schema/providers/orchestrators/federation.ts

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,13 @@ import { abortSignalAny } from '@graphql-hive/signal';
33
import type { ContractsInputType, SchemaBuilderApi } from '@hive/schema';
44
import { traceFn } from '@hive/service-common';
55
import { createTRPCProxyClient, httpLink } from '@trpc/client';
6-
import { Orchestrator, Project, ProjectType, SchemaObject } from '../../../../shared/entities';
6+
import {
7+
ComposeAndValidateResult,
8+
Orchestrator,
9+
Project,
10+
ProjectType,
11+
SchemaObject,
12+
} from '../../../../shared/entities';
713
import { Logger } from '../../../shared/providers/logger';
814
import type { SchemaServiceConfig } from './tokens';
915
import { SCHEMA_SERVICE_CONFIG } from './tokens';
@@ -121,8 +127,29 @@ export class FederationOrchestrator implements Orchestrator {
121127
signal: abortSignalAny([this.incomingRequestAbortSignal, timeoutAbortSignal]),
122128
},
123129
);
124-
125130
return result;
131+
} catch (err) {
132+
// In case of a timeout error we return something the user can process
133+
if (timeoutAbortSignal.reason) {
134+
return {
135+
contracts: null,
136+
metadataAttributes: null,
137+
schemaMetadata: null,
138+
sdl: null,
139+
supergraph: null,
140+
tags: null,
141+
includesNetworkError: true,
142+
includesException: false,
143+
errors: [
144+
{
145+
message: 'The schema composition timed out. Please try again.',
146+
source: 'composition',
147+
},
148+
],
149+
} satisfies ComposeAndValidateResult;
150+
}
151+
152+
throw err;
126153
} finally {
127154
timeoutAbortSignal.removeEventListener('abort', onTimeout);
128155
this.incomingRequestAbortSignal.removeEventListener('abort', onIncomingRequestAbort);

packages/services/api/src/modules/schema/providers/orchestrators/single.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,12 @@ import { abortSignalAny } from '@graphql-hive/signal';
33
import type { SchemaBuilderApi } from '@hive/schema';
44
import { traceFn } from '@hive/service-common';
55
import { createTRPCProxyClient, httpLink } from '@trpc/client';
6-
import { Orchestrator, ProjectType, SchemaObject } from '../../../../shared/entities';
6+
import {
7+
ComposeAndValidateResult,
8+
Orchestrator,
9+
ProjectType,
10+
SchemaObject,
11+
} from '../../../../shared/entities';
712
import { Logger } from '../../../shared/providers/logger';
813
import type { SchemaServiceConfig } from './tokens';
914
import { SCHEMA_SERVICE_CONFIG } from './tokens';
@@ -90,6 +95,27 @@ export class SingleOrchestrator implements Orchestrator {
9095
},
9196
);
9297
return result;
98+
} catch (err) {
99+
// In case of a timeout error we return something the user can process
100+
if (timeoutAbortSignal.reason) {
101+
return {
102+
contracts: null,
103+
metadataAttributes: null,
104+
schemaMetadata: null,
105+
sdl: null,
106+
supergraph: null,
107+
tags: null,
108+
includesNetworkError: true,
109+
includesException: false,
110+
errors: [
111+
{
112+
message: 'The schema composition timed out. Please try again.',
113+
source: 'composition',
114+
},
115+
],
116+
} satisfies ComposeAndValidateResult;
117+
}
118+
throw err;
93119
} finally {
94120
timeoutAbortSignal.removeEventListener('abort', onTimeout);
95121
this.incomingRequestAbortSignal.removeEventListener('abort', onIncomingRequestAbort);

packages/services/api/src/modules/schema/providers/orchestrators/stitching.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,12 @@ import { abortSignalAny } from '@graphql-hive/signal';
33
import type { SchemaBuilderApi } from '@hive/schema';
44
import { traceFn } from '@hive/service-common';
55
import { createTRPCProxyClient, httpLink } from '@trpc/client';
6-
import { Orchestrator, ProjectType, SchemaObject } from '../../../../shared/entities';
6+
import {
7+
ComposeAndValidateResult,
8+
Orchestrator,
9+
ProjectType,
10+
SchemaObject,
11+
} from '../../../../shared/entities';
712
import { Logger } from '../../../shared/providers/logger';
813
import type { SchemaServiceConfig } from './tokens';
914
import { SCHEMA_SERVICE_CONFIG } from './tokens';
@@ -84,6 +89,27 @@ export class StitchingOrchestrator implements Orchestrator {
8489
},
8590
);
8691
return result;
92+
} catch (err) {
93+
// In case of a timeout error we return something the user can process
94+
if (timeoutAbortSignal.reason) {
95+
return {
96+
contracts: null,
97+
metadataAttributes: null,
98+
schemaMetadata: null,
99+
sdl: null,
100+
supergraph: null,
101+
tags: null,
102+
includesNetworkError: true,
103+
includesException: false,
104+
errors: [
105+
{
106+
message: 'The schema composition timed out. Please try again.',
107+
source: 'composition',
108+
},
109+
],
110+
} satisfies ComposeAndValidateResult;
111+
}
112+
throw err;
87113
} finally {
88114
timeoutAbortSignal.removeEventListener('abort', onTimeout);
89115
this.incomingRequestAbortSignal.removeEventListener('abort', onIncomingRequestAbort);

packages/services/api/src/modules/schema/providers/registry-checks.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,8 @@ export class RegistryChecks {
283283
// Federation 1 apparently has SDL and validation errors at the same time.
284284
fullSchemaSdl: result.sdl,
285285
contracts: result.contracts?.map(mapContract) ?? null,
286+
includesNetworkError: result.includesNetworkError ?? false,
287+
includesException: result.includesException ?? false,
286288
},
287289
} satisfies CheckResult;
288290
}

0 commit comments

Comments
 (0)