Commit 727fc464 authored by nanahira's avatar nanahira

rename method and add destroy

parent d76866b7
...@@ -33,6 +33,9 @@ async function main() { ...@@ -33,6 +33,9 @@ async function main() {
const entries = await aragami.entries(User); const entries = await aragami.entries(User);
await aragami.delete(user2); await aragami.delete(user2);
await aragami.clear(User); await aragami.clear(User);
// use cache
const userOfCache = await aragami.cache(User, 'John', () => database.get('John'));
// wraps a function with cache. // wraps a function with cache.
const readUser = aragami.wrap( const readUser = aragami.wrap(
...@@ -40,15 +43,19 @@ async function main() { ...@@ -40,15 +43,19 @@ async function main() {
(name: string) => database.get(name), // For data fetching (name: string) => database.get(name), // For data fetching
(name) => name, // Specify cache key. (name) => name, // Specify cache key.
); );
const userFromCache = await readUser('John'); const userFromCache = await readUser('John');
// use lock
await aragami.lock([
user, // can be object
'John', // or string
], () => database.save(user))
// wraps a function with lock. // wraps a function with lock.
const saveUser = aragami.lock( const saveUser = aragami.useLock(
(user: User) => database.save(user), // Lock process (user: User) => database.save(user), // Lock process
(user) => [user], // Specify lock key. Can be an array. (user) => [user], // Specify lock key. Can be an array.
) )
await saveUser(user); await saveUser(user);
} }
......
...@@ -144,26 +144,34 @@ export class Aragami { ...@@ -144,26 +144,34 @@ export class Aragami {
return entries.map(([key, buf]) => [key, this.decode(cl, buf)]); return entries.map(([key, buf]) => [key, this.decode(cl, buf)]);
} }
wrap<T, A extends any[]>( async cache<T>(
cl: ClassType<T>,
keyOrMeta: string | T,
cb: () => Awaitable<T>,
) {
const key = await this.getKey(keyOrMeta, cl);
if (!key) {
return cb();
}
const cachedValue = await this.get(cl, key);
if (cachedValue != null) {
return cachedValue;
}
const value = await cb();
if (value != null) {
await this.set(value, { key, prototype: cl });
}
return value;
}
useCache<T, A extends any[]>(
cl: ClassType<T>, cl: ClassType<T>,
cb: (...args: A) => Awaitable<T>, cb: (...args: A) => Awaitable<T>,
keySource: (...args: A) => Awaitable<string | T>, keySource: (...args: A) => Awaitable<string | T>,
) { ) {
return async (...args: A): Promise<T> => { return async (...args: A): Promise<T> => {
const keyMeta = await keySource(...args); const keyMeta = await keySource(...args);
const key = await this.getKey(keyMeta, cl); return this.cache(cl, keyMeta, () => cb(...args));
if (!key) {
return cb(...args);
}
const cachedValue = await this.get(cl, key);
if (cachedValue) {
return cachedValue;
}
const value = await cb(...args);
if (value) {
await this.set(value, { key, prototype: cl });
}
return value;
}; };
} }
...@@ -181,23 +189,36 @@ export class Aragami { ...@@ -181,23 +189,36 @@ export class Aragami {
); );
} }
lock<A extends any[], R>( async lock<R>(
keys: MayBeArray<string | any>,
cb: () => Awaitable<R>,
): Promise<R> {
const keyMeta = makeArray(keys);
const actualKeys = (
await Promise.all(keyMeta.map((o) => this.getLockKeys(o)))
).flat();
if (!keys.length) {
return cb();
}
return this.driver.lock(actualKeys, async () => await cb());
}
useLock<A extends any[], R>(
cb: (...args: A) => R, cb: (...args: A) => R,
keySource: (...args: A) => Awaitable<MayBeArray<string | any>>, keySource: (...args: A) => Awaitable<MayBeArray<string | any>>,
) { ) {
return async (...args: A): Promise<Awaited<R>> => { return async (...args: A) => {
const keyMeta = makeArray(await keySource(...args)); const keys = await keySource(...args);
const keys = ( return this.lock(keys, () => cb(...args));
await Promise.all(keyMeta.map((o) => this.getLockKeys(o)))
).flat();
if (!keys.length) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
return cb(...args);
}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
return this.driver.lock(keys, async () => await cb(...args));
}; };
} }
async start() {
return this.driver.start();
}
async destroy() {
try {
await this.driver.destroy();
} catch (e) {}
}
} }
...@@ -46,4 +46,6 @@ export class BaseDriver { ...@@ -46,4 +46,6 @@ export class BaseDriver {
lock<R>(keys: string[], cb: () => Promise<R>) { lock<R>(keys: string[], cb: () => Promise<R>) {
return cb(); return cb();
} }
async destroy(): Promise<void> { }
} }
...@@ -6,6 +6,13 @@ export class MemoryDriver extends BaseDriver { ...@@ -6,6 +6,13 @@ export class MemoryDriver extends BaseDriver {
private cacheMap = new Map<string, LRUCache<string, Buffer>>(); private cacheMap = new Map<string, LRUCache<string, Buffer>>();
private betterLock = new BetterLock(); private betterLock = new BetterLock();
async destroy() {
for (const cache of this.cacheMap.values()) {
cache.clear();
}
this.cacheMap.clear();
}
private getCacheInstance(baseKey: string) { private getCacheInstance(baseKey: string) {
if (!this.cacheMap.has(baseKey)) { if (!this.cacheMap.has(baseKey)) {
this.cacheMap.set( this.cacheMap.set(
......
...@@ -62,4 +62,8 @@ export class RedisDriver extends BaseDriver { ...@@ -62,4 +62,8 @@ export class RedisDriver extends BaseDriver {
cb, cb,
); );
} }
async destroy() {
await this.redis.quit();
}
} }
...@@ -23,52 +23,47 @@ export class WrapDecoratorBuilder { ...@@ -23,52 +23,47 @@ export class WrapDecoratorBuilder {
const { aragamiFactory } = this; const { aragamiFactory } = this;
return { return {
UseLock: UseLock:
(): TypedMethodDecorator<(...args: any[]) => Awaitable<any>> => (): TypedMethodDecorator<(...args: any[]) => Promise<any>> =>
(obj, key, des) => { (obj, key, des) => {
const oldFun = des.value; const oldFun = des.value;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
des.value = async function (...args) { des.value = async function (...args) {
const aragami = await aragamiFactory(this); const aragami = await aragamiFactory(this);
const wrapped = aragami.lock( const lockKeyParams = reflector.getArray(
() => oldFun.apply(this, args), 'AragamiWithLockKey',
async () => { this,
const lockKeyParams = await reflector.getArray( key,
'AragamiWithLockKey',
this,
key,
);
return (
await Promise.all(
_.compact(
lockKeyParams.map(async (fun, i) => {
if (!fun) return;
const keyResult = (await fun(
args[i],
this,
key as string,
)) as MayBeArray<any>;
return makeArray(keyResult);
}),
),
)
).flat();
},
); );
return wrapped(); const keys = (
await Promise.all(
_.compact(
lockKeyParams.map(async (fun, i) => {
if (!fun) return;
const keyResult = (await fun(
args[i],
this,
key as string,
)) as MayBeArray<any>;
return makeArray(keyResult);
}),
),
)
).flat();
return aragami.lock(keys, () => oldFun.apply(this, args));
}; };
}, },
UseCache: UseCache:
<T>( <T>(
cl: ClassType<T>, cl: ClassType<T>,
): TypedMethodDecorator<(...args: any[]) => Awaitable<T>> => ): TypedMethodDecorator<(...args: any[]) => Promise<T>> =>
(obj, key, des) => { (obj, key, des) => {
const oldFun = des.value; const oldFun = des.value;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
des.value = async function (...args) { des.value = async function (...args) {
const aragami = await aragamiFactory(this); const aragami = await aragamiFactory(this);
const wrapped = aragami.wrap<T, []>( const wrapped = aragami.useCache<T, []>(
cl, cl,
() => oldFun.apply(this, args), () => oldFun.apply(this, args),
async () => { async () => {
......
...@@ -7,10 +7,14 @@ describe('Aragami.', () => { ...@@ -7,10 +7,14 @@ describe('Aragami.', () => {
beforeEach(() => { beforeEach(() => {
aragami = new Aragami({ aragami = new Aragami({
// redis: { uri: 'redis://localhost:6379' }, redis: { uri: 'redis://localhost:6379' },
}); });
}); });
afterEach(async () => {
await aragami.destroy();
});
it('Should store value', async () => { it('Should store value', async () => {
@CachePrefix('user') @CachePrefix('user')
@CacheTTL(100) @CacheTTL(100)
...@@ -50,7 +54,7 @@ describe('Aragami.', () => { ...@@ -50,7 +54,7 @@ describe('Aragami.', () => {
await expect(aragami.del(User, 'n.John')).resolves.toBeTruthy(); await expect(aragami.del(User, 'n.John')).resolves.toBeTruthy();
await expect(aragami.has(user)).resolves.toBeFalsy(); await expect(aragami.has(user)).resolves.toBeFalsy();
const wrapped = aragami.wrap( const wrapped = aragami.useCache(
User, User,
(name: string, age: number) => { (name: string, age: number) => {
const user = new User(); const user = new User();
...@@ -85,7 +89,7 @@ describe('Aragami.', () => { ...@@ -85,7 +89,7 @@ describe('Aragami.', () => {
}); });
it('should lock', async () => { it('should lock', async () => {
const fun = aragami.lock( const fun = aragami.useLock(
async (foo: string, bar: string) => { async (foo: string, bar: string) => {
await new Promise((resolve) => setTimeout(resolve, 1000)); await new Promise((resolve) => setTimeout(resolve, 1000));
return `${foo}.${bar}`; return `${foo}.${bar}`;
...@@ -110,7 +114,7 @@ describe('Aragami.', () => { ...@@ -110,7 +114,7 @@ describe('Aragami.', () => {
class MyService { class MyService {
@UseCache(Book) @UseCache(Book)
getBook(@WithKey() title: string, content: string) { async getBook(@WithKey() title: string, content: string) {
const book = new Book(); const book = new Book();
book.title = title; book.title = title;
book.content = content; book.content = content;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment