Skip to content

Commit

Permalink
enhance(@graphql-hive/yoga): improvements with Yoga's features
Browse files Browse the repository at this point in the history
  • Loading branch information
ardatan committed Dec 13, 2024
1 parent cffd08a commit dbddddf
Show file tree
Hide file tree
Showing 5 changed files with 266 additions and 96 deletions.
12 changes: 12 additions & 0 deletions .changeset/spotty-toes-help.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
'@graphql-hive/yoga': patch
---

Align with the latest yoga;

- Remove \`tiny-lru\` dependency, and use `createLruCache` from `graphql-yoga`
- Use Explicit Resource Management of GraphQL Yoga plugins for disposal which already respect Node.js process termination
- Use \`context.waitUntil\` which is handled by the environment automatically, if not `@whatwg-node/server` already takes care of it with above.
- Respect the given `fetchAPI` by GraphQL Yoga(might be Hive Gateway) for the `fetch` function
- Avoid using `async/await` for performance reasons, because in case of cache-hit, it returns synchronously
- Respect Yoga's \`logger\` for logging
4 changes: 2 additions & 2 deletions packages/libraries/core/src/client/persisted-documents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export function createPersistedDocuments(
logger: Logger;
},
): null | {
resolve(documentId: string): Promise<string | null>;
resolve(documentId: string): Promise<string | null> | string;
allowArbitraryDocuments(context: { headers?: HeadersObject }): PromiseOrValue<boolean>;
} {
const persistedDocumentsCache = LRU<string>(config.cache ?? 10_000);
Expand All @@ -32,7 +32,7 @@ export function createPersistedDocuments(
const fetchCache = new Map<string, Promise<string | null>>();

/** Batch load a persisted documents */
async function loadPersistedDocument(documentId: string) {
function loadPersistedDocument(documentId: string) {
const document = persistedDocumentsCache.get(documentId);
if (document) {
return document;
Expand Down
7 changes: 3 additions & 4 deletions packages/libraries/yoga/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,11 @@
},
"peerDependencies": {
"graphql": "^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0",
"graphql-yoga": "^5.9.0"
"graphql-yoga": "^5.10.4"
},
"dependencies": {
"@graphql-hive/core": "workspace:*",
"@graphql-yoga/plugin-persisted-operations": "^3.9.0",
"tiny-lru": "8.0.2"
"@graphql-yoga/plugin-persisted-operations": "^3.9.0"
},
"devDependencies": {
"@graphql-tools/schema": "10.0.8",
Expand All @@ -59,7 +58,7 @@
"@graphql-yoga/plugin-response-cache": "3.9.0",
"@whatwg-node/fetch": "0.9.22",
"graphql-ws": "5.16.0",
"graphql-yoga": "5.9.0",
"graphql-yoga": "5.10.5",
"nock": "14.0.0-beta.7",
"vitest": "2.0.5",
"ws": "8.18.0"
Expand Down
136 changes: 74 additions & 62 deletions packages/libraries/yoga/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { DocumentNode, ExecutionArgs, GraphQLError, GraphQLSchema, Kind, parse } from 'graphql';
import type { GraphQLParams, Plugin } from 'graphql-yoga';
import LRU from 'tiny-lru';
import { createLRUCache, type GraphQLParams, type Plugin, type YogaLogger, mapMaybePromise, DisposableSymbols } from 'graphql-yoga';
import {
autoDisposeSymbol,
CollectUsageCallback,
Expand Down Expand Up @@ -42,31 +41,30 @@ export function createHive(clientOrOptions: HivePluginOptions) {
export function useHive(clientOrOptions: HiveClient): Plugin;
export function useHive(clientOrOptions: HivePluginOptions): Plugin;
export function useHive(clientOrOptions: HiveClient | HivePluginOptions): Plugin {
const hive = isHiveClient(clientOrOptions) ? clientOrOptions : createHive(clientOrOptions);

void hive.info();

if (hive[autoDisposeSymbol]) {
if (global.process) {
const signals = Array.isArray(hive[autoDisposeSymbol])
? hive[autoDisposeSymbol]
: ['SIGINT', 'SIGTERM'];
for (const signal of signals) {
process.once(signal, () => hive.dispose());
}
} else {
console.error(
'It seems that GraphQL Hive is not being executed in Node.js. ' +
'Please attempt manual client disposal and use autoDispose: false option.',
);
}
}

const parsedDocumentCache = LRU<DocumentNode>(10_000);
const parsedDocumentCache = createLRUCache<DocumentNode>();
let latestSchema: GraphQLSchema | null = null;
const contextualCache = new WeakMap<object, CacheRecord>();

let hive: HiveClient;
let logger: YogaLogger;
return {
onYogaInit({ yoga }) {
logger = yoga.logger;
hive = isHiveClient(clientOrOptions) ? clientOrOptions : createHive({
...clientOrOptions,
agent: clientOrOptions.agent ? {
logger: yoga.logger,
...clientOrOptions.agent,
__testing: {
...clientOrOptions.agent.__testing,
// Hive Plugin should respect the given FetchAPI, note that this is not `yoga.fetch`
fetch: yoga.fetchAPI.fetch,
},
} : undefined,
});
void hive.info();
},
onSchemaChange({ schema }) {
hive.reportSchema({ schema });
latestSchema = schema;
Expand Down Expand Up @@ -103,13 +101,15 @@ export function useHive(clientOrOptions: HiveClient | HivePluginOptions): Plugin
record.executionArgs = args;

if (!isAsyncIterable(result)) {
void record.callback(
{
...record.executionArgs,
document: record.parsedDocument ?? record.executionArgs.document,
},
result,
record.experimental__documentId,
args.contextValue.waitUntil(
record.callback(
{
...record.executionArgs,
document: record.parsedDocument ?? record.executionArgs.document,
},
result,
record.experimental__documentId,
)
);
return;
}
Expand All @@ -124,10 +124,12 @@ export function useHive(clientOrOptions: HiveClient | HivePluginOptions): Plugin
errors.push(...ctx.result.errors);
},
onEnd() {
void record.callback(
args,
errors.length ? { errors } : {},
record.experimental__documentId,
args.contextValue.waitUntil(
record.callback(
args,
errors.length ? { errors } : {},
record.experimental__documentId,
)
);
},
};
Expand All @@ -147,13 +149,16 @@ export function useHive(clientOrOptions: HiveClient | HivePluginOptions): Plugin
},
};
},
onResultProcess(context) {
const record = contextualCache.get(context.serverContext);
onResultProcess({
serverContext,
result,
}) {
const record = contextualCache.get(serverContext);

if (
!record ||
Array.isArray(context.result) ||
isAsyncIterable(context.result) ||
Array.isArray(result) ||
isAsyncIterable(result) ||
record.executionArgs
) {
return;
Expand All @@ -163,26 +168,28 @@ export function useHive(clientOrOptions: HiveClient | HivePluginOptions): Plugin
if (
record.paramsArgs.query &&
latestSchema &&
Symbol.for('servedFromResponseCache') in context.result
Symbol.for('servedFromResponseCache') in result
) {
try {
let document = parsedDocumentCache.get(record.paramsArgs.query);
if (document === undefined) {
document = parse(record.paramsArgs.query);
parsedDocumentCache.set(record.paramsArgs.query, document);
}
void record.callback(
{
document,
schema: latestSchema,
variableValues: record.paramsArgs.variables,
operationName: record.paramsArgs.operationName,
},
context.result,
record.experimental__documentId,
serverContext.waitUntil(
record.callback(
{
document,
schema: latestSchema,
variableValues: record.paramsArgs.variables,
operationName: record.paramsArgs.operationName,
},
result,
record.experimental__documentId,
)
);
} catch (err) {
console.error(err);
logger.error(err);
}
}
},
Expand All @@ -206,21 +213,21 @@ export function useHive(clientOrOptions: HiveClient | HivePluginOptions): Plugin

return null;
},
async getPersistedOperation(key, request, context) {
const document = await experimental__persistedDocuments.resolve(key);
// after we resolve the document we need to update the cache record to contain the resolved document
if (document) {
const record = contextualCache.get(context);
if (record) {
record.experimental__documentId = key;
record.paramsArgs = {
...record.paramsArgs,
query: document,
};
getPersistedOperation(key, _request, context) {
return mapMaybePromise(experimental__persistedDocuments.resolve(key), document => {
// after we resolve the document we need to update the cache record to contain the resolved document
if (document) {
const record = contextualCache.get(context);
if (record) {
record.experimental__documentId = key;
record.paramsArgs = {
...record.paramsArgs,
query: document,
};
}
}
}

return document;
return document;
}) as Promise<string | null>;
},
allowArbitraryOperations(request) {
return experimental__persistedDocuments.allowArbitraryDocuments({
Expand All @@ -247,5 +254,10 @@ export function useHive(clientOrOptions: HiveClient | HivePluginOptions): Plugin
}),
);
},
[DisposableSymbols.asyncDispose]() {
if (hive[autoDisposeSymbol]) {
return hive.dispose();
}
}
};
}
Loading

0 comments on commit dbddddf

Please sign in to comment.