Skip to content

Commit 7106e4a

Browse files
authored
Cache fixes (#140)
* fixed cache issues with additionalResolvers update toolkit to fix scalars promise wrapping * upgrade to latest toolkit
1 parent 98d8c67 commit 7106e4a

File tree

13 files changed

+272
-187
lines changed

13 files changed

+272
-187
lines changed

examples/location-weather/.meshrc.yaml

+9-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,15 @@ transforms:
1818
todayForecast: Forecast!
1919
}
2020
- cache:
21-
- field: Query.*
21+
# Geo data doesn't change frequntly, so we can cache it forever
22+
- field: Query.*
23+
# Forcast data might change, so we can cache it for 1 hour only
24+
- field: PopulatedPlaceSummary.dailyForecast
25+
invalidate:
26+
ttl: 3600
27+
- field: PopulatedPlaceSummary.todayForecast
28+
invalidate:
29+
ttl: 3600
2230
require:
2331
- ts-node/register/transpile-only
2432
additionalResolvers:

packages/cli/package.json

+4-4
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,10 @@
4040
"@graphql-codegen/typescript-operations": "1.13.1",
4141
"@graphql-codegen/typescript-resolvers": "1.13.1",
4242
"@graphql-mesh/runtime": "0.0.16",
43-
"@graphql-toolkit/code-file-loader": "0.9.9",
44-
"@graphql-toolkit/common": "0.9.9",
45-
"@graphql-toolkit/core": "0.9.9",
46-
"@graphql-toolkit/graphql-file-loader": "0.9.9",
43+
"@graphql-toolkit/code-file-loader": "0.9.10",
44+
"@graphql-toolkit/common": "0.9.10",
45+
"@graphql-toolkit/core": "0.9.10",
46+
"@graphql-toolkit/graphql-file-loader": "0.9.10",
4747
"apollo-server": "2.11.0",
4848
"chalk": "3.0.0",
4949
"change-case": "4.1.1",

packages/handlers/postgraphile/src/index.ts

