Skip to content

Commit e0d5feb

Browse files
authored
feat(runtime): new cache related hooks (#743)
1 parent 7fbcd95 commit e0d5feb

File tree

5 files changed

+335
-2
lines changed

5 files changed

+335
-2
lines changed

.changeset/tasty-actors-marry.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
'@graphql-hive/gateway-runtime': minor
3+
---
4+
5+
New Cache related hooks;
6+
7+
`onCacheGet`: invoked when a cache get operation is performed.
8+
`onCacheMiss`: invoked when the performed get operation does not find a cache entry.
9+
`onCacheHit`: invoked when the performed get operation finds a cache entry.
10+
`onCacheGetError`: invoked when an error occurs during a cache get operation.
11+
12+
`onCacheSet`: invoked when a cache set operation is performed.
13+
`onCacheSetDone`: invoked when the performed set operation is completed.
14+
`onCacheSetError`: invoked when an error occurs during a cache set operation.
15+
16+
`onCacheDelete`: invoked when a cache delete operation is performed.
17+
`onCacheDeleteDone`: invoked when the performed delete operation is completed.
18+
`onCacheDeleteError`: invoked when an error occurs during a cache delete operation.

packages/runtime/src/createGatewayRuntime.ts

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,12 @@ import { useHmacUpstreamSignature } from '@graphql-mesh/hmac-upstream-signature'
2424
import useMeshHive from '@graphql-mesh/plugin-hive';
2525
import useMeshResponseCache from '@graphql-mesh/plugin-response-cache';
2626
import { TransportContext } from '@graphql-mesh/transport-common';
27-
import type { Logger, OnDelegateHook, OnFetchHook } from '@graphql-mesh/types';
27+
import type {
28+
KeyValueCache,
29+
Logger,
30+
OnDelegateHook,
31+
OnFetchHook,
32+
} from '@graphql-mesh/types';
2833
import {
2934
getHeadersObj,
3035
getInContextSDK,
@@ -85,6 +90,7 @@ import {
8590
UnifiedGraphSchema,
8691
} from './handleUnifiedGraphConfig';
8792
import landingPageHtml from './landing-page-html';
93+
import { useCacheDebug } from './plugins/useCacheDebug';
8894
import { useContentEncoding } from './plugins/useContentEncoding';
8995
import { useCustomAgent } from './plugins/useCustomAgent';
9096
import { useDelegationPlanDebug } from './plugins/useDelegationPlanDebug';
@@ -103,12 +109,16 @@ import type {
103109
GatewayContext,
104110
GatewayHiveCDNOptions,
105111
GatewayPlugin,
112+
OnCacheDeleteHook,
113+
OnCacheGetHook,
114+
OnCacheSetHook,
106115
UnifiedGraphConfig,
107116
} from './types';
108117
import {
109118
checkIfDataSatisfiesSelectionSet,
110119
defaultQueryText,
111120
getExecuteFnFromExecutor,
121+
wrapCacheWithHooks,
112122
} from './utils';
113123

114124
// TODO: this type export is not properly accessible from graphql-yoga
@@ -152,13 +162,24 @@ export function createGatewayRuntime<
152162
}
153163

154164
const onFetchHooks: OnFetchHook<GatewayContext>[] = [];
165+
const onCacheGetHooks: OnCacheGetHook[] = [];
166+
const onCacheSetHooks: OnCacheSetHook[] = [];
167+
const onCacheDeleteHooks: OnCacheDeleteHook[] = [];
155168
const wrappedFetchFn = wrapFetchWithHooks(onFetchHooks);
169+
const wrappedCache: KeyValueCache | undefined = config.cache
170+
? wrapCacheWithHooks({
171+
cache: config.cache,
172+
onCacheGet: onCacheGetHooks,
173+
onCacheSet: onCacheSetHooks,
174+
onCacheDelete: onCacheDeleteHooks,
175+
})
176+
: undefined;
156177

157178
const configContext: GatewayConfigContext = {
158179
fetch: wrappedFetchFn,
159180
logger,
160181
cwd: config.cwd || (typeof process !== 'undefined' ? process.cwd() : ''),
161-
cache: config.cache,
182+
cache: wrappedCache,
162183
pubsub: config.pubsub,
163184
};
164185

@@ -815,6 +836,15 @@ export function createGatewayRuntime<
815836
if (plugin.onDelegationStageExecute) {
816837
onDelegationStageExecuteHooks.push(plugin.onDelegationStageExecute);
817838
}
839+
if (plugin.onCacheGet) {
840+
onCacheGetHooks.push(plugin.onCacheGet);
841+
}
842+
if (plugin.onCacheSet) {
843+
onCacheSetHooks.push(plugin.onCacheSet);
844+
}
845+
if (plugin.onCacheDelete) {
846+
onCacheDeleteHooks.push(plugin.onCacheDelete);
847+
}
818848
}
819849
},
820850
};
@@ -1022,6 +1052,7 @@ export function createGatewayRuntime<
10221052
useSubgraphExecuteDebug(configContext),
10231053
useFetchDebug(configContext),
10241054
useDelegationPlanDebug(configContext),
1055+
useCacheDebug(configContext),
10251056
);
10261057
return 'Debug mode enabled';
10271058
});
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { Logger } from '@graphql-mesh/types';
2+
import { GatewayPlugin } from '../types';
3+
4+
export function useCacheDebug<TContext extends Record<string, any>>(opts: {
5+
logger: Logger;
6+
}): GatewayPlugin<TContext> {
7+
return {
8+
onCacheGet({ key }) {
9+
return {
10+
onCacheGetError({ error }) {
11+
const cacheGetErrorLogger = opts.logger.child('cache-get-error');
12+
cacheGetErrorLogger.error({ key, error });
13+
},
14+
onCacheHit({ value }) {
15+
const cacheHitLogger = opts.logger.child('cache-hit');
16+
cacheHitLogger.debug({ key, value });
17+
},
18+
onCacheMiss() {
19+
const cacheMissLogger = opts.logger.child('cache-miss');
20+
cacheMissLogger.debug({ key });
21+
},
22+
};
23+
},
24+
onCacheSet({ key, value, ttl }) {
25+
return {
26+
onCacheSetError({ error }) {
27+
const cacheSetErrorLogger = opts.logger.child('cache-set-error');
28+
cacheSetErrorLogger.error({ key, value, ttl, error });
29+
},
30+
onCacheSetDone() {
31+
const cacheSetDoneLogger = opts.logger.child('cache-set-done');
32+
cacheSetDoneLogger.debug({ key, value, ttl });
33+
},
34+
};
35+
},
36+
onCacheDelete({ key }) {
37+
return {
38+
onCacheDeleteError({ error }) {
39+
const cacheDeleteErrorLogger =
40+
opts.logger.child('cache-delete-error');
41+
cacheDeleteErrorLogger.error({ key, error });
42+
},
43+
onCacheDeleteDone() {
44+
const cacheDeleteDoneLogger = opts.logger.child('cache-delete-done');
45+
cacheDeleteDoneLogger.debug({ key });
46+
},
47+
};
48+
},
49+
};
50+
}

