Change endpoint from persons to people

This commit is contained in:
xfarrow
2025-03-23 21:00:08 +01:00
parent 4ae263662c
commit d005193f63
7158 changed files with 700476 additions and 735 deletions

View File

@ -0,0 +1,18 @@
# Test scripts to evaluate stability of drivers / pool etc.
# To run this test you need to be in this directory + have node >= 8
# and startup docker containers with proxy and sql servers
docker-compose up --no-start
docker-compose start
# Select different test script to run:
node mysql2-random-hanging-every-now-and-then.js 2> /dev/null | grep -B500 -A2 -- "- STATS"
node mysql2-sudden-exit-without-error
node knex-stress-test.js | grep -A 3 -- "- STATS "
node reconnect-test-mysql-based-drivers.js 2> /dev/null | grep -A 3 -- "- STATS "
# Shut down docker instances when done:
docker-compose down

View File

@ -0,0 +1,57 @@
version: '3'
services:
toxiproxy:
image: shopify/toxiproxy
ports:
- '8474:8474'
- '23306:23306'
- '25432:25432'
- '25433:25433'
- '21521:21521'
- '21433:21433'
links:
- 'mysql'
- 'postgresql'
- 'pgnative'
- 'oracledb'
- 'mssql'
mysql:
image: mysql:5.7
ports:
- '33306:3306'
environment:
- TZ=UTC
- MYSQL_ROOT_PASSWORD=mysqlrootpassword
postgresql:
image: mdillon/postgis
ports:
- '35432:5432'
environment:
- POSTGRES_PASSWORD=postgresrootpassword
- POSTGRES_USER=postgres
pgnative:
image: mdillon/postgis
ports:
- '35433:5432'
environment:
- POSTGRES_PASSWORD=postgresrootpassword
- POSTGRES_USER=postgres
oracledb:
image: quillbuilduser/oracle-18-xe
ports:
- '31521:1521'
environment:
- ORACLE_ALLOW_REMOTE=true
mssql:
image: microsoft/mssql-server-linux:2017-latest
ports:
- '31433:1433'
environment:
- ACCEPT_EULA=Y
- SA_PASSWORD=S0meVeryHardPassword

View File

