From ac34d0477c6cee041177c125336003aec47080df Mon Sep 17 00:00:00 2001 From: Vicary A Date: Thu, 9 May 2024 01:59:17 +0800 Subject: [PATCH] fix(package/gqty): Update all relevant temporary caches for batched queries. --- .changeset/green-plants-breathe.md | 5 +++ examples/gnt/next.config.js | 8 ++++- packages/gqty/src/Client/resolvers.ts | 49 +++++++++++++++++++-------- pnpm-lock.yaml | 7 ---- 4 files changed, 46 insertions(+), 23 deletions(-) create mode 100644 .changeset/green-plants-breathe.md diff --git a/.changeset/green-plants-breathe.md b/.changeset/green-plants-breathe.md new file mode 100644 index 000000000..14774f6e2 --- /dev/null +++ b/.changeset/green-plants-breathe.md @@ -0,0 +1,5 @@ +--- +'gqty': patch +--- + +Update all relevant temporary caches for batched queries. diff --git a/examples/gnt/next.config.js b/examples/gnt/next.config.js index 16ead183d..2bf801323 100644 --- a/examples/gnt/next.config.js +++ b/examples/gnt/next.config.js @@ -5,7 +5,13 @@ const withAnalyzer = require('@next/bundle-analyzer')({ /** @type {import('next').NextConfig} */ const nextConfig = { images: { - domains: ['rickandmortyapi.com'], + remotePatterns: [ + { + protocol: 'https', + hostname: 'rickandmortyapi.com', + pathname: '**', + }, + ], }, }; diff --git a/packages/gqty/src/Client/resolvers.ts b/packages/gqty/src/Client/resolvers.ts index 85ffda004..1a8faa9bc 100644 --- a/packages/gqty/src/Client/resolvers.ts +++ b/packages/gqty/src/Client/resolvers.ts @@ -1,3 +1,4 @@ +import { MultiDict } from 'multidict'; import { pick, type BaseGeneratedSchema, type FetchOptions } from '.'; import { createSchemaAccessor } from '../Accessor'; import { type Cache } from '../Cache'; @@ -174,7 +175,7 @@ const pendingQueries = new WeakMap>, Promise>(); export const createResolvers = ({ aliasLength, batchWindow, - cache: clientCache, + cache: targetCache, debugger: debug, depthLimit, fetchOptions, @@ -186,6 +187,11 @@ export const createResolvers = ({ schema, parentContext, }: CreateResolversOptions): Resolvers => { + // A temporary cache is created by the resolver context for no-cache policy. + // When multiple queries are batched, all corresponding temporary caches must + // be updated. Along with the target cache. + const correlatedCaches = new MultiDict, Cache>(); + const createResolver = ({ cachePolicy = defaultCachePolicy, extensions, @@ -197,7 +203,7 @@ export const createResolvers = ({ const selections = new Set(); const context = createContext({ aliasLength, - cache: clientCache, + cache: targetCache, depthLimit, cachePolicy, scalars, @@ -238,15 +244,23 @@ export const createResolvers = ({ } // Batch selections up at client level, fetch all of them "next tick". - const selectionsCacheKey = operationName ?? ''; + // 1. Query with operation names are never batched up with others. + // 2. 'no-store' queries are tracked separately because its data is not + // going into the main cache. + const selectionsCacheKey = `${operationName ?? (cachePolicy === 'no-store' ? 'no-store' : 'default')}`; const pendingSelections = addSelections( - clientCache, + targetCache, selectionsCacheKey, selections ); + // Link temporary caches together + correlatedCaches.set(pendingSelections, context.cache); + if (!pendingQueries.has(pendingSelections)) { + // [ ] debounceMicrotask + pendingQueries.set( pendingSelections, // Batching happens at the end of microtask queue @@ -260,7 +274,7 @@ export const createResolvers = ({ const uniqueSelections = new Set(); - getSelectionsSet(clientCache, selectionsCacheKey)?.forEach( + getSelectionsSet(targetCache, selectionsCacheKey)?.forEach( (selections) => { selections.forEach((selection) => { uniqueSelections.add(selection); @@ -270,7 +284,7 @@ export const createResolvers = ({ pendingQueries.delete(pendingSelections); - delSelectionSet(clientCache, selectionsCacheKey); + delSelectionSet(targetCache, selectionsCacheKey); const results = await fetchSelections(uniqueSelections, { cache: context.cache, @@ -280,13 +294,18 @@ export const createResolvers = ({ operationName, }); - updateCaches( - results, - cachePolicy !== 'no-store' && context.cache !== clientCache - ? [context.cache, clientCache] - : [context.cache], - { skipNotify: !context.notifyCacheUpdate } - ); + const targetCaches = + correlatedCaches.get(pendingSelections) ?? new Set(); + + if (cachePolicy !== 'no-store') { + targetCaches.add(targetCache); + } + + updateCaches(results, [...targetCaches], { + skipNotify: !context.notifyCacheUpdate, + }); + + correlatedCaches.delete(targetCache); return results; }) @@ -375,8 +394,8 @@ export const createResolvers = ({ } else if (data !== undefined) { updateCaches( [{ data, error, extensions }], - cachePolicy !== 'no-store' && context.cache !== clientCache - ? [context.cache, clientCache] + cachePolicy !== 'no-store' && context.cache !== targetCache + ? [context.cache, targetCache] : [context.cache], { skipNotify: !context.notifyCacheUpdate } ); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c3524261a..fd728a7e5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -801,13 +801,6 @@ importers: version: 7.8.0(eslint@8.57.0)(typescript@5.4.5) publishDirectory: dist - packages/solid: - dependencies: - gqty: - specifier: workspace:^ - version: link:../gqty/dist - publishDirectory: dist - packages/subscriptions: dependencies: isomorphic-ws: