|
|
@@ -44,92 +44,73 @@ export type FsMemoCacheOptions<T> = CacheApplyOption<T, string> & {
|
|
|
ttl?: undefined | never
|
|
|
};
|
|
|
|
|
|
-export function cache<Args extends Devalue[], T>(
|
|
|
- fn: (...args: Args) => Promise<T>,
|
|
|
- opt: FsMemoCacheOptions<T>
|
|
|
-): (...args: Args) => Promise<T> {
|
|
|
- const fixedKey = fn.toString();
|
|
|
-
|
|
|
- return async function cachedCb(...args: Args) {
|
|
|
- const { stringify: devalueStringify } = await import('devalue');
|
|
|
-
|
|
|
- // Construct the complete cache key for this function invocation
|
|
|
- // typeson.stringify is still limited. For now we uses typescript to guard the args.
|
|
|
- const cacheKey = (await Promise.all([
|
|
|
- xxhash64(fixedKey),
|
|
|
- xxhash64(devalueStringify(args))
|
|
|
- ])).join('|');
|
|
|
-
|
|
|
- const cacheName = fn.name || fixedKey;
|
|
|
+function createCache(onlyUseCachedIfFail: boolean) {
|
|
|
+ return function cache<Args extends Devalue[], T>(
|
|
|
+ fn: (...args: Args) => Promise<T>,
|
|
|
+ opt: FsMemoCacheOptions<T>
|
|
|
+ ): (...args: Args) => Promise<T> {
|
|
|
+ const fixedKey = fn.toString();
|
|
|
|
|
|
if (opt.temporaryBypass) {
|
|
|
- return fn(...args);
|
|
|
+ return fn;
|
|
|
}
|
|
|
|
|
|
- const cached = fsMemoCache.get(cacheKey);
|
|
|
- if (cached == null) {
|
|
|
- console.log(picocolors.yellow('[cache] miss'), picocolors.gray(cacheName || cacheKey));
|
|
|
+ return async function cachedCb(...args: Args) {
|
|
|
+ const { stringify: devalueStringify } = await import('devalue');
|
|
|
|
|
|
- const serializer = 'serializer' in opt ? opt.serializer : identity as any;
|
|
|
+ // Construct the complete cache key for this function invocation
|
|
|
+ // typeson.stringify is still limited. For now we uses typescript to guard the args.
|
|
|
+ const cacheKey = (await Promise.all([
|
|
|
+ xxhash64(fixedKey),
|
|
|
+ xxhash64(devalueStringify(args))
|
|
|
+ ])).join('|');
|
|
|
|
|
|
- const value = await fn(...args);
|
|
|
+ const cacheName = picocolors.gray(fn.name || fixedKey || cacheKey);
|
|
|
|
|
|
- fsMemoCache.set(cacheKey, serializer(value), TTL);
|
|
|
- return value;
|
|
|
- }
|
|
|
+ const cached = fsMemoCache.get(cacheKey);
|
|
|
|
|
|
- console.log(picocolors.green('[cache] hit'), picocolors.gray(cacheName || cacheKey));
|
|
|
+ const serializer = 'serializer' in opt ? opt.serializer : identity as any;
|
|
|
+ const deserializer = 'deserializer' in opt ? opt.deserializer : identity as any;
|
|
|
|
|
|
- fsMemoCache.updateTtl(cacheKey, TTL);
|
|
|
+ if (onlyUseCachedIfFail) {
|
|
|
+ try {
|
|
|
+ const value = await fn(...args);
|
|
|
|
|
|
- const deserializer = 'deserializer' in opt ? opt.deserializer : identity as any;
|
|
|
- return deserializer(cached);
|
|
|
- };
|
|
|
-}
|
|
|
+ console.log(picocolors.gray('[cache] update'), cacheName);
|
|
|
+ fsMemoCache.set(cacheKey, serializer(value), TTL);
|
|
|
|
|
|
-export function cachedOnlyFail<Args extends Devalue[], T>(
|
|
|
- fn: (...args: Args) => Promise<T>,
|
|
|
- opt: FsMemoCacheOptions<T>
|
|
|
-): (...args: Args) => Promise<T> {
|
|
|
- const fixedKey = fn.toString();
|
|
|
+ return value;
|
|
|
+ } catch (e) {
|
|
|
+ if (cached == null) {
|
|
|
+ console.log(picocolors.red('[fail] and no cache, throwing'), cacheName);
|
|
|
+ throw e;
|
|
|
+ }
|
|
|
|
|
|
- return async function cachedCb(...args: Args) {
|
|
|
- const { stringify: devalueStringify } = await import('devalue');
|
|
|
+ fsMemoCache.updateTtl(cacheKey, TTL);
|
|
|
|
|
|
- // Construct the complete cache key for this function invocation
|
|
|
- // typeson.stringify is still limited. For now we uses typescript to guard the args.
|
|
|
- const cacheKey = (await Promise.all([
|
|
|
- xxhash64(fixedKey),
|
|
|
- xxhash64(devalueStringify(args))
|
|
|
- ])).join('|');
|
|
|
+ console.log(picocolors.yellow('[fail] try cache'), cacheName);
|
|
|
|
|
|
- const cacheName = fn.name || fixedKey;
|
|
|
+ return deserializer(cached);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (cached == null) {
|
|
|
+ console.log(picocolors.yellow('[cache] miss'), cacheName);
|
|
|
|
|
|
- if (opt.temporaryBypass) {
|
|
|
- return fn(...args);
|
|
|
- }
|
|
|
+ const value = await fn(...args);
|
|
|
|
|
|
- const cached = fsMemoCache.get(cacheKey);
|
|
|
+ fsMemoCache.set(cacheKey, serializer(value), TTL);
|
|
|
+ return value;
|
|
|
+ }
|
|
|
|
|
|
- try {
|
|
|
- const value = await fn(...args);
|
|
|
+ console.log(picocolors.green('[cache] hit'), cacheName);
|
|
|
|
|
|
- const serializer = 'serializer' in opt ? opt.serializer : identity as any;
|
|
|
- fsMemoCache.set(cacheKey, serializer(value), TTL);
|
|
|
+ fsMemoCache.updateTtl(cacheKey, TTL);
|
|
|
|
|
|
- return value;
|
|
|
- } catch (e) {
|
|
|
- if (cached == null) {
|
|
|
- console.log(picocolors.red('[fail] and no cache, throwing'), picocolors.gray(cacheName || cacheKey));
|
|
|
- throw e;
|
|
|
+ return deserializer(cached);
|
|
|
}
|
|
|
-
|
|
|
- fsMemoCache.updateTtl(cacheKey, TTL);
|
|
|
-
|
|
|
- console.log(picocolors.yellow('[fail] try cache'), picocolors.gray(cacheName || cacheKey));
|
|
|
- const deserializer = 'deserializer' in opt ? opt.deserializer : identity as any;
|
|
|
-
|
|
|
- return deserializer(cached);
|
|
|
- }
|
|
|
+ };
|
|
|
};
|
|
|
}
|
|
|
+
|
|
|
+export const cache = createCache(false);
|
|
|
+export const cachedOnlyFail = createCache(true);
|