@autofleet/matmon 1.0.6 → 2.0.0-beta-40

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/lib/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  import { getNewLRU, getWithCacheSupport } from './cache';
2
2
  import RedisCache from './redis';
3
- export { getNewLRU, getWithCacheSupport, RedisCache, };
3
+ import { ORMCache } from './orm-cache';
4
+ export { getNewLRU, getWithCacheSupport, RedisCache, ORMCache, };
package/lib/index.js CHANGED
@@ -3,9 +3,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.RedisCache = exports.getWithCacheSupport = exports.getNewLRU = void 0;
6
+ exports.ORMCache = exports.RedisCache = exports.getWithCacheSupport = exports.getNewLRU = void 0;
7
7
  const cache_1 = require("./cache");
8
8
  Object.defineProperty(exports, "getNewLRU", { enumerable: true, get: function () { return cache_1.getNewLRU; } });
9
9
  Object.defineProperty(exports, "getWithCacheSupport", { enumerable: true, get: function () { return cache_1.getWithCacheSupport; } });
10
10
  const redis_1 = __importDefault(require("./redis"));
11
11
  exports.RedisCache = redis_1.default;
12
+ const orm_cache_1 = require("./orm-cache");
13
+ Object.defineProperty(exports, "ORMCache", { enumerable: true, get: function () { return orm_cache_1.ORMCache; } });
@@ -0,0 +1,2 @@
1
+ declare const logger: any;
2
+ export default logger;
package/lib/logger.js ADDED
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const Logger = require('@autofleet/logger');
4
+ const logger = Logger();
5
+ exports.default = logger;
@@ -0,0 +1,14 @@
1
+ import RedisCache from '../redis';
2
+ export interface ModelOptions {
3
+ name: string;
4
+ associations: {
5
+ name: string;
6
+ alias: string;
7
+ }[];
8
+ }
9
+ export interface Adapter {
10
+ ormInstance: any;
11
+ getModel: (modelName: string) => any;
12
+ injectGetWithCacheFunction: (cache: RedisCache, modelOptions: ModelOptions) => void;
13
+ addInvalidationHooks: (cache: RedisCache, modelOptions: ModelOptions) => void;
14
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,2 @@
1
+ export declare class UnsupportedOrmTypeError extends Error {
2
+ }
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.UnsupportedOrmTypeError = void 0;
4
+ class UnsupportedOrmTypeError extends Error {
5
+ }
6
+ exports.UnsupportedOrmTypeError = UnsupportedOrmTypeError;
@@ -0,0 +1,13 @@
1
+ import { ModelOptions } from './adapter';
2
+ declare enum ORMTypes {
3
+ SEQUELIZE = "sequelize"
4
+ }
5
+ interface ORMCacheOptions {
6
+ type: ORMTypes;
7
+ models: ModelOptions[];
8
+ ormInstance: any;
9
+ debug: boolean;
10
+ ttl: number;
11
+ }
12
+ export declare const ORMCache: (options: ORMCacheOptions) => void;
13
+ export {};
@@ -0,0 +1,39 @@
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 = 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 || (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
+ exports.ORMCache = (options) => {
26
+ const { type, models, ormInstance, } = 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
+ ttl: options.ttl || 90,
33
+ });
34
+ // eslint-disable-next-line array-callback-return
35
+ models.map((modelOptions) => {
36
+ adapter.addInvalidationHooks(cache, modelOptions);
37
+ adapter.injectGetWithCacheFunction(cache, modelOptions);
38
+ });
39
+ };
@@ -0,0 +1,12 @@
1
+ import { Sequelize } from 'sequelize';
2
+ import { Adapter, 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
+ debug(message: any, payload: any): void;
10
+ injectGetWithCacheFunction(cache: RedisCache, modelOptions: ModelOptions): void;
11
+ addInvalidationHooks(cache: RedisCache, modelOptions: ModelOptions): void;
12
+ }
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ const logger_1 = __importDefault(require("../logger"));
16
+ const util_1 = require("util");
17
+ const { AF_SERVICE_NAME } = process.env;
18
+ const INVALIDATION_HOOKS = ['afterSave', 'afterUpdate', 'afterDestroy'];
19
+ const BULK_HOOKS = ['beforeBulkUpdate', 'beforeBulkDestroy'];
20
+ const generateInstanceKey = (modelOptions, id) => `${AF_SERVICE_NAME}:${modelOptions.name}_${id}_INCLUDING_${modelOptions.associations.map(a => a.name).join('_')}`;
21
+ const generateDependencyKey = (modelName, associationName, associationId) => `${AF_SERVICE_NAME}:${modelName}_${associationName}_${associationId}_DEPENDENCIES`;
22
+ const handleTransactionHook = (instance, options, func) => {
23
+ const { transaction } = options;
24
+ if (transaction) {
25
+ transaction.afterCommit(() => func(instance));
26
+ }
27
+ else {
28
+ func(instance);
29
+ }
30
+ };
31
+ class SequelizeAdapter {
32
+ constructor(sequelize, debug) {
33
+ this.ormInstance = sequelize;
34
+ this.debugMode = debug;
35
+ }
36
+ getModel(modelName) {
37
+ return this.ormInstance.models[modelName];
38
+ }
39
+ debug(message, payload) {
40
+ if (this.debugMode) {
41
+ logger_1.default.info(`[ORM_CACHE Debug] ${message}`, payload);
42
+ }
43
+ }
44
+ injectGetWithCacheFunction(cache, modelOptions) {
45
+ const addDependencies = (modelName, instance) => __awaiter(this, void 0, void 0, function* () {
46
+ const dependencyKeys = modelOptions.associations.map(association => generateDependencyKey(modelName, association.name, instance[association.alias].id));
47
+ const instanceKey = generateInstanceKey(modelOptions, instance.id);
48
+ this.debug('Adding dependencies', { instanceKey, dependencyKeys });
49
+ const addDependenciesMulti = cache.getClient().multi();
50
+ const addDependenciesMultiAsync = util_1.promisify(addDependenciesMulti.exec).bind(addDependenciesMulti);
51
+ dependencyKeys.reduce((multi, key) => multi.sadd(key, instanceKey), addDependenciesMulti);
52
+ return addDependenciesMultiAsync();
53
+ });
54
+ const model = this.getModel(modelOptions.name);
55
+ model.findByPkCached = (id, scopes, options) => __awaiter(this, void 0, void 0, function* () {
56
+ const cacheKey = generateInstanceKey(modelOptions, id);
57
+ let value = JSON.parse(yield cache.getClient().getAsync(cacheKey));
58
+ if (!value) {
59
+ this.debug('Value not found in cache, looking in db', { id, cacheKey });
60
+ value = yield model.scope(scopes).findByPk(id, options);
61
+ this.debug('Value from DB', { value: value || 'not found', cacheKey });
62
+ const [setRes] = yield Promise.all([
63
+ cache.getClient().setAsync(cacheKey, JSON.stringify(value)),
64
+ value && addDependencies(modelOptions.name, value),
65
+ ]);
66
+ }
67
+ else {
68
+ this.debug('Found cached value', { value, id, cacheKey });
69
+ }
70
+ return value;
71
+ });
72
+ }
73
+ addInvalidationHooks(cache, modelOptions) {
74
+ const invalidateModelInstance = (modelName, instance) => __awaiter(this, void 0, void 0, function* () {
75
+ const dependencyKeys = modelOptions.associations
76
+ .filter(association => instance[association.alias])
77
+ .map(association => generateDependencyKey(modelName, association.name, instance[association.alias].id));
78
+ const instanceKey = generateInstanceKey(modelOptions, instance.id);
79
+ this.debug('Removing dependencies', { instance, instanceKey, dependencyKeys });
80
+ const removeMulti = cache.getClient().multi();
81
+ const removeMultiAsync = util_1.promisify(removeMulti.exec).bind(removeMulti);
82
+ dependencyKeys.map(key => removeMulti.srem(key, instanceKey));
83
+ removeMulti.del(instanceKey);
84
+ return removeMultiAsync();
85
+ });
86
+ const invalidateModelInstanceByAssociation = (modelName, association, associationId) => __awaiter(this, void 0, void 0, function* () {
87
+ const dependentInstancesKeys = yield cache.getClient().smembersAsync(generateDependencyKey(modelName, association, associationId));
88
+ this.debug('Invalidating dependent instances', { dependentInstancesKeys });
89
+ const removeMulti = cache.getClient().multi();
90
+ const removeMultiAsync = util_1.promisify(removeMulti.exec).bind(removeMulti);
91
+ const dependenciesToRemove = yield Promise.all(dependentInstancesKeys.map((instanceKey) => __awaiter(this, void 0, void 0, function* () {
92
+ const instance = JSON.parse(yield cache.getClient().getAsync(instanceKey));
93
+ const dependencyKeys = modelOptions.associations
94
+ .filter(association => instance[association.alias])
95
+ .map(association => generateDependencyKey(modelName, association.name, instance[association.alias].id));
96
+ dependencyKeys.reduce((multi, key) => multi.srem(key, instanceKey), removeMulti);
97
+ removeMulti.del(instanceKey);
98
+ return dependencyKeys;
99
+ })));
100
+ this.debug('Removing dependencies', { dependentInstancesKeys, dependenciesToRemove });
101
+ return removeMultiAsync();
102
+ });
103
+ const model = this.getModel(modelOptions.name);
104
+ INVALIDATION_HOOKS.map(hook => model.addHook(hook, (instance, options) => handleTransactionHook(instance, options, instance => invalidateModelInstance(modelOptions.name, instance))));
105
+ BULK_HOOKS.map(hook => model.addHook(hook, options => options.individualHook = true));
106
+ modelOptions.associations.map(association => {
107
+ const associationModel = this.getModel(association.name);
108
+ INVALIDATION_HOOKS.map(hook => associationModel.addHook(hook, (instance, options) => handleTransactionHook(instance, options, ({ id }) => invalidateModelInstanceByAssociation(modelOptions.name, association.name, id))));
109
+ BULK_HOOKS.map(hook => associationModel.addHook(hook, options => options.individualHook = true));
110
+ });
111
+ }
112
+ }
113
+ exports.default = SequelizeAdapter;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autofleet/matmon",
3
- "version": "1.0.6",
3
+ "version": "2.0.0-beta-40",
4
4
  "description": "manage cache",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",