Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions spec/ParseGraphQLServer.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,20 @@ describe('ParseGraphQLServer', () => {
expect(server3).not.toBe(server2);
expect(server3).toBe(server4);
});

it('should return same server reference when called 100 times in parallel', async () => {
parseGraphQLServer.server = undefined;

// Call _getServer 100 times in parallel
const promises = Array.from({ length: 100 }, () => parseGraphQLServer._getServer());
const servers = await Promise.all(promises);

// All resolved servers should be the same reference
const firstServer = servers[0];
servers.forEach((server, index) => {
expect(server).toBe(firstServer);
});
});
});

describe('_getGraphQLOptions', () => {
Expand Down
47 changes: 32 additions & 15 deletions src/GraphQL/ParseGraphQLServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,21 +97,38 @@ class ParseGraphQLServer {
if (schemaRef === newSchemaRef && this._server) {
return this._server;
}
const { schema, context } = await this._getGraphQLOptions();
const apollo = new ApolloServer({
csrfPrevention: {
// See https://www.apollographql.com/docs/router/configuration/csrf/
// needed since we use graphql upload
requestHeaders: ['X-Parse-Application-Id'],
},
introspection: this.config.graphQLPublicIntrospection,
plugins: [ApolloServerPluginCacheControlDisabled(), IntrospectionControlPlugin(this.config.graphQLPublicIntrospection)],
schema,
});
await apollo.start();
this._server = expressMiddleware(apollo, {
context,
});
// It means a parallel _getServer call is already in progress
if (this._schemaRefMutex === newSchemaRef) {
return this._server;
}
// Update the schema ref mutex to avoid parallel _getServer calls
this._schemaRefMutex = newSchemaRef;
const createServer = async () => {
try {
const { schema, context } = await this._getGraphQLOptions();
const apollo = new ApolloServer({
csrfPrevention: {
// See https://www.apollographql.com/docs/router/configuration/csrf/
// needed since we use graphql upload
requestHeaders: ['X-Parse-Application-Id'],
},
introspection: this.config.graphQLPublicIntrospection,
plugins: [ApolloServerPluginCacheControlDisabled(), IntrospectionControlPlugin(this.config.graphQLPublicIntrospection)],
schema,
});
await apollo.start();
return expressMiddleware(apollo, {
context,
});
} catch (e) {
// Reset all mutexes and forward the error
this._server = null;
this._schemaRefMutex = null;
throw e;
}
}
// Do not await so parallel request will wait the same promise ref
this._server = createServer();
return this._server;
}

Expand Down
Loading