knex/test/tape/pool.js

177 lines
4.9 KiB
JavaScript

'use strict';
const test = require('tape');
const Client = require('../../lib/dialects/sqlite3');
const tarn = require('tarn');
const Pool = tarn.Pool;
const knexfile = require('../knexfile');
const makeKnex = require('../../knex');
const delay = require('../../lib/execution/internal/delay');
test(`pool evicts dead resources when factory.validate rejects`, async (t) => {
t.plan(10);
let i = 0;
const pool = new Pool({
min: 2,
max: 5,
idleTimeoutMillis: 100,
acquireTimeoutMillis: 100,
create: () => {
return { id: i++ };
},
validate: (res) => {
return res.error ? false : true;
},
destroy: (res) => {
return true;
},
});
const fiveElements = Array.from(Array(5));
const allocateConnections = () =>
Promise.all(
fiveElements.map(() =>
pool.acquire().promise.catch((e) => {
t.fail('Could not get resource from pool', e);
})
)
);
const connectionsGroup1 = await allocateConnections();
connectionsGroup1.forEach((con) => pool.release(con));
connectionsGroup1.forEach((con) => {
// fake kill connections
con.error = 'connection lost';
});
const connectionsGroup2 = await allocateConnections();
connectionsGroup2.forEach((con) =>
t.ok(con.id > 4, 'old dead connections were not reused')
);
connectionsGroup2.forEach((con) => pool.release(con));
const connectionsGroup3 = await allocateConnections();
connectionsGroup3.forEach((con) =>
t.ok(con.id > 4 && con.id < 11, 'Released working connection was used')
);
connectionsGroup3.forEach((con) => pool.release(con));
await pool.destroy();
t.end();
});
test('#822, pool config, max: 0 should skip pool construction', function (t) {
const client = new Client({
connection: { filename: ':memory:' },
pool: { max: 0 },
});
try {
t.equal(client.pool, undefined);
t.end();
} finally {
client.destroy();
}
});
test('#823, should not skip pool construction pool config is not defined', function (t) {
const client = new Client({ connection: { filename: ':memory:' } });
try {
t.ok(client.pool instanceof Pool);
t.end();
} finally {
client.destroy();
}
});
test('#2321 dead connections are not evicted from pool (mysql)', async (t) => {
if (knexfile['mysql']) {
const knex = makeKnex(knexfile['mysql']);
t.plan(10);
try {
await Promise.all(
Array.from(Array(30)).map(() => {
// kill all connections in pool
return knex.raw(`KILL connection_id()`).catch(() => {
// just ignore errors
});
})
);
// TODO: It looks like this test case can trigger the race condition
// outlined in this issue comment:
//
// https://github.com/knex/knex/issues/3636#issuecomment-592005391
//
// For now, we're working around this problem by introducing a
// 1 second delay. But, we should revisit this once the connection
// pool logic has been refactored.
await delay(1000); // wait driver to notice connection errors (2ms was enough locally)
// all connections are dead, so they should be evicted from pool and this should work
await Promise.all(
Array.from(Array(10)).map(() =>
knex.select(1).then(() => t.pass('Read data'))
)
);
} catch (e) {
t.fail(
`Should have created new connection and execute the query, got : ${e}`
);
} finally {
t.end();
await knex.destroy();
}
} else {
t.end();
}
});
test('#2321 dead connections are not evicted from pool (mysql2)', async (t) => {
if (knexfile['mysql2']) {
const knex = makeKnex(knexfile['mysql2']);
t.plan(10);
try {
await Promise.all(
Array.from(Array(30)).map(() => {
// kill all connections in pool
return knex.raw(`KILL connection_id()`).catch(() => {
// just ignore errors
});
})
);
// TODO: It looks like this test case can trigger the race condition
// outlined in this issue comment:
//
// https://github.com/knex/knex/issues/3636#issuecomment-592005391
//
// For now, we're working around this problem by introducing a
// 1 second delay. But, we should revisit this once the connection
// pool logic has been refactored.
await delay(1000); // wait driver to notice connection errors (2ms was enough locally)
// all connections are dead, so they should be evicted from pool and this should work
await Promise.all(
Array.from(Array(10)).map(() =>
knex.select(1).then(() => t.pass('Read data'))
)
);
} catch (e) {
t.fail(
`Should have created new connection and execute the query, got : ${e}`
);
} finally {
t.end();
await knex.destroy();
}
} else {
t.end();
}
});