@vulcan-sql/core
Version:
Core package of VulcanSQL
133 lines • 6.83 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.CacheLayerRefresher = void 0;
const tslib_1 = require("tslib");
const ms_1 = require("ms");
const lodash_1 = require("lodash");
const toad_scheduler_1 = require("toad-scheduler");
const inversify_1 = require("inversify");
const types_1 = require("../../containers/types");
const models_1 = require("../../models/index");
const errors_1 = require("../utils/errors");
const utils_1 = require("../utils");
const moment = require("moment");
var RefreshResult;
(function (RefreshResult) {
RefreshResult["SUCCESS"] = "SUCCESS";
RefreshResult["FAILED"] = "FAILED";
})(RefreshResult || (RefreshResult = {}));
let CacheLayerRefresher = class CacheLayerRefresher {
constructor(loader, activityLoggers) {
this.scheduler = new toad_scheduler_1.ToadScheduler();
this.logger = (0, utils_1.getLogger)({ scopeName: 'CORE' });
this.cacheLoader = loader;
this.activityLoggers = activityLoggers;
}
start(schemas, runImmediately = true) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
// check if the cache table name is duplicated more than one API schemas
this.checkDuplicateCacheTableName(schemas);
// check if the index name is duplicated more than one API schemas
this.checkDuplicateIndex(schemas);
// traverse each cache table of each schema
yield Promise.all(schemas.map((schema) => tslib_1.__awaiter(this, void 0, void 0, function* () {
// skip the schema by return if not set the cache
if (!schema.cache)
return;
return yield Promise.all(schema.cache.map((cache) => tslib_1.__awaiter(this, void 0, void 0, function* () {
const { cacheTableName, profile, refreshTime } = cache;
// replace the '/' tp '_' to avoid the file path issue for templateSource
const templateName = schema.templateSource.replace('/', '_');
// If refresh time is set, use the scheduler to schedule the load task for refresh
if (refreshTime === null || refreshTime === void 0 ? void 0 : refreshTime.every) {
// use the work id for task to know which failed when execute and get the job to by id.
const workerId = `${templateName}/${profile}/${cacheTableName}`;
const milliseconds = (0, ms_1.default)(refreshTime.every);
const refreshJob = new toad_scheduler_1.SimpleIntervalJob({ milliseconds, runImmediately }, new toad_scheduler_1.AsyncTask(workerId, () => tslib_1.__awaiter(this, void 0, void 0, function* () {
yield this.loadCacheAndSendActivityLog(schema, cache);
})), { preventOverrun: true, id: workerId });
// add the job to schedule cache refresh task
this.scheduler.addIntervalJob(refreshJob);
}
else {
yield this.loadCacheAndSendActivityLog(schema, cache);
}
})));
})));
});
}
/**
* Stop the cache layer loader
*/
stop() {
this.scheduler.stop();
}
loadCacheAndSendActivityLog(schema, cache) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const { urlPath } = schema;
const { sql } = cache;
let refreshResult = RefreshResult.SUCCESS;
const now = moment.utc().format('YYYY-MM-DD HH:mm:ss');
const templateName = schema.templateSource.replace('/', '_');
try {
// get the current time in format of UTC
yield this.cacheLoader.load(templateName, cache);
}
catch (error) {
refreshResult = RefreshResult.FAILED;
this.logger.debug(`Failed to refresh cache: ${error}`);
}
finally {
// send activity log
const content = {
isSuccess: refreshResult === RefreshResult.SUCCESS ? true : false,
activityLogType: models_1.ActivityLogType.CACHE_REFRESH,
logTime: now,
urlPath,
sql,
};
const activityLoggers = this.getActivityLoggers();
for (const activityLogger of activityLoggers)
activityLogger.log(content).catch((err) => {
this.logger.debug(`Failed to log activity after refreshing cache: ${err}`);
});
}
});
}
getActivityLoggers() {
return this.activityLoggers.filter((logger) => logger.isEnabled());
}
checkDuplicateCacheTableName(schemas) {
const tableNames = schemas
// => [[table1, table2], [table1, table3], [table4]]
.map((schema) => { var _a; return (_a = schema.cache) === null || _a === void 0 ? void 0 : _a.map((cache) => cache.cacheTableName); })
// => [table1, table2, table1, table3, table4]
.flat()
// use filter to make sure it has value and pick it.
.filter((tableName) => tableName);
if ((0, lodash_1.uniq)(tableNames).length !== tableNames.length)
throw new errors_1.ConfigurationError('Not allow to set same cache table name more than one API schema.');
}
checkDuplicateIndex(schemas) {
const indexNames = schemas
// => [[table1_idx, table1_idx2, table2_idx], [table1_idx, table3_idx], [table4_idx]]
.map((schema) => {
var _a;
return (_a = schema.cache) === null || _a === void 0 ? void 0 : _a.map((cache) => (cache.indexes ? Object.keys(cache.indexes) : [])).flat();
})
// => [table1_idx, table1_idx2, table2_idx, table1_idx, table3_idx, table4_idx]
.flat()
// use filter to make sure it has value and pick it.
.filter((indexName) => indexName);
if ((0, lodash_1.uniq)(indexNames).length !== indexNames.length)
throw new errors_1.ConfigurationError('Not allow to set same index name more than one API schema.');
}
};
CacheLayerRefresher = tslib_1.__decorate([
(0, inversify_1.injectable)(),
tslib_1.__param(0, (0, inversify_1.inject)(types_1.TYPES.CacheLayerLoader)),
tslib_1.__param(1, (0, inversify_1.multiInject)(types_1.TYPES.Extension_ActivityLogger)),
tslib_1.__metadata("design:paramtypes", [Object, Array])
], CacheLayerRefresher);
exports.CacheLayerRefresher = CacheLayerRefresher;
//# sourceMappingURL=cacheLayerRefresher.js.map