@ -0,0 +1,212 @@
const Knex = require('../../lib');
const toxiproxy = require('toxiproxy-node-client');
const toxicli = new toxiproxy.Toxiproxy('http://localhost:8474');
const rp = require('request-promise-native');
const delay = require('../../lib/execution/internal/delay');
// init instances
const pg = Knex({
client: 'pg',
connection:
'postgres://postgres:postgresrootpassword@localhost:25432/postgres',
pool: { max: 50 },
});
const pgnative = Knex({
client: 'pgnative',
connection:
'postgres://postgres:postgresrootpassword@localhost:25433/postgres',
pool: { max: 50 },
});
const mysql2 = Knex({
client: 'mysql2',
connection:
'mysql://root:mysqlrootpassword@localhost:23306/?charset=utf8&connectTimeout=500',
pool: { max: 50 },
});
const mysql = Knex({
client: 'mysql',
connection:
'mysql://root:mysqlrootpassword@localhost:23306/?charset=utf8&connectTimeout=500',
pool: { max: 50 },
});
const mssql = Knex({
client: 'mssql',
connection: {
port: 21433,
user: 'sa',
password: 'S0meVeryHardPassword',
server: 'localhost',
requestTimeout: 500,
},
pool: { max: 50 },
});
/* TODO: figure out how to nicely install oracledb node driver on osx
const oracledb = Knex({
client: 'oracledb',
connection: {
user : "travis",
password : "travis",
connectString : "localhost/XE",
// https://github.com/oracle/node-oracledb/issues/525
stmtCacheSize : 0
},
pool: { max: 50 }
});
*/
const counters = {};
function setQueryCounters(instance, name) {
const counts = (counters[name] = { queries: 0, results: 0, errors: 0 });
instance.on('query', () => (counts.queries += 1));
instance.on('query-response', () => (counts.results += 1));
instance.on('query-error', () => (counts.errors += 1));
}
setQueryCounters(pg, 'pg');
setQueryCounters(mysql, 'mysql');
setQueryCounters(mysql2, 'mysql2');
setQueryCounters(mssql, 'mssql');
const _ = require('lodash');
// start printing out counters
let lastCounters = _.cloneDeep(counters);
setInterval(() => {
const reqsPerSec = {};
for (const key of Object.keys(counters)) {
reqsPerSec[key] = {
queries: (counters[key].queries - lastCounters[key].queries) / 2,
results: (counters[key].results - lastCounters[key].results) / 2,
errors: (counters[key].errors - lastCounters[key].errors) / 2,
};
}
console.log(
'------------------------ STATS PER SECOND ------------------------'
);
console.dir(reqsPerSec, { colors: true });
console.log(
'------------------------------- EOS ------------------------------'
);
lastCounters = _.cloneDeep(counters);
}, 2000);
async function killConnectionsPg(client) {
return client.raw(`SELECT pg_terminate_backend(pg_stat_activity.pid)
FROM pg_stat_activity
WHERE pg_stat_activity.datname = 'postgres'
AND pid <> pg_backend_pid()`);
}
async function killConnectionsMyslq(client) {
const [rows] = await client.raw(`SHOW FULL PROCESSLIST`);
await Promise.all(rows.map((row) => client.raw(`KILL ${row.Id}`)));
}
async function killConnectionsMssql() {
const rows = await mssql('sys.dm_exec_sessions').select('session_id');
await Promise.all(rows.map((row) => mssql.raw(`KILL ${row.session_id}`)));
}
async function main() {
async function loopQueries(prefix, query) {
const queries = () => [...Array(50).fill(query)];
// eslint-disable-next-line no-constant-condition
while (true) {
try {
await Promise.all(queries());
} catch (err) {
console.log(prefix, err);
}
}
}
async function recreateProxy(serviceName, listenPort, proxyToPort) {
try {
await rp.delete({
url: `${toxicli.host}/proxies/${serviceName}`,
});
} catch (err) {
/* empty */
}
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 500 bytes
await proxy.addToxic(
new toxiproxy.Toxic(proxy, {
type: 'limit_data',
attributes: { bytes: 5000 },
})
);
}
// create TCP proxies for simulating bad connections etc.
async function recreateProxies() {
await recreateProxy('postgresql', 25432, 5432);
await recreateProxy('postgresql', 25433, 5433);
await recreateProxy('mysql', 23306, 3306);
await recreateProxy('oracledb', 21521, 1521);
await recreateProxy('mssql', 21433, 1433);
}
await recreateProxies();
loopQueries('PSQL:', pg.raw('select 1'));
loopQueries('PSQL TO:', pg.raw('select 1').timeout(20));
loopQueries('PGNATIVE:', pgnative.raw('select 1'));
loopQueries('PGNATIVE TO:', pgnative.raw('select 1').timeout(20));
loopQueries('MYSQL:', mysql.raw('select 1'));
loopQueries('MYSQL TO:', mysql.raw('select 1').timeout(20));
// mysql2 still crashes app (without connection killer nor timeouts)
// https://github.com/sidorares/node-mysql2/issues/731
// loopQueries('MYSQL2:', mysql2.raw('select 1'));
// loopQueries('MYSQL2 TO:', mysql2.raw('select 1').timeout(20));
loopQueries('MSSQL:', mssql.raw('select 1'));
loopQueries('MSSQL TO:', mssql.raw('select 1').timeout(20));
setInterval(recreateProxies, 2000);
// eslint-disable-next-line no-constant-condition
while (true) {
await delay(20); // kill everything every quite often from server side
try {
await Promise.all([
killConnectionsPg(pg),
killConnectionsPg(pgnative),
killConnectionsMyslq(mysql),
// killConnectionsMyslq(mysql2),
killConnectionsMssql(),
]);
} catch (err) {
console.log('KILLER ERROR:', err);
}
}
}
process.on('exit', () => console.log('- STATS PRINT NEAR END LOGS ')); // marker for grep...
main();

View File

@ -0,0 +1,149 @@
/**
* Test case for figuring out robust way to recognize if connection is dead
* for mysql based drivers.
*/
const delay = require('../../lib/execution/internal/delay');
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) {
counters[name] = { queries: 0, results: 0, errors: 0 };
}
setMysqlQueryCounters('mysql2');
// start printing out counters
let lastCounters = _.cloneDeep(counters);
setInterval(() => {
const reqsPerSec = {};
for (const 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) {
/* empty */
}
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];
// eslint-disable-next-line no-constant-condition
while (true) {
try {
counts.queries += 1;
// without this delay we might endup to busy failure loop
await delay(0);
await query();
counts.results += 1;
} catch (err) {
counts.errors += 1;
console.log(prefix, err);
}
}
}
loopQueries('mysql2', () => mysql2Query('select 1'));
// wait forever
// eslint-disable-next-line no-constant-condition
while (true) {
await delay(1000);
}
}
main()
.then(() => console.log('DONE'))
.catch((err) => console.log(err));

View File

