knex-stress-test.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. const Knex = require('../../lib');
  2. const Bluebird = require('bluebird');
  3. const toxiproxy = require('toxiproxy-node-client');
  4. const toxicli = new toxiproxy.Toxiproxy('http://localhost:8474');
  5. const rp = require('request-promise-native');
  6. // init instances
  7. const pg = Knex({
  8. client: 'pg',
  9. connection: 'postgres://postgres:postgresrootpassword@localhost:25432/postgres',
  10. pool: { max: 50 }
  11. });
  12. const mysql2 = Knex({
  13. client: 'mysql2',
  14. connection: 'mysql://root:mysqlrootpassword@localhost:23306/?charset=utf8&connectTimeout=500',
  15. pool: { max: 50 }
  16. });
  17. const mysql = Knex({
  18. client: 'mysql',
  19. connection: 'mysql://root:mysqlrootpassword@localhost:23306/?charset=utf8&connectTimeout=500',
  20. pool: { max: 50 }
  21. });
  22. /* TODO: figure out how to nicely install oracledb node driver on osx
  23. const mysql = Knex({
  24. client: 'oracledb',
  25. connection: {
  26. user : "travis",
  27. password : "travis",
  28. connectString : "localhost/XE",
  29. // https://github.com/oracle/node-oracledb/issues/525
  30. stmtCacheSize : 0
  31. },
  32. pool: { max: 50 }
  33. });
  34. */
  35. const counters = {};
  36. function setQueryCounters(instance, name) {
  37. const counts = counters[name] = {queries: 0, results: 0, errors: 0};
  38. instance.on('query', () => counts.queries += 1);
  39. instance.on('query-response', () => counts.results += 1);
  40. instance.on('query-error', () => counts.errors += 1);
  41. }
  42. setQueryCounters(pg, 'pg');
  43. setQueryCounters(mysql, 'mysql');
  44. setQueryCounters(mysql2, 'mysql2');
  45. const _ = require('lodash');
  46. // start printing out counters
  47. let lastCounters = _.cloneDeep(counters);
  48. setInterval(() => {
  49. const reqsPerSec = {};
  50. for (let key of Object.keys(counters)) {
  51. reqsPerSec[key] = {
  52. queries: (counters[key].queries - lastCounters[key].queries) / 2,
  53. results: (counters[key].results - lastCounters[key].results) / 2,
  54. errors: (counters[key].errors - lastCounters[key].errors) / 2,
  55. }
  56. }
  57. console.log('------------------------ STATS PER SECOND ------------------------');
  58. console.dir(reqsPerSec, {colors: true});
  59. console.log('------------------------------- EOS ------------------------------');
  60. lastCounters = _.cloneDeep(counters);
  61. }, 2000);
  62. async function killConnectionsPg() {
  63. return pg.raw(`SELECT pg_terminate_backend(pg_stat_activity.pid)
  64. FROM pg_stat_activity
  65. WHERE pg_stat_activity.datname = 'postgres'
  66. AND pid <> pg_backend_pid()`);
  67. }
  68. async function killConnectionsMyslq(client) {
  69. const [rows, colDefs] = await client.raw(`SHOW FULL PROCESSLIST`);
  70. await Promise.all(rows.map(row => client.raw(`KILL ${row.Id}`)));
  71. }
  72. async function main() {
  73. async function loopQueries(prefix, query) {
  74. const queries = () => [
  75. ...Array(50).fill(query),
  76. ];
  77. while(true) {
  78. try {
  79. await Promise.all(queries());
  80. } catch (err) {
  81. console.log(prefix, err);
  82. }
  83. }
  84. }
  85. async function recreateProxy(serviceName, listenPort, proxyToPort) {
  86. try {
  87. await rp.delete({
  88. url: `${toxicli.host}/proxies/${serviceName}`
  89. });
  90. } catch(err) {}
  91. const proxy = await toxicli.createProxy({
  92. name: serviceName,
  93. listen: `0.0.0.0:${listenPort}`,
  94. upstream: `${serviceName}:${proxyToPort}`
  95. });
  96. // add some latency
  97. await proxy.addToxic(new toxiproxy.Toxic(proxy, {
  98. type: 'latency',
  99. attributes: {latency: 1, jitter: 1}
  100. }));
  101. // cause connections to be closed every 500 bytes
  102. await proxy.addToxic(new toxiproxy.Toxic(proxy, {
  103. type: 'limit_data',
  104. attributes: {bytes: 5000}
  105. }));
  106. }
  107. // create TCP proxies for simulating bad connections etc.
  108. async function recreateProxies() {
  109. await recreateProxy('postgresql', 25432, 5432);
  110. await recreateProxy('mysql', 23306, 3306);
  111. await recreateProxy('oracledbxe', 21521, 1521);
  112. }
  113. await recreateProxies();
  114. loopQueries('PSQL:', pg.raw('select 1'));
  115. loopQueries('PSQL TO:', pg.raw('select 1').timeout(20));
  116. loopQueries('MYSQL:', mysql.raw('select 1'));
  117. loopQueries('MYSQL TO:', mysql.raw('select 1').timeout(20));
  118. loopQueries('MYSQL2:', mysql2.raw('select 1'));
  119. loopQueries('MYSQL2 TO:', mysql2.raw('select 1').timeout(20));
  120. while(true) {
  121. await Bluebird.delay(20); // kill everything every quite often from server side
  122. try {
  123. await Promise.all([
  124. killConnectionsPg(),
  125. killConnectionsMyslq(mysql),
  126. killConnectionsMyslq(mysql2),
  127. ]);
  128. } catch (err) {
  129. console.log('KILLER ERROR:', err);
  130. }
  131. }
  132. }
  133. process.on('exit', () => console.log('- STATS PRINT NEAR END LOGS ')); // marker for grep...
  134. main();