Commit 727fc464 authored by nanahira's avatar nanahira

rename method and add destroy

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