db-mysql.js 3.58 KB
const HEALTHY_CHECK_INTERVAL_MS = 10000;

const MODULE_NAME = require('path').basename(__filename);

const mysql = require('mysql');

const config = require('komodo-sdk/config');
const logger = require('tektrans-logger');

const connectionLimit = config.mysql && config.mysql.pool_connection_limit
    ? config.mysql.pool_connection_limit
    : 0;

const ERROR_POOL_NOT_READY = new Error(`${MODULE_NAME}: pool is not ready`);
exports.ERROR_POOL_NOT_READY = ERROR_POOL_NOT_READY;

const pool = config.mysql ? mysql.createPool({
    connectionLimit,
    host: config.mysql.host || 'localhost',
    database: config.mysql.database || 'komodo',
    user: config.mysql.user || 'komodo',
    password: config.mysql.password,
    timezone: config.mysql.timezone,
}) : null;

exports.pool = pool;

exports.query = (query, values, cb) => {
    // pool.query.apply(null, arguments);
    if (!pool || !pool.query) {
        logger.warn(`${MODULE_NAME}: ${ERROR_POOL_NOT_READY.toString()}`);
        if (typeof cb === 'function') {
            cb(ERROR_POOL_NOT_READY);
        }

        return;
    }

    pool.query(query, values, cb);
};

exports.format = (sql, values, cb) => new Promise((resolve, reject) => {
    if (!pool) {
        reject(ERROR_POOL_NOT_READY);
        if (typeof cb === 'function') cb(ERROR_POOL_NOT_READY);
        return;
    }

    pool.getConnection((err, connection) => {
        if (err) {
            reject(err);
            if (typeof cb === 'function') cb(err);
            return;
        }

        const formatted = connection.format(sql, values);
        connection.release();

        resolve(formatted);
        if (typeof cb === 'function') cb(null, formatted);
    });
});

exports.beginConnection = (cb) => new Promise((resolve) => {
    pool.getConnection((errGetConnection, connection) => {
        if (errGetConnection) {
            resolve([errGetConnection]);
            if (typeof cb === 'function') cb(errGetConnection);
            return;
        }

        connection.beginTransaction((errBeginTransaction) => {
            if (errBeginTransaction) {
                resolve([errBeginTransaction]);
                if (typeof cb === 'function') cb(errBeginTransaction);
                return;
            }

            resolve([null, connection]);
            if (typeof cb === 'function') cb(null, connection);
        });
    });
});

exports.getBy = (tableName, fieldName, value, cb) => new Promise((resolve) => {
    const query = 'SELECT * FROM ?? WHERE ?? = ? LIMIT 1';
    const values = [tableName, fieldName, value];
    pool.query(query, values, (err, results) => {
        const result = results && results[0];
        resolve([err, result || null]);
        if (typeof cb === 'function') cb(err, result || null);
    });
});

function healthyCheck() {
    const query = 'SELECT 1';
    const values = [];

    if (!pool) {
        logger.warn(`${MODULE_NAME}: Skip healthy check on undefined pool (ERR-EB9E5C08)`);
        return;
    }

    if (!pool.query) {
        logger.warn(`${MODULE_NAME}: Skip healthy check on undefined pool.query (ERR-D10F70F3)`);
        return;
    }

    pool.query(query, values, (err) => {
        if (err) {
            logger.warn(`${MODULE_NAME}: Error on healthy check (ERR-38EC9B78)`, { err });
        }
    });
}

setInterval(() => {
    const randomMs = Math.floor(Math.random() * HEALTHY_CHECK_INTERVAL_MS * 0.3);
    setTimeout(() => {
        try {
            healthyCheck();
        } catch (err) {
            logger.warn(`${MODULE_NAME}: Exception on periodic healthy check (ERR-2D137502)`, { err });
        }
    }, randomMs);
}, HEALTHY_CHECK_INTERVAL_MS);