packages/runtime/src/types.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,69 @@ export type GatewayPlugin<
9292
> = YogaPlugin<Partial<TPluginContext> & GatewayContext & TContext> &
9393
UnifiedGraphPlugin<Partial<TPluginContext> & GatewayContext & TContext> & {
9494
onFetch?: OnFetchHook<Partial<TPluginContext> & GatewayContext & TContext>;
95+
onCacheGet?: OnCacheGetHook;
96+
onCacheSet?: OnCacheSetHook;
97+
onCacheDelete?: OnCacheDeleteHook;
9598
} & Partial<Disposable | AsyncDisposable>;
9699

100+
export type OnCacheGetHook = (
101+
payload: OnCacheGetHookEventPayload,
102+
) => MaybePromise<OnCacheGetHookResult | void>;
103+
104+
export interface OnCacheGetHookEventPayload {
105+
cache: KeyValueCache;
106+
key: string;
107+
ttl?: number;
108+
}
109+
110+
export interface OnCacheGetHookResult {
111+
onCacheHit?: OnCacheHitHook;
112+
onCacheMiss?: OnCacheMissHook;
113+
onCacheGetError?: OnCacheErrorHook;
114+
}
115+
116+
export type OnCacheErrorHook = (payload: OnCacheErrorHookPayload) => void;
117+
118+
export interface OnCacheErrorHookPayload {
119+
error: Error;
120+
}
121+
122+
export type OnCacheHitHook = (payload: OnCacheHitHookEventPayload) => void;
123+
export interface OnCacheHitHookEventPayload {
124+
value: any;
125+
}
126+
export type OnCacheMissHook = () => void;
127+
128+
export type OnCacheSetHook = (
129+
payload: OnCacheSetHookEventPayload,
130+
) => MaybePromise<OnCacheSetHookResult | void>;
131+
132+
export interface OnCacheSetHookResult {
133+
onCacheSetDone?: () => void;
134+
onCacheSetError?: OnCacheErrorHook;
135+
}
136+
137+
export interface OnCacheSetHookEventPayload {
138+
cache: KeyValueCache;
139+
key: string;
140+
value: any;
141+
ttl?: number;
142+
}
143+
144+
export type OnCacheDeleteHook = (
145+
payload: OnCacheDeleteHookEventPayload,
146+
) => MaybePromise<OnCacheDeleteHookResult | void>;
147+
148+
export interface OnCacheDeleteHookResult {
149+
onCacheDeleteDone?: () => void;
150+
onCacheDeleteError?: OnCacheErrorHook;
151+
}
152+
153+
export interface OnCacheDeleteHookEventPayload {
154+
cache: KeyValueCache;
155+
key: string;
156+
}
157+
97158
export interface GatewayConfigSupergraph<
98159
TContext extends Record<string, any> = Record<string, any>,
99160
> extends GatewayConfigSchemaBase<TContext> {

0 commit comments

Comments
 (0)