mirror of
https://github.com/vale981/apollo-server
synced 2025-03-05 09:41:40 -05:00
Shift the burden of object approximation into the ApolloServerBase
class.
The implementation of object-size approximation which is used for cache eviction purposes in the `InMemoryLRUCache` implementation (via `lru-cache`) was a short-term location for extensible logic which is better located within `ApolloServerBase`. This is particularly important since future logic may necessitate knowing or understanding the current size (roughly, memory usage) of the in-memory storage. Effective immediately, this adds support for providing a `dispose` function which is called when an object is purged from the cache to make room for another.
This commit is contained in:
parent
0879a1267e
commit
be7162094c
3 changed files with 34 additions and 22 deletions
|
@ -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<V = string> implements KeyValueCache<V> {
|
||||
private store: LRU.Cache<string, V>;
|
||||
|
||||
// FIXME: Define reasonable default max size of the cache
|
||||
constructor({ maxSize = Infinity }: { maxSize?: number } = {}) {
|
||||
constructor({
|
||||
maxSize = Infinity,
|
||||
sizeCalculator = defaultLengthCalculation,
|
||||
onDispose,
|
||||
}: {
|
||||
maxSize?: number;
|
||||
sizeCalculator?: (value: V, key: string) => number;
|
||||
onDispose?: (key: string, value: V) => void;
|
||||
} = {}) {
|
||||
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: sizeCalculator,
|
||||
dispose: onDispose,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -43,4 +44,7 @@ export class InMemoryLRUCache<V = string> implements KeyValueCache<V> {
|
|||
async flush(): Promise<void> {
|
||||
this.store.reset();
|
||||
}
|
||||
async getTotalSize() {
|
||||
return this.store.length;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -93,6 +93,10 @@ function getEngineServiceId(engine: Config['engine']): string | undefined {
|
|||
const forbidUploadsForTesting =
|
||||
process && process.env.NODE_ENV === 'test' && !supportsUploadsInNode;
|
||||
|
||||
function approximateObjectSize<T>(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<DocumentNode>({
|
||||
// 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,
|
||||
sizeCalculator: approximateObjectSize,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -573,7 +573,10 @@ describe('runQuery', () => {
|
|||
approximateObjectSize(parse(querySmall1)) +
|
||||
approximateObjectSize(parse(querySmall2));
|
||||
|
||||
const documentStore = new InMemoryLRUCache<DocumentNode>({ maxSize });
|
||||
const documentStore = new InMemoryLRUCache<DocumentNode>({
|
||||
maxSize,
|
||||
sizeCalculator: approximateObjectSize,
|
||||
});
|
||||
|
||||
await runRequest({ plugins, documentStore, queryString: querySmall1 });
|
||||
expect(parsingDidStart.mock.calls.length).toBe(1);
|
||||
|
|
Loading…
Add table
Reference in a new issue