/** * Test case for figuring out robust way to recognize if connection is dead * for mysql based drivers. */ const Bluebird = require('bluebird'); const toxiproxy = require('toxiproxy-node-client'); const toxicli = new toxiproxy.Toxiproxy('http://localhost:8474'); const rp = require('request-promise-native'); const _ = require('lodash'); async function stdMysqlQuery(con, sql) { return new Promise((resolve, reject) => { try { con.query( { sql, timeout: 500, }, function(error, results, fields) { if (error) { reject(error); } else { resolve(results); } } ); } catch (err) { reject(err); // double sure... } }); } const mysql2 = require('mysql2'); let mysql2Con = { _fatalError: 'initmefirst' }; async function mysql2Query(sql) { // recreate connection on fatal error if (mysql2Con._fatalError) { console.log('========== Reconnecting mysql2'); mysql2Con = mysql2.createConnection({ host: 'localhost', user: 'root', password: 'mysqlrootpassword', port: 23306, connectTimeout: 500, debug: true, }); mysql2Con.on('error', (err) => { console.log('- STATS Mysql2 connection died:', err); }); } console.log('================ MYSQL2 Running query ======'); const res = await stdMysqlQuery(mysql2Con, sql); console.log('====================== done ================'); return res; } const counters = {}; function setMysqlQueryCounters(name) { const counts = (counters[name] = { queries: 0, results: 0, errors: 0 }); } setMysqlQueryCounters('mysql2'); // start printing out counters let lastCounters = _.cloneDeep(counters); setInterval(() => { const reqsPerSec = {}; for (let key of Object.keys(counters)) { reqsPerSec[key] = { queries: counters[key].queries - lastCounters[key].queries, results: counters[key].results - lastCounters[key].results, errors: counters[key].errors - lastCounters[key].errors, }; } console.log( '------------------------ STATS PER SECOND ------------------------' ); console.dir(reqsPerSec, { colors: true }); console.log( '------------------------------- EOS ------------------------------' ); lastCounters = _.cloneDeep(counters); }, 1000); async function recreateProxy(serviceName, listenPort, proxyToPort) { try { await rp.delete({ url: `${toxicli.host}/proxies/${serviceName}`, }); } catch (err) {} const proxy = await toxicli.createProxy({ name: serviceName, listen: `0.0.0.0:${listenPort}`, upstream: `${serviceName}:${proxyToPort}`, }); // add some latency await proxy.addToxic( new toxiproxy.Toxic(proxy, { type: 'latency', attributes: { latency: 1, jitter: 1 }, }) ); // cause connections to be closed every some transferred bytes await proxy.addToxic( new toxiproxy.Toxic(proxy, { type: 'limit_data', attributes: { bytes: 1000 }, }) ); } async function main() { await recreateProxy('mysql', 23306, 3306); setInterval(() => recreateProxy('mysql', 23306, 3306), 2000); async function loopQueries(prefix, query) { const counts = counters[prefix]; while (true) { try { counts.queries += 1; // without this delay we might endup to busy failure loop await Bluebird.delay(0); await query(); counts.results += 1; } catch (err) { counts.errors += 1; console.log(prefix, err); } } } loopQueries('mysql2', () => mysql2Query('select 1')); // wait forever while (true) { await Bluebird.delay(1000); } } main() .then(() => console.log('DONE')) .catch((err) => console.log(err));