在哪里破坏knex连接
where to destroy knex connection
我有一个类似下面的项目。
dbClient.js
const dbClient = require('knex')({
client: 'pg',
connection: {
host: '127.0.0.1',
user: 'user',
password: 'password',
database: 'staging',
port: '5431'
}
})
module.exports = dbClient
libs.js
const knex = require('./dbClient.js')
async function doThis(email) {
const last = await knex('users').where({email}).first('last_name').then(res => res.last_name)
// knex.destroy()
return last
}
async function doThat(email) {
const first = await knex('users').where({email}).first('first_name').then(res => res.first_name)
// knex.destroy()
return first
}
module.exports = {
doThat,
doThis
}
test01.js
const {doThis, doThat} = require('./libs.js');
(async () => {
try {
const res1 = await doThis('user53@gmail.com')
console.log(res1)
const res2 = await doThat('user53@gmail.com')
console.log(res2)
} catch (err) {
console.log(err)
}
})()
当 knex.destroy()
从 libs.js
中删除时,如上所示。 node test01
可以输出 res1
和 res2
。但问题是连接无限期挂起,CMD 永远不会 return.
但是如果我从 libs.js
中取消注释 knex.destroy()
,那么 doThis
将执行,CMD 将在 doThat
处挂起,因为在 doThis
.
我的问题是:
knex.destroy()
的最佳位置是哪里?或者还有其他方法吗?
感谢您的宝贵时间!
您可能通常不需要显式调用 knex.destroy()
– 文档本身暗示了这一点(强调我的):
If you ever need to explicitly teardown the connection pool, you may use knex.destroy([callback])
.
Knex destroy() 似乎是一次性手术。销毁连接后,可能需要一个全新的连接池才能进行下一次操作。
您导出的数据库客户端模块 is cached 到节点模块缓存中,并且不会在您每次需要时创建新的连接池。
这是预期用途,池应该在应用程序退出或所有测试完成时销毁。如果你有理由为每个操作 create/destroy 连接(比如在无服务器环境中),你不应该重用被破坏的客户端,而是每次都创建一个新实例。
否则,就达不到连接池的目的了。
关于 lambda/server-less 环境的更新:
从技术上讲,函数及其资源将在 lambda 函数 运行 之后释放,这包括它可能已打开的任何连接。这对于真正的无状态函数是必需的。因此,建议在功能完成后关闭连接。但是,很多函数 opening/closing 很多连接最终可能会使数据库服务器 运行 失去连接(请参阅协商数据库服务器和 Lambda 函数之间的连接的 this discussion for example). One solution might be to use an intermediate pool like PgBouncer or PgPool。
另一种方式是平台提供商 (AWS) 为 lambda 环境添加特殊的池化功能,让它们共享长期存在的资源。
在每次查询后销毁连接就像在每次弹奏音符时收拾吉他。演出一开始就拉出来,把所有的歌都放完,收起来。
同样,在为应用程序的其余部分完成连接后销毁连接,而不是在每次这样的查询之后。在 Web 服务器中,这可能永远不会发生,因为您将在某个不确定的点用信号终止它,并且在此之前应用程序可能需要活动连接。
对于测试,您可能希望使用 destroy
函数来避免挂起。同样,在您展示的(人为的?)应用程序中,如果遇到挂起并且应用程序卡住,请在完成连接后销毁一次连接。
这是 Mocha 的一个说明性示例,它在评论中被提及并且似乎是一个非常合理的假设,即它(或类似的东西)正在被结束在这个线程中的人们使用。在所有测试之前设置、在所有测试之后拆除以及进行 per-test 案例设置和拆除的模式是通用的。
与您的问题相关,after(() => knex.destroy());
是所有测试结束时的拆卸调用。没有这个,摩卡挂起。请注意,我们还关闭了每个测试的 http 服务器,因此有多个候选挂起测试套件需要注意。
server.js
:
const express = require("express");
const createServer = (knex, port=3000) => {
const app = express();
app.get("/users/:username", (request, response) => {
knex
.where("username", request.params.username)
.select()
.first()
.table("users")
.then(user => user ? response.json({data: user})
: response.sendStatus(404))
.catch(err => response.sendStatus(500))
});
const server = app.listen(port, () =>
console.log(`[server] listening on port ${port}`)
);
return {
app,
close: cb => server.close(() => {
console.log("[server] closed");
cb && cb();
})
};
};
module.exports = {createServer};
server.test.js
:
const chai = require("chai");
const chaiHttp = require("chai-http");
const {createServer} = require("./server");
const {expect} = chai;
chai.use(chaiHttp);
chai.config.truncateThreshold = 0;
describe("server", function () {
this.timeout(3000);
let knex;
let server;
let app;
before(() => {
knex = require("knex")({
client: "pg",
connection: "postgresql://postgres@localhost",
});
});
beforeEach(done => {
server = createServer(knex);
app = server.app;
knex
.schema
.dropTableIfExists("users")
.then(() =>
knex.schema.createTable("users", table => {
table.increments();
table.string("username");
})
)
.then(() => knex("users").insert({
username: "foo"
}))
.then(() => done())
.catch(err => done(err));
});
afterEach(done => server.close(done));
after(() => knex.destroy());
it("should get user 'foo'", done => {
chai
.request(app)
.get("/users/foo")
.then(response => {
expect(response.status).to.equal(200);
expect(response).to.be.json;
expect(response.body).to.be.instanceOf(Object);
expect(response.body.data).to.be.instanceOf(Object);
expect(response.body.data.username).to.eq("foo");
done();
})
.catch(err => done(err));
});
});
套餐:
"knex": "0.21.6",
"express": "4.17.1",
"mocha": "8.0.1",
"pg": "8.3.0",
"node": "12.19.0"
我有一个类似下面的项目。
dbClient.js
const dbClient = require('knex')({
client: 'pg',
connection: {
host: '127.0.0.1',
user: 'user',
password: 'password',
database: 'staging',
port: '5431'
}
})
module.exports = dbClient
libs.js
const knex = require('./dbClient.js')
async function doThis(email) {
const last = await knex('users').where({email}).first('last_name').then(res => res.last_name)
// knex.destroy()
return last
}
async function doThat(email) {
const first = await knex('users').where({email}).first('first_name').then(res => res.first_name)
// knex.destroy()
return first
}
module.exports = {
doThat,
doThis
}
test01.js
const {doThis, doThat} = require('./libs.js');
(async () => {
try {
const res1 = await doThis('user53@gmail.com')
console.log(res1)
const res2 = await doThat('user53@gmail.com')
console.log(res2)
} catch (err) {
console.log(err)
}
})()
当 knex.destroy()
从 libs.js
中删除时,如上所示。 node test01
可以输出 res1
和 res2
。但问题是连接无限期挂起,CMD 永远不会 return.
但是如果我从 libs.js
中取消注释 knex.destroy()
,那么 doThis
将执行,CMD 将在 doThat
处挂起,因为在 doThis
.
我的问题是:
knex.destroy()
的最佳位置是哪里?或者还有其他方法吗?
感谢您的宝贵时间!
您可能通常不需要显式调用 knex.destroy()
– 文档本身暗示了这一点(强调我的):
If you ever need to explicitly teardown the connection pool, you may use
knex.destroy([callback])
.
Knex destroy() 似乎是一次性手术。销毁连接后,可能需要一个全新的连接池才能进行下一次操作。
您导出的数据库客户端模块 is cached 到节点模块缓存中,并且不会在您每次需要时创建新的连接池。
这是预期用途,池应该在应用程序退出或所有测试完成时销毁。如果你有理由为每个操作 create/destroy 连接(比如在无服务器环境中),你不应该重用被破坏的客户端,而是每次都创建一个新实例。
否则,就达不到连接池的目的了。
关于 lambda/server-less 环境的更新:
从技术上讲,函数及其资源将在 lambda 函数 运行 之后释放,这包括它可能已打开的任何连接。这对于真正的无状态函数是必需的。因此,建议在功能完成后关闭连接。但是,很多函数 opening/closing 很多连接最终可能会使数据库服务器 运行 失去连接(请参阅协商数据库服务器和 Lambda 函数之间的连接的 this discussion for example). One solution might be to use an intermediate pool like PgBouncer or PgPool。
另一种方式是平台提供商 (AWS) 为 lambda 环境添加特殊的池化功能,让它们共享长期存在的资源。
在每次查询后销毁连接就像在每次弹奏音符时收拾吉他。演出一开始就拉出来,把所有的歌都放完,收起来。
同样,在为应用程序的其余部分完成连接后销毁连接,而不是在每次这样的查询之后。在 Web 服务器中,这可能永远不会发生,因为您将在某个不确定的点用信号终止它,并且在此之前应用程序可能需要活动连接。
对于测试,您可能希望使用 destroy
函数来避免挂起。同样,在您展示的(人为的?)应用程序中,如果遇到挂起并且应用程序卡住,请在完成连接后销毁一次连接。
这是 Mocha 的一个说明性示例,它在评论中被提及并且似乎是一个非常合理的假设,即它(或类似的东西)正在被结束在这个线程中的人们使用。在所有测试之前设置、在所有测试之后拆除以及进行 per-test 案例设置和拆除的模式是通用的。
与您的问题相关,after(() => knex.destroy());
是所有测试结束时的拆卸调用。没有这个,摩卡挂起。请注意,我们还关闭了每个测试的 http 服务器,因此有多个候选挂起测试套件需要注意。
server.js
:
const express = require("express");
const createServer = (knex, port=3000) => {
const app = express();
app.get("/users/:username", (request, response) => {
knex
.where("username", request.params.username)
.select()
.first()
.table("users")
.then(user => user ? response.json({data: user})
: response.sendStatus(404))
.catch(err => response.sendStatus(500))
});
const server = app.listen(port, () =>
console.log(`[server] listening on port ${port}`)
);
return {
app,
close: cb => server.close(() => {
console.log("[server] closed");
cb && cb();
})
};
};
module.exports = {createServer};
server.test.js
:
const chai = require("chai");
const chaiHttp = require("chai-http");
const {createServer} = require("./server");
const {expect} = chai;
chai.use(chaiHttp);
chai.config.truncateThreshold = 0;
describe("server", function () {
this.timeout(3000);
let knex;
let server;
let app;
before(() => {
knex = require("knex")({
client: "pg",
connection: "postgresql://postgres@localhost",
});
});
beforeEach(done => {
server = createServer(knex);
app = server.app;
knex
.schema
.dropTableIfExists("users")
.then(() =>
knex.schema.createTable("users", table => {
table.increments();
table.string("username");
})
)
.then(() => knex("users").insert({
username: "foo"
}))
.then(() => done())
.catch(err => done(err));
});
afterEach(done => server.close(done));
after(() => knex.destroy());
it("should get user 'foo'", done => {
chai
.request(app)
.get("/users/foo")
.then(response => {
expect(response.status).to.equal(200);
expect(response).to.be.json;
expect(response.body).to.be.instanceOf(Object);
expect(response.body.data).to.be.instanceOf(Object);
expect(response.body.data.username).to.eq("foo");
done();
})
.catch(err => done(err));
});
});
套餐:
"knex": "0.21.6",
"express": "4.17.1",
"mocha": "8.0.1",
"pg": "8.3.0",
"node": "12.19.0"