diff --git a/packages/apollo-server-caching/src/InMemoryLRUCache.ts b/packages/apollo-server-caching/src/InMemoryLRUCache.ts index 2f6d1085923..b6cf3ad1ffc 100644 --- a/packages/apollo-server-caching/src/InMemoryLRUCache.ts +++ b/packages/apollo-server-caching/src/InMemoryLRUCache.ts @@ -1,32 +1,33 @@ import LRU from 'lru-cache'; import { KeyValueCache } from './KeyValueCache'; +function defaultLengthCalculation(item: any) { + if (Array.isArray(item) || typeof item === 'string') { + return item.length; + } + + // Go with the lru-cache default "naive" size, in lieu anything better: + // https://github.com/isaacs/node-lru-cache/blob/a71be6cd/index.js#L17 + return 1; +} + export class InMemoryLRUCache implements KeyValueCache { private store: LRU.Cache; // FIXME: Define reasonable default max size of the cache - constructor({ maxSize = Infinity }: { maxSize?: number } = {}) { + constructor({ + maxSize = Infinity, + length = defaultLengthCalculation, + dispose, + }: { + maxSize?: number; + length?: (value: V, key: string) => number; + dispose?: (key: string, value: V) => any; + } = {}) { this.store = new LRU({ max: maxSize, - length(item) { - if (Array.isArray(item) || typeof item === 'string') { - return item.length; - } - - // If it's an object, we'll use JSON.stringify+Buffer.byteLength to - // approximate the size of what it would take to store. It's certainly - // not 100% accurate, but it should be a fast implementation which - // doesn't require bringing in other dependencies or logic which we need - // to maintain. In the future, we might consider something like: - // npm.im/object-sizeof, but this should be sufficient for now. - if (typeof item === 'object') { - return Buffer.byteLength(JSON.stringify(item), 'utf8'); - } - - // Go with the lru-cache default "naive" size, in lieu anything better: - // https://github.com/isaacs/node-lru-cache/blob/a71be6cd/index.js#L17 - return 1; - }, + length, + dispose, }); } diff --git a/packages/apollo-server-core/src/ApolloServer.ts b/packages/apollo-server-core/src/ApolloServer.ts index 5f1db09fd13..014e4cbeb1e 100644 --- a/packages/apollo-server-core/src/ApolloServer.ts +++ b/packages/apollo-server-core/src/ApolloServer.ts @@ -93,6 +93,10 @@ function getEngineServiceId(engine: Config['engine']): string | undefined { const forbidUploadsForTesting = process && process.env.NODE_ENV === 'test' && !supportsUploadsInNode; +function approximateObjectSize(obj: T): number { + return Buffer.byteLength(JSON.stringify(obj), 'utf8'); +} + export class ApolloServerBase { public subscriptionsPath?: string; public graphqlPath: string = '/graphql'; @@ -496,13 +500,14 @@ export class ApolloServerBase { } private initializeDocumentStore(): void { - this.documentStore = new InMemoryLRUCache({ + this.documentStore = new InMemoryLRUCache({ // Create ~about~ a 30MiB InMemoryLRUCache. This is less than precise // since the technique to calculate the size of a DocumentNode is // only using JSON.stringify on the DocumentNode (and thus doesn't account // for unicode characters, etc.), but it should do a reasonable job at // providing a caching document store for most operations. maxSize: Math.pow(2, 20) * 30, + length: approximateObjectSize, }); } diff --git a/packages/apollo-server-core/src/__tests__/runQuery.test.ts b/packages/apollo-server-core/src/__tests__/runQuery.test.ts index c75c376c2ae..ab292003988 100644 --- a/packages/apollo-server-core/src/__tests__/runQuery.test.ts +++ b/packages/apollo-server-core/src/__tests__/runQuery.test.ts @@ -573,7 +573,10 @@ describe('runQuery', () => { approximateObjectSize(parse(querySmall1)) + approximateObjectSize(parse(querySmall2)); - const documentStore = new InMemoryLRUCache({ maxSize }); + const documentStore = new InMemoryLRUCache({ + maxSize, + length: approximateObjectSize, + }); await runRequest({ plugins, documentStore, queryString: querySmall1 }); expect(parsingDidStart.mock.calls.length).toBe(1);