mirror of
https://github.com/nocobase/nocobase
synced 2024-11-15 01:48:14 +00:00
feat(core/cache): support cache (#876)
* feat(core/cache): support cache * build(create-nocobase-app): remove --cache-store-package cli option * perf(core/cache): modify default cache config and remove unnecessary logic code
This commit is contained in:
parent
83e6f93e1e
commit
6e6086de7a
@ -27,6 +27,10 @@ DB_STORAGE=storage/db/nocobase.sqlite
|
||||
# DB_PASSWORD=nocobase
|
||||
# DB_LOGGING=on
|
||||
|
||||
################# CACHE #################
|
||||
# default is memory cache, when develop mode,code's change will be clear memory cache, so can use 'cache-manager-fs-hash'
|
||||
# CACHE_CONFIG={"storePackage":"cache-manager-fs-hash","ttl":86400,"max":1000}
|
||||
|
||||
################# STORAGE (Initialization only) #################
|
||||
|
||||
INIT_ROOT_EMAIL=admin@nocobase.com
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -16,4 +16,5 @@ coverage
|
||||
docs-dist/
|
||||
.npmrc
|
||||
dist/
|
||||
docker/**/storage
|
||||
docker/**/storage
|
||||
cache/diskstore-*
|
||||
|
5
packages/app/server/src/config/cache.ts
Normal file
5
packages/app/server/src/config/cache.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { createDefaultCacheConfig } from '@nocobase/cache';
|
||||
|
||||
const cacheConfig = !!process.env.CACHE_CONFIG ? JSON.parse(process.env.CACHE_CONFIG) : createDefaultCacheConfig();
|
||||
|
||||
export default cacheConfig;
|
@ -1,9 +1,11 @@
|
||||
import database from './database';
|
||||
import plugins from './plugins';
|
||||
import resourcer from './resourcer';
|
||||
import cache from './cache';
|
||||
|
||||
export default {
|
||||
database,
|
||||
resourcer,
|
||||
plugins,
|
||||
cache,
|
||||
};
|
||||
|
@ -13,7 +13,8 @@
|
||||
"types": "./lib/index.d.ts",
|
||||
"dependencies": {
|
||||
"@nocobase/database": "0.7.4-alpha.7",
|
||||
"@nocobase/resourcer": "0.7.4-alpha.7"
|
||||
"@nocobase/resourcer": "0.7.4-alpha.7",
|
||||
"@nocobase/cache": "0.7.4-alpha.7"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -1,6 +1,7 @@
|
||||
import Koa from 'koa';
|
||||
import { Database } from '@nocobase/database';
|
||||
import { Action } from '@nocobase/resourcer';
|
||||
import { Cache } from '@nocobase/cache';
|
||||
import lodash from 'lodash';
|
||||
import * as actions from './actions';
|
||||
|
||||
@ -10,9 +11,11 @@ export type Next = () => Promise<any>;
|
||||
|
||||
export interface Context extends Koa.Context {
|
||||
db: Database;
|
||||
cache: Cache;
|
||||
action: Action;
|
||||
body: any;
|
||||
app: any;
|
||||
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
|
8
packages/core/cache/.npmignore
vendored
Normal file
8
packages/core/cache/.npmignore
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
node_modules
|
||||
*.log
|
||||
docs
|
||||
__tests__
|
||||
jest.config.js
|
||||
tsconfig.json
|
||||
src
|
||||
.fatherrc.ts
|
26
packages/core/cache/package.json
vendored
Normal file
26
packages/core/cache/package.json
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "@nocobase/cache",
|
||||
"version": "0.7.4-alpha.7",
|
||||
"description": "",
|
||||
"license": "Apache-2.0",
|
||||
"licenses": [
|
||||
{
|
||||
"type": "Apache-2.0",
|
||||
"url": "http://www.apache.org/licenses/LICENSE-2.0"
|
||||
}
|
||||
],
|
||||
"main": "./lib/index.js",
|
||||
"types": "./lib/index.d.ts",
|
||||
"dependencies": {
|
||||
"cache-manager": "^4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/cache-manager": "^4.0.2",
|
||||
"cache-manager-fs-hash": "^1.0.0"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/nocobase/nocobase.git",
|
||||
"directory": "packages/cache"
|
||||
}
|
||||
}
|
74
packages/core/cache/src/__tests__/index.test.ts
vendored
Normal file
74
packages/core/cache/src/__tests__/index.test.ts
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
import { createCache, createDefaultCacheConfig } from '@nocobase/cache';
|
||||
|
||||
export function sleep(ms: number) {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(resolve, ms);
|
||||
});
|
||||
}
|
||||
|
||||
describe('cache', () => {
|
||||
it('createCache-with-mem', async () => {
|
||||
const cacheConfig = createDefaultCacheConfig();
|
||||
cacheConfig.ttl = 1;
|
||||
const cache = createCache(cacheConfig);
|
||||
await cache.set('name', 'Emma');
|
||||
expect(await cache.get('name')).toEqual('Emma');
|
||||
await sleep(100);
|
||||
expect(await cache.get('name')).toEqual('Emma');
|
||||
await sleep(1005);
|
||||
expect(await cache.get('name')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('createCache-with-single-config', async () => {
|
||||
let cacheConfigStr =
|
||||
'{"store":"memory","ttl":1,"max":10}';
|
||||
let cacheConfig = JSON.parse(cacheConfigStr);
|
||||
let cache = createCache(cacheConfig);
|
||||
await cache.set('name', 'Emma');
|
||||
expect(await cache.get('name')).toEqual('Emma');
|
||||
await sleep(100);
|
||||
expect(await cache.get('name')).toEqual('Emma');
|
||||
await sleep(1005);
|
||||
expect(await cache.get('name')).toBeUndefined();
|
||||
|
||||
cacheConfigStr =
|
||||
'[{"store":"memory","ttl":1,"max":10}]';
|
||||
cacheConfig = JSON.parse(cacheConfigStr);
|
||||
cache = createCache(cacheConfig);
|
||||
await cache.set('name', 'Emma');
|
||||
expect(await cache.get('name')).toEqual('Emma');
|
||||
await sleep(100);
|
||||
expect(await cache.get('name')).toEqual('Emma');
|
||||
await sleep(1005);
|
||||
expect(await cache.get('name')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('createCache-with-default-cache-manager-fs-hash', async () => {
|
||||
const cacheConfig = createDefaultCacheConfig();
|
||||
cacheConfig.ttl = 1;
|
||||
cacheConfig.storePackage = 'cache-manager-fs-hash';
|
||||
const cache = createCache(cacheConfig);
|
||||
await cache.set('name', 'Emma');
|
||||
expect(await cache.get('name')).toEqual('Emma');
|
||||
await sleep(100);
|
||||
expect(await cache.get('name')).toEqual('Emma');
|
||||
await sleep(1005);
|
||||
expect(await cache.get('name')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('createCache-multi-cache', async () => {
|
||||
const cacheConfigStr =
|
||||
'[{"store":"memory","ttl":1,"max":10},{"storePackage":"cache-manager-fs-hash","ttl":10,"max":100}]';
|
||||
const cacheConfig = JSON.parse(cacheConfigStr);
|
||||
const cache = createCache(cacheConfig);
|
||||
|
||||
await cache.set('name', 'Emma');
|
||||
expect(await cache.get('name')).toEqual('Emma');
|
||||
await sleep(1005);
|
||||
expect(await cache.get('name')).toEqual('Emma');
|
||||
await sleep(5000);
|
||||
expect(await cache.get('name')).toEqual('Emma');
|
||||
await sleep(5000);
|
||||
expect(await cache.get('name')).toBeUndefined();
|
||||
});
|
||||
});
|
70
packages/core/cache/src/index.ts
vendored
Normal file
70
packages/core/cache/src/index.ts
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
import { CacheOptions, caching, CachingConfig, multiCaching, StoreConfig, WrapArgsType } from 'cache-manager';
|
||||
|
||||
/**
|
||||
* be used for create cache {@link createCache}
|
||||
*/
|
||||
export type ICacheConfig = StoreConfig &
|
||||
CacheOptions & {
|
||||
// every storeConfig init a store instance
|
||||
storePackage?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* create a default cache config object
|
||||
* @returns {ICacheConfig}
|
||||
*/
|
||||
export function createDefaultCacheConfig(): ICacheConfig {
|
||||
return {
|
||||
ttl: 86400, // seconds
|
||||
max: 1000,
|
||||
store: 'memory',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* cache and multi cache common method and only keep promise method
|
||||
*/
|
||||
export interface Cache {
|
||||
set<T>(key: string, value: T, options?: CachingConfig): Promise<T>;
|
||||
|
||||
set<T>(key: string, value: T, ttl: number): Promise<T>;
|
||||
|
||||
wrap<T>(...args: WrapArgsType<T>[]): Promise<T>;
|
||||
|
||||
get<T>(key: string): Promise<T | undefined>;
|
||||
|
||||
del(key: string): Promise<any>;
|
||||
|
||||
reset(): Promise<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* create cache
|
||||
* <br/> if cacheConfig is array and length gt 1 then will be return multi cache, else will be return cache
|
||||
* @param {ICacheConfig | ICacheConfig[]} cacheConfig
|
||||
* @returns {Cache}
|
||||
*/
|
||||
export function createCache(cacheConfig: ICacheConfig | ICacheConfig[] = createDefaultCacheConfig()): Cache {
|
||||
if (Array.isArray(cacheConfig)) {
|
||||
// multi cache
|
||||
if (cacheConfig.length === 1) {
|
||||
return createCacheByICacheConfig(cacheConfig[0]);
|
||||
} else {
|
||||
let caches = [];
|
||||
for (const cacheConfigEle of cacheConfig) {
|
||||
caches.push(createCacheByICacheConfig(cacheConfigEle));
|
||||
}
|
||||
return multiCaching(caches) as Cache;
|
||||
}
|
||||
} else {
|
||||
return createCacheByICacheConfig(cacheConfig);
|
||||
}
|
||||
}
|
||||
|
||||
function createCacheByICacheConfig(cacheConfig: ICacheConfig): Cache {
|
||||
// if storePackage exist then load storePackage and instead store
|
||||
if (cacheConfig.storePackage) {
|
||||
cacheConfig.store = require(cacheConfig.storePackage);
|
||||
}
|
||||
return caching(cacheConfig);
|
||||
}
|
@ -15,6 +15,7 @@ import { registerCli } from './commands';
|
||||
import { createI18n, createResourcer, registerMiddlewares } from './helper';
|
||||
import { Plugin } from './plugin';
|
||||
import { InstallOptions, PluginManager } from './plugin-manager';
|
||||
import { createCache, ICacheConfig, Cache } from '@nocobase/cache';
|
||||
|
||||
const packageJson = require('../package.json');
|
||||
|
||||
@ -27,6 +28,7 @@ export interface ResourcerOptions {
|
||||
|
||||
export interface ApplicationOptions {
|
||||
database?: IDatabaseOptions | Database;
|
||||
cache?: ICacheConfig | ICacheConfig[];
|
||||
resourcer?: ResourcerOptions;
|
||||
bodyParser?: any;
|
||||
cors?: any;
|
||||
@ -38,12 +40,15 @@ export interface ApplicationOptions {
|
||||
|
||||
export interface DefaultState {
|
||||
currentUser?: any;
|
||||
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export interface DefaultContext {
|
||||
db: Database;
|
||||
cache: Cache;
|
||||
resourcer: Resourcer;
|
||||
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
@ -130,6 +135,8 @@ export class ApplicationVersion {
|
||||
export class Application<StateT = DefaultState, ContextT = DefaultContext> extends Koa implements AsyncEmitter {
|
||||
public readonly db: Database;
|
||||
|
||||
public readonly cache: Cache;
|
||||
|
||||
public readonly resourcer: Resourcer;
|
||||
|
||||
public readonly cli: Command;
|
||||
@ -153,6 +160,7 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
|
||||
|
||||
this.acl = createACL();
|
||||
this.db = this.createDatabase(options);
|
||||
this.cache = createCache(options.cache);
|
||||
this.resourcer = createResourcer(options);
|
||||
this.cli = new Command('nocobase').usage('[command] [options]');
|
||||
this.i18n = createI18n(options);
|
||||
|
@ -51,6 +51,7 @@ export function registerMiddlewares(app: Application, options: ApplicationOption
|
||||
return ctx.get('Authorization').replace(/^Bearer\s+/gi, '');
|
||||
};
|
||||
ctx.db = app.db;
|
||||
ctx.cache = app.cache;
|
||||
ctx.resourcer = app.resourcer;
|
||||
const i18n = app.i18n.cloneInstance({ initImmediate: false });
|
||||
ctx.i18n = i18n;
|
||||
|
48
yarn.lock
48
yarn.lock
@ -4978,6 +4978,11 @@
|
||||
"@types/connect" "*"
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/cache-manager@^4.0.2":
|
||||
version "4.0.2"
|
||||
resolved "https://registry.npmmirror.com/@types/cache-manager/-/cache-manager-4.0.2.tgz#5e76dd9e7881c23f332c2f48e5f326bd05ba9ac9"
|
||||
integrity sha512-fT5FMdzsiSX0AbgnS5gDvHl2Nco0h5zYyjwDQy4yPC7Ww6DeGMVKPRqIZtg9HOXDV2kkc18SL1B0N8f0BecrCA==
|
||||
|
||||
"@types/connect@*":
|
||||
version "3.4.35"
|
||||
resolved "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1"
|
||||
@ -6680,6 +6685,11 @@ async-validator@^4.0.2:
|
||||
resolved "https://registry.npmjs.org/async-validator/-/async-validator-4.0.7.tgz#034a0fd2103a6b2ebf010da75183bec299247afe"
|
||||
integrity sha512-Pj2IR7u8hmUEDOwB++su6baaRi+QvsgajuFB9j95foM1N2gy5HM4z60hfusIO0fBPG5uLAEl6yCJr1jNSVugEQ==
|
||||
|
||||
async@3.2.3, async@^3.2.0, async@~3.2.0:
|
||||
version "3.2.3"
|
||||
resolved "https://registry.npmjs.org/async/-/async-3.2.3.tgz#ac53dafd3f4720ee9e8a160628f18ea91df196c9"
|
||||
integrity sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==
|
||||
|
||||
async@^2.6.2:
|
||||
version "2.6.4"
|
||||
resolved "https://registry.npmmirror.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221"
|
||||
@ -6694,11 +6704,6 @@ async@^2.6.3, async@~2.6.1:
|
||||
dependencies:
|
||||
lodash "^4.17.14"
|
||||
|
||||
async@^3.2.0, async@~3.2.0:
|
||||
version "3.2.3"
|
||||
resolved "https://registry.npmjs.org/async/-/async-3.2.3.tgz#ac53dafd3f4720ee9e8a160628f18ea91df196c9"
|
||||
integrity sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==
|
||||
|
||||
asynckit@^0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
|
||||
@ -7440,6 +7445,22 @@ cache-content-type@^1.0.0:
|
||||
mime-types "^2.1.18"
|
||||
ylru "^1.2.0"
|
||||
|
||||
cache-manager-fs-hash@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmmirror.com/cache-manager-fs-hash/-/cache-manager-fs-hash-1.0.0.tgz#9a3f3fa239c48c54fc6b00575032b72c07dcad99"
|
||||
integrity sha512-fK2vEAhWh7IAzBP+JwUEJkng0OPOxw3ji86SjQS5SWdLx2Cytku6xFNoQK32pedtuae/wBl/jA/X4abiAYHS/Q==
|
||||
dependencies:
|
||||
lockfile "^1.0.4"
|
||||
|
||||
cache-manager@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.npmmirror.com/cache-manager/-/cache-manager-4.1.0.tgz#aa986421f1c975a862d6de88edb9ab1d30f4bd39"
|
||||
integrity sha512-ZGM6dLxrP65bfOZmcviWMadUOCICqpLs92+P/S5tj8onz+k+tB7Gr+SAgOUHCQtfm2gYEQDHiKeul4+tYPOJ8A==
|
||||
dependencies:
|
||||
async "3.2.3"
|
||||
lodash.clonedeep "^4.5.0"
|
||||
lru-cache "^7.10.1"
|
||||
|
||||
cacheable-request@^6.0.0:
|
||||
version "6.1.0"
|
||||
resolved "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912"
|
||||
@ -14478,6 +14499,13 @@ locate-path@^6.0.0:
|
||||
dependencies:
|
||||
p-locate "^5.0.0"
|
||||
|
||||
lockfile@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.npmmirror.com/lockfile/-/lockfile-1.0.4.tgz#07f819d25ae48f87e538e6578b6964a4981a5609"
|
||||
integrity sha512-cvbTwETRfsFh4nHsL1eGWapU1XFi5Ot9E85sWAwia7Y7EgB7vfqcZhTKZ+l7hCGxSPoushMv5GKhT5PdLv03WA==
|
||||
dependencies:
|
||||
signal-exit "^3.0.2"
|
||||
|
||||
lodash-es@^4.17.11:
|
||||
version "4.17.21"
|
||||
resolved "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee"
|
||||
@ -14510,6 +14538,11 @@ lodash.clone@4.5.0, lodash.clone@^4.3.2:
|
||||
resolved "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz#195870450f5a13192478df4bc3d23d2dea1907b6"
|
||||
integrity sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y=
|
||||
|
||||
lodash.clonedeep@^4.5.0:
|
||||
version "4.5.0"
|
||||
resolved "https://registry.npmmirror.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
|
||||
integrity sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==
|
||||
|
||||
lodash.debounce@^4.0.8:
|
||||
version "4.0.8"
|
||||
resolved "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
|
||||
@ -14709,6 +14742,11 @@ lru-cache@^6.0.0:
|
||||
dependencies:
|
||||
yallist "^4.0.0"
|
||||
|
||||
lru-cache@^7.10.1:
|
||||
version "7.14.0"
|
||||
resolved "https://registry.npmmirror.com/lru-cache/-/lru-cache-7.14.0.tgz#21be64954a4680e303a09e9468f880b98a0b3c7f"
|
||||
integrity sha512-EIRtP1GrSJny0dqb50QXRUNBxHJhcpxHC++M5tD7RYbvLLn5KVWKsbyswSSqDuU15UFi3bgTQIY8nhDMeF6aDQ==
|
||||
|
||||
luxon@^1.28.0:
|
||||
version "1.28.0"
|
||||
resolved "https://registry.yarnpkg.com/luxon/-/luxon-1.28.0.tgz#e7f96daad3938c06a62de0fb027115d251251fbf"
|
||||
|
Loading…
Reference in New Issue
Block a user