UNPKG

@voicenter-team/mysql-dynamic-cluster

Version:

Galera cluster with implementation of dynamic choose mysql server for queries, caching, hashing it and metrics

235 lines 9.39 kB
"use strict"; /** * Created by Bohdan on Sep, 2021 */ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ClusterHashing = void 0; const Logger_1 = __importDefault(require("../utils/Logger")); const Timer_1 = require("../utils/Timer"); const fs_1 = require("fs"); const path_1 = require("path"); class ClusterHashing { /** * @param cluster cluster for what hashing data * @param clusterName cluster name used for prefix * @param options cluster settings */ constructor(cluster, clusterName, options) { this.connected = false; this._serviceNodeMap = new Map(); // key: serviceID; value: nodeID this._databaseVersion = 1; Logger_1.default.debug("Configuring hashing in cluster..."); this._cluster = cluster; this._nextCheckTime = options.nextCheckTime; this._database = `${clusterName}_${options.dbName}`; this._timer = new Timer_1.Timer(this._checkHashing.bind(this)); Logger_1.default.debug("Cluster hashing configured"); } /** * Activate hashing and create helper db if needed */ connect() { return __awaiter(this, void 0, void 0, function* () { try { if (!(yield this._isDatabaseVersionEquals())) { yield this._cluster.query(`DROP SCHEMA IF EXISTS ${this._database};`, null, { maxRetry: 1, redis: false }); yield this._createDB(); } Logger_1.default.info(`Database ${this._database} created for hashing`); yield this._insertNodes(); this._checkHashing(); this.connected = true; } catch (err) { throw err; } }); } /** * Stop timer for hashing check */ stop() { this._timer.dispose(); this.connected = false; Logger_1.default.info("Checking hashing in the cluster stopped"); } /** * Update node for service in db * @param serviceId service what need to hashing * @param nodeId pool where hashing data */ updateNodeForService(serviceId, nodeId) { return __awaiter(this, void 0, void 0, function* () { try { yield this._cluster.query('CALL SP_NodeServiceUpdate(?, ?);', [serviceId, nodeId], { maxRetry: 1, database: this._database, redis: false }); this._serviceNodeMap.set(serviceId, nodeId); } catch (e) { Logger_1.default.error(e.message); } }); } /** * Get node / pool from hashing by service ID * @param serviceId service ID */ getNodeByService(serviceId) { return this._serviceNodeMap.get(serviceId); } /** * Create database for hashing * @private */ _createDB() { return __awaiter(this, void 0, void 0, function* () { try { // const extraPath = ''; const extraPath = '../'; const sqlLocations = { tables: extraPath + '../../assets/sql/create_hashing_database/tables/', routines: extraPath + '../../assets/sql/create_hashing_database/routines/', metadata: extraPath + '../../assets/sql/create_hashing_database/metadata/' }; Logger_1.default.debug(`Creating database ${this._database} and procedures for hashing...`); yield this._cluster.query(`CREATE SCHEMA IF NOT EXISTS \`${this._database}\` COLLATE utf8_general_ci;`, null, { maxRetry: 1, redis: false }); const sqls = []; sqls.push(...this._readFilesInDir((0, path_1.join)(__dirname, sqlLocations.tables)).fileContents); const routinesSqls = this._readFilesInDir((0, path_1.join)(__dirname, sqlLocations.routines)); sqls.push(...routinesSqls.fileContents); const sqlsMetadata = this._readFilesInDir((0, path_1.join)(__dirname, sqlLocations.metadata)).fileContents; const sqlsDrop = []; routinesSqls.fileNames.forEach(name => { sqlsDrop.push(`DROP PROCEDURE IF EXISTS ${name};`); }); yield this._cluster.pools[0].multiStatementQuery(sqlsDrop, { database: this._database }); yield this._cluster.pools[0].multiStatementQuery(sqls, { database: this._database }); yield this._cluster.pools[0].multiStatementQuery(sqlsMetadata, { database: this._database }); yield this._cluster.query(`INSERT INTO metadata (version) VALUES (${this._databaseVersion});`, null, { maxRetry: 1, database: this._database, redis: false }); } catch (e) { throw e; } }); } /** * Check if database version is the same in the server * @private */ _isDatabaseVersionEquals() { var _a; return __awaiter(this, void 0, void 0, function* () { try { const resDb = yield this._cluster.query(`show databases where \`Database\` = '${this._database}';`, null, { maxRetry: 1, redis: false }); if (resDb.length < 1) return false; const res = yield this._cluster.query(`SELECT version FROM metadata;`, null, { maxRetry: 1, database: this._database, redis: false }); const serverVersion = (_a = res[0]) === null || _a === void 0 ? void 0 : _a.version; return serverVersion === this._databaseVersion; } catch (e) { Logger_1.default.error(e.message); return false; } }); } /** * Read content in files which in folder * @param dirname path to folder * @private */ _readFilesInDir(dirname) { const fullFileNames = (0, fs_1.readdirSync)(dirname); const fileNames = []; const fileContents = []; fullFileNames.forEach(filename => { fileContents.push((0, fs_1.readFileSync)(dirname + filename).toString()); fileNames.push((0, path_1.parse)(filename).name); }); return { fileNames, fileContents }; } /** * Create helper db * @private */ _insertNodes() { return __awaiter(this, void 0, void 0, function* () { this._cluster.pools.forEach(pool => { try { this._cluster.query('CALL SP_NodeInsert( ? , ? , ? , ? );', [pool.id, pool.name, pool.host, pool.port], { maxRetry: 1, database: this._database, redis: false }); } catch (e) { Logger_1.default.error(e.message); } }); }); } /** * Update hashing data from db */ _checkHashing() { return __awaiter(this, void 0, void 0, function* () { try { if (!this._timer.active) return; Logger_1.default.debug("checking async status in cluster"); const result = yield this._cluster.query(`SELECT FN_GetServiceNodeMapping() AS Result;`, null, { maxRetry: 1, database: this._database, redis: false }); const res = result[0].Result; res === null || res === void 0 ? void 0 : res.forEach(obj => { this._serviceNodeMap.set(obj.ServiceID, obj.NodeID); }); this._nextCheckHashing(); } catch (err) { Logger_1.default.error("Something wrong while checking hashing status in cluster.\n Message: " + err.message); this._nextCheckHashing(); } }); } /** * Activate next hashing check * @private */ _nextCheckHashing() { this._timer.start(this._nextCheckTime); } } exports.ClusterHashing = ClusterHashing; //# sourceMappingURL=ClusterHashing.js.map