-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy patherrorResilient.ts
97 lines (82 loc) · 2.92 KB
/
errorResilient.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import {extractErrorMessage, Log, Logger, LogLevel} from '@croct/logging';
import {CacheLoader, CacheProvider} from './cacheProvider';
type CacheErrorLog = Log<{
errorMessage: string,
errorStack?: string,
}>;
/**
* A wrapper that indicates the wrapped error originated on the fresh data loader.
*
* This allows the error handling logic to differentiate an error that originated on the
* wrapped cache provider from an error that originated from the caller-provider data loader.
*/
class LoaderError extends Error {
public readonly internal: unknown;
public constructor(internal: unknown) {
super('Loader error.');
this.internal = internal;
}
}
/**
* A cache wrapper that prevents any error from the wrapped cache from propagating to the caller.
*
* Errors retrieving values from the cache behave as a cache miss.
* Errors retrieving a fresh value from the loader are propagated unchanged.
*/
export class ErrorResilientCache<K, V> implements CacheProvider<K, V> {
private readonly cache: CacheProvider<K, V>;
private readonly logger: Logger<CacheErrorLog>;
public constructor(cache: CacheProvider<K, V>, logger: Logger<CacheErrorLog>) {
this.cache = cache;
this.logger = logger;
this.logProviderError = this.logProviderError.bind(this);
}
public get(key: K, loader: CacheLoader<K, V>): Promise<V> {
return this.cache
.get(key, async loaderKey => {
try {
return await loader(loaderKey);
} catch (error) {
throw new LoaderError(error);
}
})
.catch(error => {
if (error instanceof LoaderError) {
this.logLoaderError(error.internal);
throw error.internal;
}
this.logProviderError(error);
return loader(key);
});
}
public set(key: K, value: V): Promise<void> {
return this.cache
.set(key, value)
.catch(this.logProviderError);
}
public delete(key: K): Promise<void> {
return this.cache
.delete(key)
.catch(this.logProviderError);
}
private logLoaderError(error: unknown): void {
this.logger.log({
level: LogLevel.ERROR,
message: 'Error detected on cache loader.',
details: {
errorMessage: extractErrorMessage(error),
errorStack: error instanceof Error ? error.stack : undefined,
},
});
}
private logProviderError(error: unknown): void {
this.logger.log({
level: LogLevel.ERROR,
message: 'Suppressed error on cache operation',
details: {
errorMessage: extractErrorMessage(error),
errorStack: error instanceof Error ? error.stack : undefined,
},
});
}
}