@autofleet/matmon 2.4.0 → 2.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -14
- package/lib/index.cjs +2 -0
- package/lib/index.cjs.map +1 -0
- package/lib/index.d.cts +70 -0
- package/lib/index.d.ts +70 -4
- package/lib/index.js +2 -15
- package/lib/index.js.map +1 -0
- package/package.json +25 -11
- package/lib/cache.d.ts +0 -25
- package/lib/cache.js +0 -140
- package/lib/cache.test.d.ts +0 -1
- package/lib/cache.test.js +0 -168
- package/lib/locking.d.ts +0 -3
- package/lib/locking.js +0 -18
- package/lib/logger.d.ts +0 -2
- package/lib/logger.js +0 -8
- package/lib/orm-cache/adapter.d.ts +0 -17
- package/lib/orm-cache/adapter.js +0 -2
- package/lib/orm-cache/errors.d.ts +0 -2
- package/lib/orm-cache/errors.js +0 -6
- package/lib/orm-cache/index.d.ts +0 -12
- package/lib/orm-cache/index.js +0 -39
- package/lib/orm-cache/sequelize-adapter.d.ts +0 -13
- package/lib/orm-cache/sequelize-adapter.js +0 -156
- package/lib/promise-utils.d.ts +0 -1
- package/lib/promise-utils.js +0 -19
- package/lib/redis/errors.d.ts +0 -13
- package/lib/redis/errors.js +0 -25
- package/lib/redis/index.d.ts +0 -21
- package/lib/redis/index.js +0 -148
- package/lib/redis/index.test.d.ts +0 -1
- package/lib/redis/index.test.js +0 -62
package/lib/cache.js
DELETED
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
-
if (mod && mod.__esModule) return mod;
|
|
20
|
-
var result = {};
|
|
21
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
-
__setModuleDefault(result, mod);
|
|
23
|
-
return result;
|
|
24
|
-
};
|
|
25
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
-
};
|
|
28
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
-
exports.getMultipleWithCache = exports.getWithCacheSupport = exports.getNewLRU = void 0;
|
|
30
|
-
const lru_cache_1 = __importDefault(require("lru-cache"));
|
|
31
|
-
const locking = __importStar(require("./locking"));
|
|
32
|
-
const dotenv_1 = __importDefault(require("dotenv"));
|
|
33
|
-
const logger_1 = __importDefault(require("./logger"));
|
|
34
|
-
dotenv_1.default.config();
|
|
35
|
-
const DEFAULT_CACHE_SIZE = 300;
|
|
36
|
-
const MAX_SIZE = 100000;
|
|
37
|
-
const MUTEX_MAP = {};
|
|
38
|
-
const getOptions = ({ lifeTimeInSec, size = DEFAULT_CACHE_SIZE, }) => ({
|
|
39
|
-
max: Math.min(size, MAX_SIZE),
|
|
40
|
-
maxAge: process.env.NODE_ENV !== 'test' ? 1000 * lifeTimeInSec : 0,
|
|
41
|
-
});
|
|
42
|
-
const getMutexByCacheKey = (key) => {
|
|
43
|
-
MUTEX_MAP[key] || (MUTEX_MAP[key] = locking.getMutex());
|
|
44
|
-
return MUTEX_MAP[key];
|
|
45
|
-
};
|
|
46
|
-
const deleteMutexByCacheKey = (key) => {
|
|
47
|
-
if (MUTEX_MAP[key]) {
|
|
48
|
-
delete MUTEX_MAP[key];
|
|
49
|
-
}
|
|
50
|
-
};
|
|
51
|
-
const getNewLRU = (lifeTimeInSec, size) => new lru_cache_1.default(getOptions({
|
|
52
|
-
lifeTimeInSec,
|
|
53
|
-
size,
|
|
54
|
-
}));
|
|
55
|
-
exports.getNewLRU = getNewLRU;
|
|
56
|
-
const IN_LOCAL_TEST = process.env.IS_IN_MATMON_TESTING === 'true';
|
|
57
|
-
const IS_IN_SERVICE_TEST = process.env.NODE_ENV === 'test' && !IN_LOCAL_TEST;
|
|
58
|
-
const tryToSetInCache = async (callback) => {
|
|
59
|
-
try {
|
|
60
|
-
await callback();
|
|
61
|
-
}
|
|
62
|
-
catch (e) {
|
|
63
|
-
logger_1.default.error('Failed to set in cache', e);
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
|
-
const getWithCacheSupport = async ({ cacheKey, cacheGet, cacheSet, fetching, skipCache, }) => {
|
|
67
|
-
if (skipCache || IS_IN_SERVICE_TEST) {
|
|
68
|
-
const res = await fetching();
|
|
69
|
-
await tryToSetInCache(() => cacheSet(res));
|
|
70
|
-
return res;
|
|
71
|
-
}
|
|
72
|
-
let valueToReturn = null;
|
|
73
|
-
try {
|
|
74
|
-
await locking.wrapWithMutex(getMutexByCacheKey(cacheKey), async () => {
|
|
75
|
-
valueToReturn = await cacheGet();
|
|
76
|
-
if (!valueToReturn) {
|
|
77
|
-
valueToReturn = await fetching();
|
|
78
|
-
await tryToSetInCache(() => cacheSet(valueToReturn));
|
|
79
|
-
}
|
|
80
|
-
else {
|
|
81
|
-
// logger.info('get value from cache');
|
|
82
|
-
}
|
|
83
|
-
deleteMutexByCacheKey(cacheKey);
|
|
84
|
-
});
|
|
85
|
-
// retry without locking if failed
|
|
86
|
-
}
|
|
87
|
-
catch (e) {
|
|
88
|
-
valueToReturn = await fetching();
|
|
89
|
-
await tryToSetInCache(() => cacheSet(valueToReturn));
|
|
90
|
-
deleteMutexByCacheKey(cacheKey);
|
|
91
|
-
}
|
|
92
|
-
return valueToReturn;
|
|
93
|
-
};
|
|
94
|
-
exports.getWithCacheSupport = getWithCacheSupport;
|
|
95
|
-
const getIdField = (query, idField) => {
|
|
96
|
-
if (typeof query === 'string') {
|
|
97
|
-
return query;
|
|
98
|
-
}
|
|
99
|
-
return query[idField];
|
|
100
|
-
};
|
|
101
|
-
const getMultipleWithCache = ({ getFromCache, multiGetterFromCache, setInCache, setMultiInCache, getter, multiGetter, idField = 'id', }) => async (queries) => {
|
|
102
|
-
const queriesMap = new Map(queries.filter(Boolean).map(query => [getIdField(query, idField), query]));
|
|
103
|
-
const resultMap = new Map();
|
|
104
|
-
const valuesToPullFromCache = [...queriesMap.values()];
|
|
105
|
-
const valuesFromCache = await (multiGetterFromCache?.(valuesToPullFromCache) || // Use multiGetterFromCache if it's provided
|
|
106
|
-
Promise.all(valuesToPullFromCache.map(query => getFromCache(query))) // Otherwise, iterate over the queries with getFromCache
|
|
107
|
-
);
|
|
108
|
-
valuesFromCache.filter(Boolean).forEach((value) => {
|
|
109
|
-
queriesMap.delete(getIdField(value, idField));
|
|
110
|
-
resultMap.set(getIdField(value, idField), value);
|
|
111
|
-
});
|
|
112
|
-
if (queriesMap.size > 0) {
|
|
113
|
-
const valuesFromGetter = await (multiGetter?.([...queriesMap.values()]) || // Use multiGetter if it's provided
|
|
114
|
-
Promise.all([...queriesMap.values()].map(id => getter(id))) // Otherwise, iterate over the queries with getter
|
|
115
|
-
);
|
|
116
|
-
if (setMultiInCache && valuesFromGetter.length > 0) {
|
|
117
|
-
const setCacheObject = valuesFromGetter.reduce((acc, value) => {
|
|
118
|
-
acc[getIdField(value, idField)] = value;
|
|
119
|
-
return acc;
|
|
120
|
-
}, {});
|
|
121
|
-
await tryToSetInCache(() => setMultiInCache(setCacheObject));
|
|
122
|
-
}
|
|
123
|
-
valuesFromGetter.forEach((value) => {
|
|
124
|
-
if (!value) {
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
if (!setMultiInCache) {
|
|
128
|
-
tryToSetInCache(() => setInCache(value[idField], value));
|
|
129
|
-
}
|
|
130
|
-
resultMap.set(getIdField(value, idField), value);
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
return queries.map((query) => {
|
|
134
|
-
if (!query) {
|
|
135
|
-
return null;
|
|
136
|
-
}
|
|
137
|
-
return resultMap.get(getIdField(query, idField));
|
|
138
|
-
});
|
|
139
|
-
};
|
|
140
|
-
exports.getMultipleWithCache = getMultipleWithCache;
|
package/lib/cache.test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/lib/cache.test.js
DELETED
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const vitest_1 = require("vitest");
|
|
4
|
-
const cache_1 = require("./cache");
|
|
5
|
-
(0, vitest_1.describe)('Cache', () => {
|
|
6
|
-
(0, vitest_1.describe)('getNewLRU', () => {
|
|
7
|
-
(0, vitest_1.it)('should create a new LRU cache with default size', () => {
|
|
8
|
-
const cache = (0, cache_1.getNewLRU)(60);
|
|
9
|
-
(0, vitest_1.expect)(cache.max).toBe(300);
|
|
10
|
-
});
|
|
11
|
-
(0, vitest_1.it)('should create a new LRU cache with specified size', () => {
|
|
12
|
-
const cache = (0, cache_1.getNewLRU)(60, 500);
|
|
13
|
-
(0, vitest_1.expect)(cache.max).toBe(500);
|
|
14
|
-
});
|
|
15
|
-
(0, vitest_1.it)('should not exceed the maximum size', () => {
|
|
16
|
-
const cache = (0, cache_1.getNewLRU)(60, 200000);
|
|
17
|
-
(0, vitest_1.expect)(cache.max).toBe(100000);
|
|
18
|
-
});
|
|
19
|
-
});
|
|
20
|
-
(0, vitest_1.describe)('getWithCacheSupport', () => {
|
|
21
|
-
const cacheKey = 'testKey';
|
|
22
|
-
const createMocks = () => {
|
|
23
|
-
const cacheGet = vitest_1.vi.fn();
|
|
24
|
-
const cacheSet = vitest_1.vi.fn();
|
|
25
|
-
const fetching = vitest_1.vi.fn();
|
|
26
|
-
const getFromCache = vitest_1.vi.fn();
|
|
27
|
-
const setInCache = vitest_1.vi.fn();
|
|
28
|
-
const multiGetterFromCache = vitest_1.vi.fn();
|
|
29
|
-
const setMultiInCache = vitest_1.vi.fn();
|
|
30
|
-
return {
|
|
31
|
-
cacheGet,
|
|
32
|
-
cacheSet,
|
|
33
|
-
fetching,
|
|
34
|
-
getFromCache,
|
|
35
|
-
setInCache,
|
|
36
|
-
multiGetterFromCache,
|
|
37
|
-
setMultiInCache
|
|
38
|
-
};
|
|
39
|
-
};
|
|
40
|
-
(0, vitest_1.beforeEach)(() => {
|
|
41
|
-
vitest_1.vi.clearAllMocks();
|
|
42
|
-
});
|
|
43
|
-
(0, vitest_1.it)('should fetch and set value if skipCache is true', async () => {
|
|
44
|
-
const { cacheGet, cacheSet, fetching } = createMocks();
|
|
45
|
-
fetching.mockResolvedValue('fetchedValue');
|
|
46
|
-
await (0, cache_1.getWithCacheSupport)({ cacheKey, cacheGet, cacheSet, fetching, skipCache: true });
|
|
47
|
-
(0, vitest_1.expect)(fetching).toHaveBeenCalled();
|
|
48
|
-
(0, vitest_1.expect)(cacheSet).toHaveBeenCalledWith('fetchedValue');
|
|
49
|
-
});
|
|
50
|
-
(0, vitest_1.it)('should fetch and set value if cache is empty', async () => {
|
|
51
|
-
const { cacheGet, cacheSet, fetching } = createMocks();
|
|
52
|
-
cacheGet.mockResolvedValue(null);
|
|
53
|
-
fetching.mockResolvedValue('fetchedValue');
|
|
54
|
-
await (0, cache_1.getWithCacheSupport)({ cacheKey, cacheGet, cacheSet, fetching });
|
|
55
|
-
(0, vitest_1.expect)(fetching).toHaveBeenCalled();
|
|
56
|
-
(0, vitest_1.expect)(cacheSet).toHaveBeenCalledWith('fetchedValue');
|
|
57
|
-
});
|
|
58
|
-
(0, vitest_1.it)('should return cached value if available', async () => {
|
|
59
|
-
const { cacheGet, cacheSet, fetching } = createMocks();
|
|
60
|
-
cacheGet.mockResolvedValue('cachedValue');
|
|
61
|
-
const result = await (0, cache_1.getWithCacheSupport)({ cacheKey, cacheGet, cacheSet, fetching });
|
|
62
|
-
(0, vitest_1.expect)(result).toBe('cachedValue');
|
|
63
|
-
(0, vitest_1.expect)(fetching).not.toHaveBeenCalled();
|
|
64
|
-
});
|
|
65
|
-
(0, vitest_1.it)('should retry fetching if an error occurs', async () => {
|
|
66
|
-
const { cacheGet, cacheSet, fetching } = createMocks();
|
|
67
|
-
cacheGet.mockRejectedValue(new Error('Cache error'));
|
|
68
|
-
fetching.mockResolvedValue('fetchedValue');
|
|
69
|
-
const result = await (0, cache_1.getWithCacheSupport)({ cacheKey, cacheGet, cacheSet, fetching });
|
|
70
|
-
(0, vitest_1.expect)(result).toBe('fetchedValue');
|
|
71
|
-
(0, vitest_1.expect)(fetching).toHaveBeenCalled();
|
|
72
|
-
(0, vitest_1.expect)(cacheSet).toHaveBeenCalledWith('fetchedValue');
|
|
73
|
-
});
|
|
74
|
-
(0, vitest_1.it)('should not throw an error if cacheSet fails', async () => {
|
|
75
|
-
const { cacheGet, cacheSet, fetching } = createMocks();
|
|
76
|
-
cacheGet.mockResolvedValue(null);
|
|
77
|
-
cacheSet.mockRejectedValue(new Error('Cache set error'));
|
|
78
|
-
fetching.mockResolvedValue('fetchedValue');
|
|
79
|
-
await (0, vitest_1.expect)((0, cache_1.getWithCacheSupport)({ cacheKey, cacheGet, cacheSet, fetching })).resolves.not.toThrow();
|
|
80
|
-
});
|
|
81
|
-
});
|
|
82
|
-
(0, vitest_1.describe)('getMultipleWithCache', () => {
|
|
83
|
-
const buildMocks = () => {
|
|
84
|
-
const getFromCache = vitest_1.vi.fn();
|
|
85
|
-
const multiGetterFromCache = vitest_1.vi.fn();
|
|
86
|
-
const setInCache = vitest_1.vi.fn();
|
|
87
|
-
const setMultiInCache = vitest_1.vi.fn();
|
|
88
|
-
const getter = vitest_1.vi.fn();
|
|
89
|
-
const multiGetter = vitest_1.vi.fn();
|
|
90
|
-
return {
|
|
91
|
-
getFromCache,
|
|
92
|
-
multiGetterFromCache,
|
|
93
|
-
setInCache,
|
|
94
|
-
setMultiInCache,
|
|
95
|
-
getter,
|
|
96
|
-
multiGetter,
|
|
97
|
-
};
|
|
98
|
-
};
|
|
99
|
-
(0, vitest_1.beforeEach)(() => {
|
|
100
|
-
vitest_1.vi.clearAllMocks();
|
|
101
|
-
});
|
|
102
|
-
(0, vitest_1.it)('should return values from cache if available', async () => {
|
|
103
|
-
const { getFromCache, setInCache, getter } = buildMocks();
|
|
104
|
-
const queries = [{ id: 1 }, { id: 2 }];
|
|
105
|
-
getFromCache.mockResolvedValueOnce({ id: 1, value: 'cachedValue1' }).mockResolvedValueOnce({ id: 2, value: 'cachedValue2' });
|
|
106
|
-
const getMultiple = (0, cache_1.getMultipleWithCache)({ getFromCache, setInCache, getter });
|
|
107
|
-
const result = await getMultiple(queries);
|
|
108
|
-
(0, vitest_1.expect)(result.map(({ value }) => value)).toEqual(['cachedValue1', 'cachedValue2']);
|
|
109
|
-
});
|
|
110
|
-
(0, vitest_1.it)('should fetch and cache values if not in cache', async () => {
|
|
111
|
-
const { getFromCache, setInCache, getter } = buildMocks();
|
|
112
|
-
const queries = [{ id: 1 }, { id: 2 }];
|
|
113
|
-
getFromCache.mockResolvedValueOnce(null).mockResolvedValueOnce(null);
|
|
114
|
-
getter.mockResolvedValueOnce({ id: 1, value: 'fetchedValue1' }).mockResolvedValueOnce({ id: 2, value: 'fetchedValue2' });
|
|
115
|
-
const getMultiple = (0, cache_1.getMultipleWithCache)({ getFromCache, setInCache, getter });
|
|
116
|
-
const result = await getMultiple(queries);
|
|
117
|
-
(0, vitest_1.expect)(result.map(({ value }) => value)).toEqual(['fetchedValue1', 'fetchedValue2']);
|
|
118
|
-
(0, vitest_1.expect)(setInCache).toHaveBeenCalledWith(1, { id: 1, value: 'fetchedValue1' });
|
|
119
|
-
(0, vitest_1.expect)(setInCache).toHaveBeenCalledWith(2, { id: 2, value: 'fetchedValue2' });
|
|
120
|
-
});
|
|
121
|
-
(0, vitest_1.it)('should use multiGetterFromCache if provided', async () => {
|
|
122
|
-
const { multiGetterFromCache, setInCache, getter } = buildMocks();
|
|
123
|
-
const queries = [{ id: 1 }, { id: 2 }];
|
|
124
|
-
multiGetterFromCache.mockResolvedValue([{ id: 1, value: 'cachedValue1' }, { id: 2, value: 'cachedValue2' }]);
|
|
125
|
-
const getMultiple = (0, cache_1.getMultipleWithCache)({ multiGetterFromCache, setInCache, getter });
|
|
126
|
-
const result = await getMultiple(queries);
|
|
127
|
-
(0, vitest_1.expect)(result.map(({ value }) => value)).toEqual(['cachedValue1', 'cachedValue2']);
|
|
128
|
-
});
|
|
129
|
-
(0, vitest_1.it)('should use multiGetter if provided', async () => {
|
|
130
|
-
const { getFromCache, setInCache, multiGetter } = buildMocks();
|
|
131
|
-
const queries = [{ id: 1 }, { id: 2 }];
|
|
132
|
-
getFromCache.mockResolvedValueOnce(null).mockResolvedValueOnce(null);
|
|
133
|
-
multiGetter.mockResolvedValue([{ id: 1, value: 'fetchedValue1' }, { id: 2, value: 'fetchedValue2' }]);
|
|
134
|
-
const getMultiple = (0, cache_1.getMultipleWithCache)({ getFromCache, setInCache, multiGetter });
|
|
135
|
-
const result = await getMultiple(queries);
|
|
136
|
-
(0, vitest_1.expect)(result.map(({ value }) => value)).toEqual(['fetchedValue1', 'fetchedValue2']);
|
|
137
|
-
(0, vitest_1.expect)(setInCache).toHaveBeenCalledWith(1, { id: 1, value: 'fetchedValue1' });
|
|
138
|
-
(0, vitest_1.expect)(setInCache).toHaveBeenCalledWith(2, { id: 2, value: 'fetchedValue2' });
|
|
139
|
-
});
|
|
140
|
-
(0, vitest_1.it)('should use setMultiInCache if provided', async () => {
|
|
141
|
-
const { getFromCache, setInCache, multiGetter, setMultiInCache } = buildMocks();
|
|
142
|
-
const queries = [{ id: 1 }, { id: 2 }];
|
|
143
|
-
getFromCache.mockResolvedValueOnce(null).mockResolvedValueOnce(null);
|
|
144
|
-
multiGetter.mockResolvedValue([{ id: 1, value: 'fetchedValue1' }, { id: 2, value: 'fetchedValue2' }]);
|
|
145
|
-
const getMultiple = (0, cache_1.getMultipleWithCache)({ getFromCache, setInCache, multiGetter, setMultiInCache });
|
|
146
|
-
await getMultiple(queries);
|
|
147
|
-
(0, vitest_1.expect)(setMultiInCache).toHaveBeenCalledWith({ 1: { id: 1, value: 'fetchedValue1' }, 2: { id: 2, value: 'fetchedValue2' } });
|
|
148
|
-
});
|
|
149
|
-
(0, vitest_1.it)('Should not throw and error if setMultiInCache throws an error', async () => {
|
|
150
|
-
const { getFromCache, setInCache, multiGetter, setMultiInCache } = buildMocks();
|
|
151
|
-
const queries = [{ id: 1 }, { id: 2 }];
|
|
152
|
-
getFromCache.mockResolvedValueOnce(null).mockResolvedValueOnce(null);
|
|
153
|
-
multiGetter.mockResolvedValue([{ id: 1, value: 'fetchedValue1' }, { id: 2, value: 'fetchedValue2' }]);
|
|
154
|
-
setMultiInCache.mockRejectedValue(new Error('Error setting cache'));
|
|
155
|
-
const getMultiple = (0, cache_1.getMultipleWithCache)({ getFromCache, setInCache, multiGetter, setMultiInCache });
|
|
156
|
-
await (0, vitest_1.expect)(getMultiple(queries)).resolves.not.toThrow();
|
|
157
|
-
});
|
|
158
|
-
(0, vitest_1.it)('should not throw an error if setInCache fails', async () => {
|
|
159
|
-
const { getFromCache, setInCache, getter } = buildMocks();
|
|
160
|
-
const queries = [{ id: 1 }, { id: 2 }];
|
|
161
|
-
getFromCache.mockResolvedValueOnce(null).mockResolvedValueOnce(null);
|
|
162
|
-
getter.mockResolvedValueOnce({ id: 1, value: 'fetchedValue1' }).mockResolvedValueOnce({ id: 2, value: 'fetchedValue2' });
|
|
163
|
-
setInCache.mockRejectedValue(new Error('Error setting cache'));
|
|
164
|
-
const getMultiple = (0, cache_1.getMultipleWithCache)({ getFromCache, setInCache, getter });
|
|
165
|
-
await (0, vitest_1.expect)(getMultiple(queries)).resolves.not.toThrow();
|
|
166
|
-
});
|
|
167
|
-
});
|
|
168
|
-
});
|
package/lib/locking.d.ts
DELETED
package/lib/locking.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getMutex = exports.wrapWithMutex = void 0;
|
|
4
|
-
const async_mutex_1 = require("async-mutex");
|
|
5
|
-
const CACHE_LOCK_TIMEOUT_MILIS = 3000;
|
|
6
|
-
const LOCK_TIMEOUT_MESSAGE = 'mutex - locking timeout';
|
|
7
|
-
const wrapWithMutex = async (mutex, funcToRun) => {
|
|
8
|
-
const release = await mutex.acquire();
|
|
9
|
-
try {
|
|
10
|
-
await funcToRun();
|
|
11
|
-
}
|
|
12
|
-
finally {
|
|
13
|
-
release();
|
|
14
|
-
}
|
|
15
|
-
};
|
|
16
|
-
exports.wrapWithMutex = wrapWithMutex;
|
|
17
|
-
const getMutex = () => (0, async_mutex_1.withTimeout)(new async_mutex_1.Mutex(), CACHE_LOCK_TIMEOUT_MILIS, new Error(LOCK_TIMEOUT_MESSAGE));
|
|
18
|
-
exports.getMutex = getMutex;
|
package/lib/logger.d.ts
DELETED
package/lib/logger.js
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const logger_1 = __importDefault(require("@autofleet/logger"));
|
|
7
|
-
const logger = (0, logger_1.default)();
|
|
8
|
-
exports.default = logger;
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import type RedisCache from '../redis';
|
|
2
|
-
export interface ModelOptions {
|
|
3
|
-
name: string;
|
|
4
|
-
associations?: AssociationOptions[];
|
|
5
|
-
}
|
|
6
|
-
export interface AssociationOptions {
|
|
7
|
-
name: string;
|
|
8
|
-
alias: string;
|
|
9
|
-
accessKey?: string;
|
|
10
|
-
innerAssociation?: AssociationOptions;
|
|
11
|
-
}
|
|
12
|
-
export interface Adapter {
|
|
13
|
-
ormInstance: any;
|
|
14
|
-
getModel: (modelName: string) => any;
|
|
15
|
-
injectGetWithCacheFunction: (cache: RedisCache, modelOptions: ModelOptions) => void;
|
|
16
|
-
addInvalidationHooks: (cache: RedisCache, modelOptions: ModelOptions) => void;
|
|
17
|
-
}
|
package/lib/orm-cache/adapter.js
DELETED
package/lib/orm-cache/errors.js
DELETED
package/lib/orm-cache/index.d.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { ModelOptions } from './adapter';
|
|
2
|
-
export declare enum ORMTypes {
|
|
3
|
-
SEQUELIZE = "sequelize"
|
|
4
|
-
}
|
|
5
|
-
interface ORMCacheOptions {
|
|
6
|
-
type: ORMTypes;
|
|
7
|
-
models: ModelOptions[];
|
|
8
|
-
ormInstance: any;
|
|
9
|
-
debug: boolean;
|
|
10
|
-
}
|
|
11
|
-
export declare const ORMCache: (options: ORMCacheOptions) => void;
|
|
12
|
-
export {};
|
package/lib/orm-cache/index.js
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.ORMCache = exports.ORMTypes = void 0;
|
|
7
|
-
const sequelize_adapter_1 = __importDefault(require("./sequelize-adapter"));
|
|
8
|
-
const errors_1 = require("./errors");
|
|
9
|
-
const redis_1 = __importDefault(require("../redis"));
|
|
10
|
-
const logger_1 = __importDefault(require("../logger"));
|
|
11
|
-
var ORMTypes;
|
|
12
|
-
(function (ORMTypes) {
|
|
13
|
-
ORMTypes["SEQUELIZE"] = "sequelize";
|
|
14
|
-
})(ORMTypes || (exports.ORMTypes = ORMTypes = {}));
|
|
15
|
-
const ORMInstanceFactory = (options) => {
|
|
16
|
-
switch (options.type) {
|
|
17
|
-
case ORMTypes.SEQUELIZE: {
|
|
18
|
-
return new sequelize_adapter_1.default(options.ormInstance, options.debug);
|
|
19
|
-
}
|
|
20
|
-
default: {
|
|
21
|
-
throw new errors_1.UnsupportedOrmTypeError(`ORM type ${options.type} is unsupported at the moment`);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
const ORMCache = (options) => {
|
|
26
|
-
const { models } = options;
|
|
27
|
-
logger_1.default.info('Starting ORM Cache', { options });
|
|
28
|
-
const adapter = ORMInstanceFactory(options);
|
|
29
|
-
const cache = new redis_1.default({
|
|
30
|
-
host: process.env.REDIS_HOST,
|
|
31
|
-
port: process.env.REDIS_PORT,
|
|
32
|
-
});
|
|
33
|
-
// eslint-disable-next-line array-callback-return
|
|
34
|
-
models.map((modelOptions) => {
|
|
35
|
-
adapter.addInvalidationHooks(cache, modelOptions);
|
|
36
|
-
adapter.injectGetWithCacheFunction(cache, modelOptions);
|
|
37
|
-
});
|
|
38
|
-
};
|
|
39
|
-
exports.ORMCache = ORMCache;
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { Sequelize } from 'sequelize';
|
|
2
|
-
import { Adapter, AssociationOptions, ModelOptions } from './adapter';
|
|
3
|
-
import RedisCache from '../redis';
|
|
4
|
-
export default class SequelizeAdapter implements Adapter {
|
|
5
|
-
ormInstance: Sequelize;
|
|
6
|
-
debugMode: boolean;
|
|
7
|
-
constructor(sequelize: Sequelize, debug: any);
|
|
8
|
-
getModel(modelName: string): any;
|
|
9
|
-
getModelDependencies(modelName: string): AssociationOptions[];
|
|
10
|
-
debug(message: any, payload: any): void;
|
|
11
|
-
injectGetWithCacheFunction(cache: RedisCache, modelOptions: ModelOptions): void;
|
|
12
|
-
addInvalidationHooks(cache: RedisCache, modelOptions: ModelOptions): void;
|
|
13
|
-
}
|
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const logger_1 = __importDefault(require("../logger"));
|
|
7
|
-
const util_1 = require("util");
|
|
8
|
-
const { AF_SERVICE_NAME } = process.env;
|
|
9
|
-
const ORM_CACHE_PREFIX = 'ormCache';
|
|
10
|
-
const INVALIDATION_HOOKS = ['afterSave', 'afterUpdate', 'afterDestroy'];
|
|
11
|
-
const BULK_HOOKS = ['beforeBulkUpdate', 'beforeBulkDestroy'];
|
|
12
|
-
const generateInstanceKey = (name, id) => `${AF_SERVICE_NAME}:${ORM_CACHE_PREFIX}:${name}_${id}`;
|
|
13
|
-
const generateDependencyKey = (modelName, associationName, associationId) => `${AF_SERVICE_NAME}:${ORM_CACHE_PREFIX}:${modelName}_${associationName}_${associationId}_DEPENDENCIES`;
|
|
14
|
-
const getInstanceDependencyKeys = (modelOptions, instance) => {
|
|
15
|
-
const keys = modelOptions.associations
|
|
16
|
-
.filter((associationOptions) => instance[associationOptions.alias])
|
|
17
|
-
.map((associationOptions) => {
|
|
18
|
-
const accessKey = associationOptions.accessKey;
|
|
19
|
-
const depKeys = [];
|
|
20
|
-
if (Array.isArray(instance[associationOptions.alias])) {
|
|
21
|
-
instance[associationOptions.alias].map(associationInstance => {
|
|
22
|
-
depKeys.push(generateDependencyKey(modelOptions.name, associationOptions.name, associationInstance[accessKey]));
|
|
23
|
-
if (associationOptions.innerAssociation &&
|
|
24
|
-
associationInstance[associationOptions.innerAssociation.alias]) {
|
|
25
|
-
depKeys.push(generateDependencyKey(modelOptions.name, associationOptions.innerAssociation.name, associationInstance[associationOptions.innerAssociation.alias][associationOptions.innerAssociation.accessKey]));
|
|
26
|
-
}
|
|
27
|
-
});
|
|
28
|
-
return depKeys;
|
|
29
|
-
}
|
|
30
|
-
return [
|
|
31
|
-
generateDependencyKey(modelOptions.name, associationOptions.name, instance[associationOptions.alias][accessKey]),
|
|
32
|
-
];
|
|
33
|
-
});
|
|
34
|
-
return keys.reduce((flattenArray, array) => flattenArray.concat(array), []);
|
|
35
|
-
};
|
|
36
|
-
const handleTransactionHook = (instance, options, func) => {
|
|
37
|
-
const { transaction } = options;
|
|
38
|
-
if (transaction) {
|
|
39
|
-
transaction.afterCommit(() => func(instance));
|
|
40
|
-
}
|
|
41
|
-
else {
|
|
42
|
-
func(instance);
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
|
-
class SequelizeAdapter {
|
|
46
|
-
constructor(sequelize, debug) {
|
|
47
|
-
this.ormInstance = sequelize;
|
|
48
|
-
this.debugMode = debug;
|
|
49
|
-
}
|
|
50
|
-
getModel(modelName) {
|
|
51
|
-
return this.ormInstance.models[modelName];
|
|
52
|
-
}
|
|
53
|
-
getModelDependencies(modelName) {
|
|
54
|
-
const { associations } = this.ormInstance.models[modelName];
|
|
55
|
-
return [
|
|
56
|
-
...Object.keys(associations).map(association => {
|
|
57
|
-
const sequelizeAssociation = associations[association];
|
|
58
|
-
const associationOptions = {
|
|
59
|
-
alias: sequelizeAssociation.as,
|
|
60
|
-
name: sequelizeAssociation.target.name,
|
|
61
|
-
accessKey: sequelizeAssociation.target.primaryKeyAttribute,
|
|
62
|
-
};
|
|
63
|
-
if (sequelizeAssociation.through) {
|
|
64
|
-
const relationModel = sequelizeAssociation.through.model;
|
|
65
|
-
associationOptions.innerAssociation = {
|
|
66
|
-
alias: relationModel.name,
|
|
67
|
-
name: relationModel.name,
|
|
68
|
-
accessKey: relationModel.primaryKeyAttribute,
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
return associationOptions;
|
|
72
|
-
}),
|
|
73
|
-
];
|
|
74
|
-
}
|
|
75
|
-
debug(message, payload) {
|
|
76
|
-
if (this.debugMode) {
|
|
77
|
-
logger_1.default.info(`[ORM_CACHE Debug] ${message}`, payload);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
injectGetWithCacheFunction(cache, modelOptions) {
|
|
81
|
-
const addDependencies = async (instance) => {
|
|
82
|
-
const dependencyKeys = getInstanceDependencyKeys(modelOptions, instance);
|
|
83
|
-
const instanceKey = generateInstanceKey(modelOptions.name, instance.id);
|
|
84
|
-
this.debug('Adding dependencies', { instanceKey, dependencyKeys });
|
|
85
|
-
const addDependenciesMulti = cache.getClient().multi();
|
|
86
|
-
const addDependenciesMultiAsync = (0, util_1.promisify)(addDependenciesMulti.exec).bind(addDependenciesMulti);
|
|
87
|
-
dependencyKeys.reduce((multi, key) => multi.sadd(key, instanceKey), addDependenciesMulti);
|
|
88
|
-
return addDependenciesMultiAsync();
|
|
89
|
-
};
|
|
90
|
-
const model = this.getModel(modelOptions.name);
|
|
91
|
-
model.findByPkCached = async (id, scopes, options) => {
|
|
92
|
-
const cacheKey = generateInstanceKey(modelOptions.name, id);
|
|
93
|
-
let value = JSON.parse(await cache.getClient().getAsync(cacheKey));
|
|
94
|
-
if (!value) {
|
|
95
|
-
this.debug('Value not found in cache, looking in db', { id, cacheKey });
|
|
96
|
-
value = await model.scope(scopes).findByPk(id, options);
|
|
97
|
-
this.debug('Value from DB', { value: value || 'not found', cacheKey });
|
|
98
|
-
await Promise.all([
|
|
99
|
-
cache.getClient().setAsync(cacheKey, JSON.stringify(value)),
|
|
100
|
-
value && addDependencies(value),
|
|
101
|
-
]);
|
|
102
|
-
}
|
|
103
|
-
else {
|
|
104
|
-
value = this.getModel(modelOptions.name).build(value, { isNewRecord: false, include: options.include });
|
|
105
|
-
this.debug('Found cached value', { value, id, cacheKey });
|
|
106
|
-
}
|
|
107
|
-
return value;
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
addInvalidationHooks(cache, modelOptions) {
|
|
111
|
-
const invalidateModelInstance = async (instance) => {
|
|
112
|
-
const dependencyKeys = getInstanceDependencyKeys(modelOptions, instance);
|
|
113
|
-
const instanceKey = generateInstanceKey(modelOptions.name, instance.id);
|
|
114
|
-
this.debug('Removing dependencies', { instance, instanceKey, dependencyKeys });
|
|
115
|
-
const removeMulti = cache.getClient().multi();
|
|
116
|
-
const removeMultiAsync = (0, util_1.promisify)(removeMulti.exec).bind(removeMulti);
|
|
117
|
-
dependencyKeys.map(key => removeMulti.srem(key, instanceKey));
|
|
118
|
-
removeMulti.del(instanceKey);
|
|
119
|
-
return removeMultiAsync();
|
|
120
|
-
};
|
|
121
|
-
const invalidateModelInstanceByAssociation = async (association, associationId) => {
|
|
122
|
-
const dependentInstancesKeys = await cache.getClient().smembersAsync(generateDependencyKey(modelOptions.name, association, associationId));
|
|
123
|
-
this.debug('Invalidating dependent instances', { dependentInstancesKeys });
|
|
124
|
-
const removeMulti = cache.getClient().multi();
|
|
125
|
-
const removeMultiAsync = (0, util_1.promisify)(removeMulti.exec).bind(removeMulti);
|
|
126
|
-
const dependenciesToRemove = await Promise.all(dependentInstancesKeys.map(async (instanceKey) => {
|
|
127
|
-
const instance = JSON.parse(await cache.getClient().getAsync(instanceKey));
|
|
128
|
-
if (!instance) {
|
|
129
|
-
return [];
|
|
130
|
-
}
|
|
131
|
-
const dependencyKeys = getInstanceDependencyKeys(modelOptions, instance);
|
|
132
|
-
dependencyKeys.reduce((multi, key) => multi.srem(key, instanceKey), removeMulti);
|
|
133
|
-
removeMulti.del(instanceKey);
|
|
134
|
-
return dependencyKeys;
|
|
135
|
-
}));
|
|
136
|
-
this.debug('Removing dependencies', { dependentInstancesKeys, dependenciesToRemove });
|
|
137
|
-
return removeMultiAsync();
|
|
138
|
-
};
|
|
139
|
-
const model = this.getModel(modelOptions.name);
|
|
140
|
-
INVALIDATION_HOOKS.map(hook => model.addHook(hook, (instance, options) => handleTransactionHook(instance, options, instance => invalidateModelInstance(instance))));
|
|
141
|
-
BULK_HOOKS.map(hook => model.addHook(hook, options => options.individualHook = true));
|
|
142
|
-
modelOptions.associations = this.getModelDependencies(modelOptions.name);
|
|
143
|
-
this.debug(`Adding Invalidations Hooks to ${modelOptions.name}'s associations`, { associations: modelOptions.associations });
|
|
144
|
-
modelOptions.associations.map((associationOptions) => {
|
|
145
|
-
const associationModel = this.getModel(associationOptions.name);
|
|
146
|
-
INVALIDATION_HOOKS.map(hook => associationModel.addHook(hook, (instance, options) => handleTransactionHook(instance, options, associationInstance => invalidateModelInstanceByAssociation(associationOptions.name, associationInstance[associationOptions.accessKey]))));
|
|
147
|
-
BULK_HOOKS.map(hook => associationModel.addHook(hook, options => options.individualHook = true));
|
|
148
|
-
if (associationOptions.innerAssociation) {
|
|
149
|
-
const innerAssociationModel = this.getModel(associationOptions.innerAssociation.name);
|
|
150
|
-
INVALIDATION_HOOKS.map(hook => innerAssociationModel.addHook(hook, (instance, options) => handleTransactionHook(instance, options, innerAssociationInstance => invalidateModelInstanceByAssociation(associationOptions.innerAssociation.name, innerAssociationInstance[associationOptions.innerAssociation.accessKey]))));
|
|
151
|
-
BULK_HOOKS.map(hook => innerAssociationModel.addHook(hook, options => options.individualHook = true));
|
|
152
|
-
}
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
exports.default = SequelizeAdapter;
|
package/lib/promise-utils.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const promisifyAll: (obj: any) => void;
|
package/lib/promise-utils.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.promisifyAll = void 0;
|
|
4
|
-
const util_1 = require("util");
|
|
5
|
-
const promisifyAll = (obj) => {
|
|
6
|
-
Object.keys(obj).forEach((key) => {
|
|
7
|
-
if (typeof obj[key] === 'function') {
|
|
8
|
-
obj[`${key}Async`] = (0, util_1.promisify)(obj[key]);
|
|
9
|
-
// To support the `isPromisified` check in `bluebird`, we need to set the `__isPromisified__` property to `true`.
|
|
10
|
-
Object.defineProperty(obj[`${key}Async`], '__isPromisified__', {
|
|
11
|
-
value: true,
|
|
12
|
-
configurable: true,
|
|
13
|
-
enumerable: false,
|
|
14
|
-
writable: true
|
|
15
|
-
});
|
|
16
|
-
}
|
|
17
|
-
});
|
|
18
|
-
};
|
|
19
|
-
exports.promisifyAll = promisifyAll;
|
package/lib/redis/errors.d.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { RedisError } from 'redis';
|
|
2
|
-
declare class RedisCacheError extends RedisError {
|
|
3
|
-
innerError: any;
|
|
4
|
-
message: string;
|
|
5
|
-
constructor(msg: string, err: any);
|
|
6
|
-
toString(): string;
|
|
7
|
-
}
|
|
8
|
-
declare class RedisLockError extends RedisCacheError {
|
|
9
|
-
key: string;
|
|
10
|
-
constructor(msg: string, err: any, key: string);
|
|
11
|
-
toString(): string;
|
|
12
|
-
}
|
|
13
|
-
export { RedisCacheError, RedisLockError, };
|