@ -0,0 +1,101 @@
/**
* Test case when mysql2 driver strangely exits when one tries to send query
* to dead connection.
*/
const delay = require('../../lib/execution/internal/delay');
const toxiproxy = require('toxiproxy-node-client');
const toxicli = new toxiproxy.Toxiproxy('http://localhost:8474');
const rp = require('request-promise-native');
// drops old toxicproxy and creates new
async function recreateProxy(serviceName, listenPort, proxyToPort) {
try {
await rp.delete({
url: `${toxicli.host}/proxies/${serviceName}`,
});
} catch (err) {
// there was no proxy by that name... its ok
}
await toxicli.createProxy({
name: serviceName,
listen: `0.0.0.0:${listenPort}`,
upstream: `${serviceName}:${proxyToPort}`,
});
}
async function insanelyParanoidQuery(con) {
console.log('sending query');
const res = await new Promise((resolve, reject) => {
try {
con.query('select 1', [], function (err, rows, fields) {
if (err) {
reject(err);
} else {
resolve(rows);
}
});
} catch (err) {
console.log('Huh synchronous exception?! (shouldnt be possible)');
reject(err);
}
});
console.log(res);
}
async function main() {
// create proxy from localhost:23306 -> mysqldocker:3306
await recreateProxy('mysql', 23306, 3306);
// ------------- setup mysql2 db driver connection
const mysql2 = require('mysql2'); // with mysql this works...
const mysql2Con = await mysql2.createConnection({
host: 'localhost',
user: 'root',
password: 'mysqlrootpassword',
port: 23306,
});
mysql2Con.on('error', function (err) {
console.log("I'm dead", err);
});
console.log('Going to cut connections');
// cut connection during recreate
await recreateProxy('mysql', 23306, 3306);
console.log('Proxy recreated... start waiting');
// wait forever
// eslint-disable-next-line no-constant-condition
while (true) {
await delay(1000);
try {
await insanelyParanoidQuery(mysql2Con);
} catch (err) {
console.log('query failed:', err);
}
await recreateProxy('mysql', 23306, 3306);
}
}
main()
.then(() => console.log('Process stopped normally'))
.catch((err) => {
console.log('Process stopped to failure', err);
});
console.log('Waiting for eventloop to stop...');
process.on('uncaughtException', function (err) {
console.log('uncaughtException', err);
});
process.on('exit', function () {
console.log(
'Did someone call process.exit() or wat? exitCode:',
process.exitCode
);
});

View File

@ -0,0 +1,188 @@
/**
* Test case for figuring out robust way to recognize if connection is dead
* for mysql based drivers.
*/
const delay = require('../../lib/execution/internal/delay');
const toxiproxy = require('toxiproxy-node-client');
const toxicli = new toxiproxy.Toxiproxy('http://localhost:8474');
const rp = require('request-promise-native');
async function stdMysqlQuery(con, sql) {
return new Promise((resolve, reject) => {
try {
con.query(
{
sql,
timeout: 4000,
},
function (error, results, fields) {
if (error) {
reject(error);
} else {
resolve(results);
}
}
);
} catch (err) {
reject(err); // double sure...
}
});
}
// ALL THE DRIVERS HAS DIFFERENT BAG OF TRICKS TO RECOVER AND
// RECOGNIZE WHEN CONNECTION HAS BEEN CLOSED
// ------------- setup mysql db driver connection
const mysql = require('mysql');
let mysqlCon = { state: 'disconnected' };
async function mysqlQuery(sql) {
// best way to check if connection is still alive
if (mysqlCon.state === 'disconnected') {
console.log('reconnecting mysql');
mysqlCon = mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'mysqlrootpassword',
port: 23306,
connectTimeout: 500,
});
// not always triggered, if this happens during query
mysqlCon.on('error', (err) => {
console.log('- STATS Mysql connection died:', err);
});
}
return stdMysqlQuery(mysqlCon, sql);
}
// ------------- setup mysql2 db driver connection
const mysql2 = require('mysql2');
let mysql2Con = { _fatalError: 'initmefirst' };
async function mysql2Query(sql) {
if (mysql2Con._fatalError) {
console.log('reconnecting mysql2');
mysql2Con = mysql2.createConnection({
host: 'localhost',
user: 'root',
password: 'mysqlrootpassword',
port: 23306,
connectTimeout: 500,
});
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) {
counters[name] = { queries: 0, results: 0, errors: 0 };
}
setMysqlQueryCounters('mysql');
setMysqlQueryCounters('mysql2');
const _ = require('lodash');
// start printing out counters
let lastCounters = _.cloneDeep(counters);
setInterval(() => {
const reqsPerSec = {};
for (const 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);
// if hang
///if (reqsPerSec.mysql2.queries === 0) process.exit(0);
}, 1000);
async function main() {
async function recreateProxy(serviceName, listenPort, proxyToPort) {
try {
await rp.delete({
url: `${toxicli.host}/proxies/${serviceName}`,
});
} catch (err) {
/* empty */
}
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 },
})
);
}
// create TCP proxies for simulating bad connections etc.
async function recreateProxies() {
console.log('----- Recreating proxies -> cutting connections completely');
await recreateProxy('postgresql', 25432, 5432);
await recreateProxy('mysql', 23306, 3306);
await recreateProxy('oracledb', 21521, 1521);
}
setInterval(() => recreateProxies(), 2000);
async function loopQueries(prefix, query) {
const counts = counters[prefix];
// eslint-disable-next-line no-constant-condition
while (true) {
try {
counts.queries += 1;
// without this delay we endup to busy failure loop
await delay(0);
await query();
counts.results += 1;
} catch (err) {
counts.errors += 1;
console.log(prefix, err);
}
}
}
await recreateProxies();
loopQueries('mysql', () => mysqlQuery('select 1'));
loopQueries('mysql2', () => mysql2Query('select 1'));
// wait forever
// eslint-disable-next-line no-constant-condition
while (true) {
await delay(1000);
}
}
main()
.then(() => console.log('DONE'))
.catch((err) => console.log(err));