diff --git a/src/core/server/graph/tenant/loaders/Comments.ts b/src/core/server/graph/tenant/loaders/Comments.ts index 7db234394..105374c1c 100644 --- a/src/core/server/graph/tenant/loaders/Comments.ts +++ b/src/core/server/graph/tenant/loaders/Comments.ts @@ -20,6 +20,9 @@ import { retrieveManyComments, } from "talk-server/models/comment"; import { Connection } from "talk-server/models/connection"; +import { retrieveSharedModerationQueueQueuesCounts } from "talk-server/models/story/counts/shared"; + +import { SingletonResolver } from "./util"; /** * primeCommentsFromConnection will prime a given context with the comments @@ -114,4 +117,11 @@ export default (ctx: Context) => ({ // The cursor passed here is always going to be a number. before: before as number, }).then(primeCommentsFromConnection(ctx)), + sharedModerationQueueQueuesCounts: new SingletonResolver(() => + retrieveSharedModerationQueueQueuesCounts( + ctx.mongo, + ctx.redis, + ctx.tenant.id + ) + ), }); diff --git a/src/core/server/graph/tenant/loaders/Stories.ts b/src/core/server/graph/tenant/loaders/Stories.ts index 47c946618..61857e694 100644 --- a/src/core/server/graph/tenant/loaders/Stories.ts +++ b/src/core/server/graph/tenant/loaders/Stories.ts @@ -11,8 +11,18 @@ import { findOrCreate } from "talk-server/services/stories"; import { scraper } from "talk-server/services/stories/scraper"; export default (ctx: TenantContext) => ({ - findOrCreate: (input: FindOrCreateStoryInput) => - findOrCreate(ctx.mongo, ctx.tenant, input, ctx.queue.scraper), + findOrCreate: new DataLoader( + (inputs: FindOrCreateStoryInput[]) => + Promise.all( + inputs.map(input => + findOrCreate(ctx.mongo, ctx.tenant, input, ctx.queue.scraper) + ) + ), + { + // TODO: (wyattjoh) see if there's something we can do to improve the cache key + cacheKeyFn: (input: FindOrCreateStoryInput) => `${input.id}:${input.url}`, + } + ), story: new DataLoader(ids => retrieveManyStories(ctx.mongo, ctx.tenant.id, ids) ), diff --git a/src/core/server/graph/tenant/loaders/util.ts b/src/core/server/graph/tenant/loaders/util.ts new file mode 100644 index 000000000..aaf93286a --- /dev/null +++ b/src/core/server/graph/tenant/loaders/util.ts @@ -0,0 +1,26 @@ +/** + * SingletonResolver is a cached loader for a single result. + */ +export class SingletonResolver { + private cache: Promise | null = null; + private resolver: () => Promise; + + constructor(resolver: () => Promise) { + this.resolver = resolver; + } + + public load() { + if (this.cache) { + return this.cache; + } + + const promise = this.resolver().then(result => { + return result; + }); + + // Set the promise on the cache. + this.cache = promise; + + return promise; + } +} diff --git a/src/core/server/graph/tenant/resolvers/Query.ts b/src/core/server/graph/tenant/resolvers/Query.ts index e6fec375c..1e6cadbd2 100644 --- a/src/core/server/graph/tenant/resolvers/Query.ts +++ b/src/core/server/graph/tenant/resolvers/Query.ts @@ -1,9 +1,9 @@ import { GQLQueryTypeResolver } from "talk-server/graph/tenant/schema/__generated__/types"; -import { retrieveSharedModerationQueueQueuesCounts } from "talk-server/models/story"; + import { ModerationQueuesInput } from "./ModerationQueues"; export const Query: GQLQueryTypeResolver = { - story: (source, args, ctx) => ctx.loaders.Stories.findOrCreate(args), + story: (source, args, ctx) => ctx.loaders.Stories.findOrCreate.load(args), comment: (source, { id }, ctx) => id ? ctx.loaders.Comments.comment.load(id) : null, comments: (source, args, ctx) => ctx.loaders.Comments.forFilter(args), @@ -21,10 +21,6 @@ export const Query: GQLQueryTypeResolver = { // We don't need to filter the connection, as this is tenant wide (tenant // filtering is completed at the model layer). connection: {}, - counts: await retrieveSharedModerationQueueQueuesCounts( - ctx.mongo, - ctx.redis, - ctx.tenant.id - ), + counts: await ctx.loaders.Comments.sharedModerationQueueQueuesCounts.load(), }), };