在哪里破坏 knex 连接

mCY*_*mCY 10 node.js knex.js

我正在使用knexpg

我有一个类似于以下的项目。

数据库客户端.js

const dbClient = require('knex')({
  client: 'pg',
  connection: {
    host: '127.0.0.1',
    user: 'user',
    password: 'password',
    database: 'staging',
    port: '5431'
  }
})

module.exports = dbClient
Run Code Online (Sandbox Code Playgroud)

库.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
}
Run Code Online (Sandbox Code Playgroud)

测试01.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)
  }
})()
Run Code Online (Sandbox Code Playgroud)

knex.destroy()从去除libs.js如上所示。node test01可以输出res1res2。但问题是连接无限期挂起,CMD 永远不会返回。

但是,如果我取消knex.destroy()libs.js,那么doThis将执行,CMD将挂起doThat,因为没有联系了已在被关闭doThis

我的问题是:

什么是最好的位置knex.destroy()?或者有其他方法可以做到吗?

谢谢你的时间!

S.D*_*.D. 11

Knex destroy()似乎是一次性操作。破坏连接后,下一个操作可能需要一个全新的连接池。

您导出的 db 客户端模块会缓存到节点模块缓存中,并且不会在每次需要时创建新的连接池。

这是预期用途,应该在应用程序退出或所有测试完成时销毁池。如果您有理由为每个操作创建/销毁连接(例如在无服务器环境中),您不应该重用被销毁的客户端,而是每次都创建一个新实例。

否则,它违背了连接池的目的。


关于 lambda/无服务器环境的更新

从技术上讲,函数及其资源将在 lambda 函数运行后释放,这包括它可能已打开的任何连接。这对于真正的无状态函数是必要的。因此,建议在功能完成后关闭连接。但是,打开/关闭大量连接的许多功能最终可能会使数据库服务器耗尽连接(例如,请参阅此讨论)。一种解决方案可能是使用像PgBouncerPgPool这样的中间池来协商数据库服务器和 Lambda 函数之间的连接。

另一种方式是让平台提供商 (AWS) 为 lambda 环境添加特殊的池化功能,让它们共享长期存在的资源。

  • 但在我上面的代码中,如果我不在某处调用 `knex.destroy()` ,那么 `node test01.js` 将挂在那里并且永远不会返回。我使用 knex 的方式是使用 Mocha。所以我在想是否需要一个拆卸脚本来关闭连接。如果没有,它会再次挂起吗? (2认同)
  • 我必须调用 .destroy() 来结束进程,而不必调用 process.exit(0); (2认同)

ggo*_*len 10

每次查询后就破坏连接就像每次弹奏音符时都把吉他收起来一样。只需在表演开始时将其拉出,播放所有歌曲并在结束时将其收起来即可。

同样,当您完成应用程序其余部分的连接时,请销毁连接,而不是像这样在每次查询后销毁连接。在网络服务器中,这可能永远不会发生,因为您将在某个不确定的点用信号杀死它,并且在此之前应用程序可能需要活动连接。

对于测试,您可能希望使用该destroy函数来避免挂起。同样,在像您所展示的(人为的?)应用程序中,如果您遇到挂起并且应用程序卡住,请在完成后销毁一次连接。

这是 Mocha 的一个说明性示例,它在评论中被提及,并且似乎是一个非常合理的假设,即最终出现在该线程中的人们正在使用它(或类似的东西)。在所有测试之前进行设置、在所有测试之后进行拆除以及针对每个测试用例进行设置和拆除的模式是通用的。

与您的问题相关的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};
Run Code Online (Sandbox Code Playgroud)

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));
  });
});
Run Code Online (Sandbox Code Playgroud)

套餐:

"knex": "0.21.6",
"express": "4.17.1",
"mocha": "8.0.1",
"pg": "8.3.0",
Run Code Online (Sandbox Code Playgroud)

节点 12.19.0。


AKX*_*AKX 5

您可能通常不需要显式调用knex.destroy()——文档本身暗示了这一点(强调我的):

如果您需要明确拆除连接池,您可以使用knex.destroy([callback]).

  • 如果我不调用 `knex.destroy()`,我的脚本就会挂起。那么`我们不需要显式调用 knex.destroy()` 是什么意思? (4认同)
  • 啊,你只是在别处提到这是给摩卡的。对于常规服务器使用,您不需要拆除连接池 - 对于 Mocha,您可能需要查看全局拆卸挂钩,请参阅 https://futurestud.io/tutorials/mocha-global-setup-and-teardown -前后 (2认同)