WIP store-agnostic cache manager.
Signed-off-by: Eric Peterson <ericpeterson@spotify.com>
This commit is contained in:
Vendored
+22
@@ -68,6 +68,28 @@ export interface Config {
|
||||
connection: string | object;
|
||||
};
|
||||
|
||||
/** Cache connection configuration, select cache type using the `store` field */
|
||||
cache?:
|
||||
| {
|
||||
store: 'memory';
|
||||
}
|
||||
| {
|
||||
store: 'memcache';
|
||||
connection: {
|
||||
/**
|
||||
* An array of memcache hosts, optionally including a port number.
|
||||
* e.g. 127.0.0.1:11211
|
||||
*/
|
||||
hosts: string[];
|
||||
|
||||
/**
|
||||
* Number of milliseconds to wait before assuming there is a
|
||||
* network timeout. Defaults to 500ms.
|
||||
*/
|
||||
netTimeout?: number;
|
||||
};
|
||||
};
|
||||
|
||||
cors?: {
|
||||
origin?: string | string[];
|
||||
methods?: string | string[];
|
||||
|
||||
@@ -36,10 +36,13 @@
|
||||
"@backstage/integration": "^0.5.2",
|
||||
"@google-cloud/storage": "^5.8.0",
|
||||
"@octokit/rest": "^18.5.3",
|
||||
"@types/cache-manager": "^3.4.0",
|
||||
"@types/cors": "^2.8.6",
|
||||
"@types/dockerode": "^3.2.1",
|
||||
"@types/express": "^4.17.6",
|
||||
"archiver": "^5.0.2",
|
||||
"cache-manager": "^3.4.3",
|
||||
"cache-manager-memcached-store": "^4.0.0",
|
||||
"compression": "^1.7.4",
|
||||
"concat-stream": "^2.0.0",
|
||||
"cors": "^2.8.5",
|
||||
@@ -54,6 +57,7 @@
|
||||
"knex": "^0.95.1",
|
||||
"lodash": "^4.17.15",
|
||||
"logform": "^2.1.1",
|
||||
"memcache-pp": "^0.3.3",
|
||||
"minimatch": "^3.0.4",
|
||||
"minimist": "^1.2.5",
|
||||
"morgan": "^1.10.0",
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { Config } from '@backstage/config';
|
||||
import cacheManager from 'cache-manager';
|
||||
// @ts-expect-error
|
||||
import Memcache from 'memcache-pp';
|
||||
// @ts-expect-error
|
||||
import memcachedStore from 'cache-manager-memcached-store';
|
||||
|
||||
/**
|
||||
* A Cache Manager, which is able to retrieve a `node-cache-manager`-compliant
|
||||
* cache client, configured according to app configuration. If no cache is
|
||||
* configured, or an unknown store is provided, a no-op cache instance will be
|
||||
* returned.
|
||||
*/
|
||||
export class CacheManager {
|
||||
private readonly storeGetterMap = {
|
||||
memcache: this.getMemcacheClient,
|
||||
memory: this.getMemoryClient,
|
||||
none: this.getNoneClient,
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new CacheManager instance by reading from the `backend` config
|
||||
* section, specifically the `.cache` key.
|
||||
*
|
||||
* @param config The loaded application configuration.
|
||||
*/
|
||||
static fromConfig(config: Config): CacheManager {
|
||||
return new CacheManager(config.getConfig('backend.cache'));
|
||||
}
|
||||
|
||||
private constructor(private readonly config: Config) {}
|
||||
|
||||
getClientWithTtl(ttl: number): cacheManager.Cache {
|
||||
const store = this.config.getOptionalString(
|
||||
'store',
|
||||
) as keyof CacheManager['storeGetterMap'];
|
||||
|
||||
if (this.storeGetterMap.hasOwnProperty(store)) {
|
||||
return this.storeGetterMap[store].call(this, ttl);
|
||||
}
|
||||
return this.storeGetterMap.none.call(this, ttl);
|
||||
}
|
||||
|
||||
private getMemcacheClient(ttl: number): cacheManager.Cache {
|
||||
const hosts = this.config.getStringArray('connection.hosts');
|
||||
const netTimeout = this.config.getOptionalNumber('connection.netTimeout');
|
||||
return cacheManager.caching({
|
||||
store: memcachedStore,
|
||||
driver: Memcache,
|
||||
options: {
|
||||
hosts,
|
||||
...(netTimeout && { netTimeout }),
|
||||
},
|
||||
ttl,
|
||||
});
|
||||
}
|
||||
|
||||
private getMemoryClient(ttl: number): cacheManager.Cache {
|
||||
return cacheManager.caching({
|
||||
store: 'memory',
|
||||
ttl,
|
||||
});
|
||||
}
|
||||
|
||||
private getNoneClient(ttl: number): cacheManager.Cache {
|
||||
return cacheManager.caching({
|
||||
store: 'none',
|
||||
ttl,
|
||||
});
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export * from './CacheManager';
|
||||
@@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './cache';
|
||||
export * from './config';
|
||||
export * from './database';
|
||||
export * from './discovery';
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
import Router from 'express-promise-router';
|
||||
import {
|
||||
CacheManager,
|
||||
createServiceBuilder,
|
||||
getRootLogger,
|
||||
loadBackendConfig,
|
||||
@@ -59,11 +60,12 @@ function makeCreateEnv(config: Config) {
|
||||
root.info(`Created UrlReader ${reader}`);
|
||||
|
||||
const databaseManager = SingleConnectionDatabaseManager.fromConfig(config);
|
||||
const cache = CacheManager.fromConfig(config);
|
||||
|
||||
return (plugin: string): PluginEnvironment => {
|
||||
const logger = root.child({ type: 'plugin', plugin });
|
||||
const database = databaseManager.forPlugin(plugin);
|
||||
return { logger, database, config, reader, discovery };
|
||||
return { logger, cache, database, config, reader, discovery };
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
import { Logger } from 'winston';
|
||||
import { Config } from '@backstage/config';
|
||||
import {
|
||||
CacheManager,
|
||||
PluginDatabaseManager,
|
||||
PluginEndpointDiscovery,
|
||||
UrlReader,
|
||||
@@ -24,6 +25,7 @@ import {
|
||||
|
||||
export type PluginEnvironment = {
|
||||
logger: Logger;
|
||||
cache: CacheManager;
|
||||
database: PluginDatabaseManager;
|
||||
config: Config;
|
||||
reader: UrlReader;
|
||||
|
||||
Reference in New Issue
Block a user