+12-14
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,15 @@ const handler: MeshHandlerLibrary<
99
> = {
1010
async getMeshSource({ config, hooks }) {
1111
const mapsToPatch: Array<Map<GraphQLNamedType, any>> = [];
12-
const pgClient = new Client(
13-
{
14-
...config?.pool
15-
? {
16-
...config?.pool
17-
}
18-
: {
19-
connectionString: config.connectionString
20-
}
21-
}
22-
);
12+
const pgClient = new Client({
13+
...(config?.pool
14+
? {
15+
...config?.pool
16+
}
17+
: {
18+
connectionString: config.connectionString
19+
})
20+
});
2321
await pgClient.connect();
2422
(pgClient as any)['release'] = () => {};
2523
const graphileSchema = await createPostGraphileSchema(
@@ -44,7 +42,7 @@ const handler: MeshHandlerLibrary<
4442

4543
// This is a workaround because the final schema changes, and we need to make sure
4644
// the new types are there on those maps, otherwise postgraphile will fail to build queries
47-
hooks.on('schemaReady', finalSchema => {
45+
hooks.on('schemaReady', ({ schema: finalSchema }) => {
4846
const typeMap = finalSchema.getTypeMap();
4947

5048
for (const [typeName, type] of Object.entries(typeMap)) {
@@ -60,10 +58,10 @@ const handler: MeshHandlerLibrary<
6058
});
6159

6260
hooks.on('destroy', () => pgClient.end());
63-
61+
6462
return {
6563
schema: graphileSchema,
66-
contextBuilder: async () => ({ pgClient }),
64+
contextBuilder: async () => ({ pgClient })
6765
};
6866
}
6967
};

packages/runtime/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,13 @@
2525
"definition": "dist/index.d.ts"
2626
},
2727
"dependencies": {
28-
"@graphql-toolkit/common": "0.9.10-alpha-f92a59f.16",
28+
"@graphql-toolkit/common": "0.9.10",
2929
"@graphql-mesh/cache-inmemory-lru": "0.0.16",
3030
"@graphql-mesh/extend": "0.0.16",
3131
"@graphql-mesh/prefix": "0.0.16",
3232
"@graphql-mesh/rename": "0.0.16",
3333
"@graphql-mesh/types": "0.0.16",
34-
"@graphql-toolkit/schema-merging": "0.9.9",
34+
"@graphql-toolkit/schema-merging": "0.9.10",
3535
"apollo-server": "2.11.0",
3636
"chalk": "3.0.0",
3737
"cosmiconfig": "6.0.0",

packages/runtime/src/get-mesh.ts

+10-3
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
ensureDocumentNode
1515
} from './utils';
1616
import { Hooks, KeyValueCache } from '@graphql-mesh/types';
17-
import { addResolveFunctionsToSchema } from 'graphql-tools-fork';
17+
import { addResolversToSchema } from 'graphql-tools-fork';
1818
import { InMemoryLRUCache } from '@graphql-mesh/cache-inmemory-lru';
1919
import {
2020
applyResolversHooksToSchema,
@@ -91,7 +91,7 @@ export async function getMesh(
9191
}
9292

9393
if (options.additionalResolvers) {
94-
unifiedSchema = addResolveFunctionsToSchema({
94+
unifiedSchema = addResolversToSchema({
9595
resolvers: applyResolversHooksToResolvers(
9696
options.additionalResolvers,
9797
hooks
@@ -100,7 +100,14 @@ export async function getMesh(
100100
});
101101
}
102102

103-
hooks.emit('schemaReady', unifiedSchema);
103+
hooks.emit('schemaReady', {
104+
schema: unifiedSchema,
105+
applyResolvers: modifiedResolvers => {
106+
if (modifiedResolvers) {
107+
unifiedSchema = addResolversToSchema(unifiedSchema, modifiedResolvers);
108+
}
109+
}
110+
});
104111

105112
async function buildMeshContext(
106113
initialContextValue?: any

packages/runtime/src/resolvers-hooks.ts

+28-16
Original file line numberDiff line numberDiff line change
@@ -11,31 +11,43 @@ export function applyResolversHooksToResolvers(
1111
hooks: Hooks
1212
): IResolvers {
1313
return composeResolvers(resolvers, {
14-
'*.*': originalResolver => (
14+
'*.*': originalResolver => async (
1515
parentOrKind,
1616
args,
1717
context,
1818
info: GraphQLResolveInfo
1919
) => {
20-
// In case this is a scalar resolver, just run it as-is, without wrapping
21-
// TODO: Fix in graphql-toolkit
22-
if (parentOrKind && parentOrKind.kind) {
23-
return originalResolver(parentOrKind, args, context, info);
24-
}
20+
hooks.emit('resolverCalled', {
21+
parent: parentOrKind,
22+
args,
23+
context,
24+
info
25+
});
2526

26-
hooks.emit('resolverCalled', { parent: parentOrKind, args, context, info });
27+
try {
28+
const result = await originalResolver(
29+
parentOrKind,
30+
args,
31+
context,
32+
info
33+
);
2734

28-
return Promise.resolve(originalResolver(parentOrKind, args, context, info))
29-
.then(result => {
30-
hooks.emit('resolverDone', { parent: parentOrKind, args, context, info }, result);
35+
hooks.emit(
36+
'resolverDone',
37+
{ parent: parentOrKind, args, context, info },
38+
result
39+
);
3140

32-
return result;
33-
})
34-
.catch(e => {
35-
hooks.emit('resolverError', { parent: parentOrKind, args, context, info }, e);
41+
return result;
42+
} catch (e) {
43+
hooks.emit(
44+
'resolverError',
45+
{ parent: parentOrKind, args, context, info },
46+
e
47+
);
3648

37-
throw e;
38-
});
49+
throw e;
50+
}
3951
}
4052
});
4153
}

packages/runtime/src/utils.ts

+10-2
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,17 @@ export async function applySchemaTransformations(
2727
let resultSchema: GraphQLSchema = schema;
2828

2929
for (const transformation of transformations) {
30-
resultSchema = await transformation.transformFn({
30+
const transformedSchema = await transformation.transformFn({
3131
apiName: name,
3232
schema: resultSchema,
3333
config: transformation.config,
3434
cache,
3535
hooks
3636
});
37+
38+
if (transformedSchema) {
39+
resultSchema = transformedSchema;
40+
}
3741
}
3842

3943
return resultSchema;
@@ -48,12 +52,16 @@ export async function applyOutputTransformations(
4852
let resultSchema: GraphQLSchema = schema;
4953

5054
for (const transformation of transformations) {
51-
resultSchema = await transformation.transformFn({
55+
const transformedSchema = await transformation.transformFn({
5256
schema: resultSchema,
5357
config: transformation.config,
5458
cache,
5559
hooks
5660
});
61+
62+
if (transformedSchema) {
63+
resultSchema = transformedSchema;
64+
}
5765
}
5866

5967
return resultSchema;

packages/transforms/cache/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
"object-hash": "2.0.3",
2727
"date-fns": "2.11.0",
2828
"string-interpolation": "1.2.5",
29-
"@graphql-toolkit/common": "0.9.9",
29+
"@graphql-toolkit/common": "0.9.10",
3030
"@graphql-mesh/types": "0.0.16",
3131
"graphql-tools-fork": "8.10.0"
3232
},

packages/transforms/cache/src/index.ts

+63-64
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import { addResolveFunctionsToSchema } from 'graphql-tools-fork';
2-
import { GraphQLSchema, GraphQLResolveInfo } from 'graphql';
1+
import { GraphQLResolveInfo } from 'graphql';
32
import { TransformFn, YamlConfig } from '@graphql-mesh/types';
43
import {
54
composeResolvers,
@@ -26,77 +25,77 @@ interpolator.registerModifier('hash', (value: any) =>
2625
);
2726

2827
export const cacheTransform: TransformFn<YamlConfig.CacheTransformConfig[]> = async ({
29-
schema,
3028
config,
3129
cache,
3230
hooks
33-
}): Promise<GraphQLSchema> => {
34-
const sourceResolvers = extractResolversFromSchema(schema);
35-
const compositions: ResolversComposerMapping = {};
36-
37-
for (const cacheItem of config) {
38-
const effectingOperations = cacheItem.invalidate?.effectingOperations || [];
39-
40-
if (effectingOperations.length > 0) {
41-
hooks.on('resolverDone', async (resolverInfo, result) => {
42-
const effectingRule = effectingOperations.find(
43-
o =>
44-
o.operation ===
45-
`${resolverInfo.info.parentType.name}.${resolverInfo.info.fieldName}`
46-
);
47-
48-
if (effectingRule) {
31+
}): Promise<void> => {
32+
// We need to use `schemaReady` hook and not the schema directly because we need to make sure to run cache after all
33+
// other transformations are done, and to make sure that custom resolve are already loaded and merged into the schema
34+
hooks.on('schemaReady', ({ schema, applyResolvers }) => {
35+
const sourceResolvers = extractResolversFromSchema(schema);
36+
const compositions: ResolversComposerMapping = {};
37+
38+
for (const cacheItem of config) {
39+
const effectingOperations =
40+
cacheItem.invalidate?.effectingOperations || [];
41+
42+
if (effectingOperations.length > 0) {
43+
hooks.on('resolverDone', async (resolverInfo, result) => {
44+
const effectingRule = effectingOperations.find(
45+
o =>
46+
o.operation ===
47+
`${resolverInfo.info.parentType.name}.${resolverInfo.info.fieldName}`
48+
);
49+
50+
if (effectingRule) {
51+
const cacheKey = computeCacheKey({
52+
keyStr: effectingRule.matchKey,
53+
args: resolverInfo.args,
54+
info: resolverInfo.info
55+
});
56+
57+
await cache.delete(cacheKey);
58+
}
59+
});
60+
}
61+
62+
compositions[cacheItem.field] = <ResolversComposition>(
63+
(originalResolver => async (
64+
root: any,
65+
args: any,
66+
context: any,
67+
info: GraphQLResolveInfo
68+
) => {
4969
const cacheKey = computeCacheKey({
50-
keyStr: effectingRule.matchKey,
51-
args: resolverInfo.args,
52-
info: resolverInfo.info
70+
keyStr: cacheItem.cacheKey,
71+
args,
72+
info
5373
});
5474

55-
await cache.delete(cacheKey);
56-
}
57-
});
58-
}
59-
60-
compositions[cacheItem.field] = <ResolversComposition>(
61-
(originalResolver => async (
62-
root: any,
63-
args: any,
64-
context: any,
65-
info: GraphQLResolveInfo
66-
) => {
67-
const cacheKey = computeCacheKey({
68-
keyStr: cacheItem.cacheKey,
69-
args,
70-
info
71-
});
72-
73-
const cachedValue = await cache.get(cacheKey);
75+
const cachedValue = await cache.get(cacheKey);
7476

75-
if (cachedValue) {
76-
return cachedValue;
77-
}
77+
if (cachedValue) {
78+
return cachedValue;
79+
}
7880

79-
const resolverResult = await originalResolver(
80-
root,
81-
args,
82-
context,
83-
info
84-
);
81+
const resolverResult = await originalResolver(
82+
root,
83+
args,
84+
context,
85+
info
86+
);
8587

86-
await cache.set(cacheKey, resolverResult, {
87-
ttl: cacheItem.invalidate?.ttl || undefined
88-
});
89-
90-
return resolverResult;
91-
})
92-
);
93-
}
88+
await cache.set(cacheKey, resolverResult, {
89+
ttl: cacheItem.invalidate?.ttl || undefined
90+
});
9491

95-
const wrappedResolvers = composeResolvers(sourceResolvers, compositions);
92+
return resolverResult;
93+
})
94+
);
95+
}
9696

97-
return addResolveFunctionsToSchema({
98-
schema,
99-
resolvers: wrappedResolvers
97+
const wrappedResolvers = composeResolvers(sourceResolvers, compositions);
98+
applyResolvers(wrappedResolvers);
10099
});
101100
};
102101

@@ -106,8 +105,8 @@ export function computeCacheKey(options: {
106105
info: GraphQLResolveInfo;
107106
}): string {
108107
const argsHash = options.args
109-
? objectHash(options.args, { ignoreUnknown: true })
110-
: '';
108+
? objectHash(options.args, { ignoreUnknown: true })
109+
: '';
111110

112111
if (!options.keyStr) {
113112
return `${options.info.parentType.name}-${options.info.fieldName}-${argsHash}`;

0 commit comments

Comments
 (0)