Implement a NoStore cache backend, and do not provide a cache backend by default.

Signed-off-by: Eric Peterson <ericpeterson@spotify.com>
This commit is contained in:
Eric Peterson
2021-05-09 17:52:06 +02:00
parent d6936dcbde
commit 38e943da0c
4 changed files with 60 additions and 14 deletions
-2
View File
@@ -26,8 +26,6 @@ backend:
baseUrl: http://localhost:7000
listen:
port: 7000
cache:
store: memory
database:
client: sqlite3
connection: ':memory:'
+5 -7
View File
@@ -20,6 +20,7 @@ import Keyv from 'keyv';
import KeyvMemcache from 'keyv-memcache';
import { DefaultCacheClient } from './CacheClient';
import { CacheManager } from './CacheManager';
import { NoStore } from './NoStore';
jest.createMockFromModule('keyv');
jest.mock('keyv');
@@ -104,21 +105,17 @@ describe('CacheManager', () => {
});
describe('CacheManager.forPlugin stores', () => {
it('returns memory client when no cache is configured', () => {
it('returns none client when no cache is configured', () => {
const manager = CacheManager.fromConfig(
new ConfigReader({ backend: {} }),
);
const expectedTtl = 3600;
const expectedNamespace = 'test-plugin';
manager.forPlugin(expectedNamespace).getClient({ defaultTtl: expectedTtl });
manager.forPlugin(expectedNamespace).getClient();
const cache = Keyv as unknown as jest.Mock;
const mockCalls = cache.mock.calls.splice(-1);
const callArgs = mockCalls[0];
expect(callArgs[0]).toMatchObject({
ttl: expectedTtl,
namespace: expectedNamespace,
});
expect(callArgs[0].store).toBeInstanceOf(NoStore);
});
it('returns memory client when explicitly configured', () => {
@@ -156,6 +153,7 @@ describe('CacheManager', () => {
expect(mockCacheCalls[0][0]).toMatchObject({
ttl: expectedTtl,
});
expect(mockCacheCalls[0][0].store).toBeInstanceOf(KeyvMemcache);
const memcache = KeyvMemcache as jest.Mock;
const mockMemcacheCalls = memcache.mock.calls.splice(-1);
expect(mockMemcacheCalls[0][0]).toEqual(expectedHost);
+12 -5
View File
@@ -19,11 +19,12 @@ import Keyv from 'keyv';
// @ts-expect-error
import KeyvMemcache from 'keyv-memcache';
import { DefaultCacheClient, CacheClient } from './CacheClient';
import { NoStore } from './NoStore';
import { PluginCacheManager } from './types';
/**
* Implements a Cache Manager which will automatically create new cache clients
* for plugins when requested. All requested cacheclients are created with the
* for plugins when requested. All requested cache clients are created with the
* connection details provided.
*/
export class CacheManager {
@@ -34,6 +35,7 @@ export class CacheManager {
private readonly storeFactories = {
memcache: this.getMemcacheClient,
memory: this.getMemoryClient,
none: this.getNoneClient,
};
private readonly store: keyof CacheManager['storeFactories'];
@@ -47,8 +49,8 @@ export class CacheManager {
*/
static fromConfig(config: Config): CacheManager {
// If no `backend.cache` config is provided, instantiate the CacheManager
// with a "memory" cache client.
const store = config.getOptionalString('backend.cache.store') || 'memory';
// with a "NoStore" cache client.
const store = config.getOptionalString('backend.cache.store') || 'none';
const connectionString = config.getOptionalString('backend.cache.connection') || '';
return new CacheManager(store, connectionString);
}
@@ -82,11 +84,10 @@ export class CacheManager {
}
private getMemcacheClient(pluginId: string, defaultTtl: number | undefined): Keyv {
const memcache = new KeyvMemcache(this.connection);
return new Keyv({
namespace: pluginId,
ttl: defaultTtl,
store: memcache,
store: new KeyvMemcache(this.connection),
});
}
@@ -97,4 +98,10 @@ export class CacheManager {
});
}
private getNoneClient(pluginId: string): Keyv {
return new Keyv({
namespace: pluginId,
store: new NoStore(),
})
}
}
+43
View File
@@ -0,0 +1,43 @@
/*
* Copyright 2021 Spotify AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Storage class compatible with Keyv which always results in a no-op. This is
* used when no cache store is configured in a Backstage backend instance.
*/
export class NoStore extends Map<string, any> {
clear(): void {
return;
}
delete(_key: string): boolean {
return false;
}
get(_key: string) {
return;
}
has(_key: string): boolean {
return false;
}
set(_key: string, _value: any): this {
return this;
}
}