Skip to content

Commit edbc829

Browse files
Fix double instantiation of DataSources (#3388)
Currently, buildService is being called twice for each service on gateway startup. This is because we're caching the datasources, but failing to utilize the cache. This commit places the creation and caching of datasources in one place, and indicates this more clearly with an updated function name. Fixes #3367
1 parent da8575e commit edbc829

File tree

3 files changed

+37
-7
lines changed

3 files changed

+37
-7
lines changed

packages/apollo-gateway/CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44

55
> The changes noted within this `vNEXT` section have not been released yet. New PRs and commits which introduce changes should include an entry in this `vNEXT` section as part of their development. When a release is being prepared, a new header will be (manually) created below and the the appropriate changes within that release will be moved into the new section.
66
7-
* Add export for experimental observability functions types.
7+
* Add export for experimental observability functions types. [#3371](https://github.com/apollographql/apollo-server/pull/3371)
8+
* Fix double instantiation of DataSources [#3388](https://github.com/apollographql/apollo-server/pull/3388)
89

910
# v0.10.2
1011

packages/apollo-gateway/src/__tests__/gateway/buildService.test.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import gql from 'graphql-tag';
2-
import { print } from 'graphql';
32
import { fetch } from '__mocks__/apollo-server-env';
43
import { createTestClient } from 'apollo-server-testing';
54
import { ApolloServerBase as ApolloServer } from 'apollo-server-core';
6-
import { buildFederatedSchema } from '@apollo/federation';
75

86
import { RemoteGraphQLDataSource } from '../../datasources/RemoteGraphQLDataSource';
97
import { ApolloGateway } from '../../';
@@ -18,6 +16,27 @@ beforeEach(() => {
1816
fetch.mockReset();
1917
});
2018

19+
it('calls buildService only once per service', async () => {
20+
fetch.mockJSONResponseOnce({
21+
data: { _service: { sdl: `extend type Query { thing: String }` } },
22+
});
23+
24+
const buildServiceSpy = jest.fn(() => {
25+
return new RemoteGraphQLDataSource({
26+
url: 'https://api.example.com/foo',
27+
});
28+
});
29+
30+
const gateway = new ApolloGateway({
31+
serviceList: [{ name: 'foo', url: 'https://api.example.com/foo' }],
32+
buildService: buildServiceSpy
33+
});
34+
35+
await gateway.load();
36+
37+
expect(buildServiceSpy).toHaveBeenCalledTimes(1);
38+
});
39+
2140
it('correctly passes the context from ApolloServer to datasources', async () => {
2241
const gateway = new ApolloGateway({
2342
localServiceList: [accounts, books, inventory, product, reviews],

packages/apollo-gateway/src/index.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -385,24 +385,34 @@ export class ApolloGateway implements GraphQLService {
385385
this.pollingTimer.unref();
386386
}
387387

388-
private createDataSource(
388+
private createAndCacheDataSource(
389389
serviceDef: ServiceEndpointDefinition,
390390
): GraphQLDataSource {
391+
// If the DataSource has already been created, early return
392+
if (this.serviceMap[serviceDef.name])
393+
return this.serviceMap[serviceDef.name];
394+
391395
if (!serviceDef.url && !isLocalConfig(this.config)) {
392396
throw new Error(
393397
`Service definition for service ${serviceDef.name} is missing a url`,
394398
);
395399
}
396-
return this.config.buildService
400+
401+
const dataSource = this.config.buildService
397402
? this.config.buildService(serviceDef)
398403
: new RemoteGraphQLDataSource({
399404
url: serviceDef.url,
400405
});
406+
407+
// Cache the created DataSource
408+
this.serviceMap[serviceDef.name] = dataSource;
409+
410+
return dataSource;
401411
}
402412

403413
protected createServices(services: ServiceEndpointDefinition[]) {
404414
for (const serviceDef of services) {
405-
this.serviceMap[serviceDef.name] = this.createDataSource(serviceDef);
415+
this.createAndCacheDataSource(serviceDef);
406416
}
407417
}
408418

@@ -416,7 +426,7 @@ export class ApolloGateway implements GraphQLService {
416426
if (isRemoteConfig(config)) {
417427
const serviceList = config.serviceList.map(serviceDefinition => ({
418428
...serviceDefinition,
419-
dataSource: this.createDataSource(serviceDefinition),
429+
dataSource: this.createAndCacheDataSource(serviceDefinition),
420430
}));
421431

422432
return getServiceDefinitionsFromRemoteEndpoint({

0 commit comments

Comments
 (0)