在我公司中,我们的应用程序在NodeJS上的多个EC2实例和一个RDS数据库上运行。
我们的应用程序需要进行一些升级,因为某些依赖项已经很老了,而引起我们注意的升级之一是更新数据库库:mysql(从2.16.0升级到2.17.0),knex(从0.12开始)。 2至0.19.1)和书架(0.10.2至0.15.1)。
检查变更日志后,无需更改代码,因此我们迅速设法将其上传到登台服务器。
突然,我们的应用程序运行缓慢。加载所有数据需要花费几秒钟,而主要用户的仪表板(在同一服务器上加载几毫秒)需要大约30秒。几分钟后,整个应用程序完全没有响应。
为了检查问题是否仅与依赖关系升级有关,我们设法将其降级为工作版本,然后应用程序恢复到正常速度。再次升级,再次变慢。
我们已经开始通过New Relic分析RDS方面是否存在问题。没事没有峰值,没有CPU高使用率,没有慢查询或其他任何东西。然后我们来检查连接池,发现对我们有用的knex版本使用“ generic-pool”,而新版本使用“ tarn”。
因此,我们开始调试该池,发现该池已填充指定的查询,完全冻结了一段时间,然后开始抛出“ TimeoutError:Knex:超时获取连接。该池可能已满”错误。
但是对于填充所有池并冻结的查询而言,最有趣的是根本不应该生成该查询(并且在使用不存在此问题的过时版本时也不应该生成该查询)。
在我们的应用程序中,我们仅在两种情况下针对联系人表执行SELECT请求:
很明显,首先是用户要列出其联系人:
let contacts = await Contacts.forge({ 'list_owner': udata.id }).fetchAll()
其次,当检查联系人匹配以判断某些信息是否对指定用户可见时,具体取决于信息所有者的隐私设置:
let checkContact = await Contacts.where({
list_owner: target_user,
contact: udata.id
}).fetch()
经过几次grepping之后,我可以保证在我们的代码库中没有其他地方可以从contacts表中进行SELECTS选择。在我们的调试中,我们没有发现未定义的值,并且我们的调查显示查询在前一个代码运行时运行。但是,如您在屏幕快照中所见,查询knex运行没有条件:
select `contacts`.* from `contacts`
我们相信这就是它填充池的原因(因为请求每个用户的联系人是一件很艰巨的工作),但与此同时,我们看不到为什么knex运行这样的查询,例如:
是什么原因导致这种问题?
答案 0 :(得分:0)
对于一些可能落在这里的人!
如果没有意义,您最近将nodejs升级到v14!可能是原因!
我遇到了问题,上次安静地拉头发! 在尝试跟踪与众不同之处后,我以某种方式使用了nvm!因为它以前工作过!我想到了它,并使用v13进行了测试!并且再次起作用!
所以要注意!可能就是这样,它可以为您节省大量时间和压力!
Nodejs v14进行了重大更改! pg
模块会受到影响! pg开始在connect() call
退出进程!
如果您使用的是postgres
!使用nodejs v14及更高版本!确保使用版本为pg
的驱动程序模块>=8.0.3
!更好地升级到最新版本
npm install pg@latest --save
如果您不使用postgres
!尝试更新您的数据库驱动程序!可能是一样的!也可以尝试使用nodejs V13
。要确认这是同样的问题!
如果喜欢我,您想知道详细信息以及发生了什么事!?
使用节点V14! API发生了一些重大变化!很多事情也改变了!包括Openssl版本!
对于postgres!还有pg
模块!问题comment thread对此PR进行了描述:
初始readyState(一个私有/未公开的API,
net.Socket的pg使用)似乎已从“关闭”更改为“打开” 在节点14中。
很难实现完美的向后兼容性,但我想我 补丁足够近了。
并按照此this diffing!
您可以看到this line
中的更改简而言之! onReady的api已更改为net.Socket
!
实施的解决方案是根本不使用onReady!
按照这个
现在,当连接被调用时,Connection总是在其流上调用connect。
在旧版本中,仅当套接字处于closed
状态时才调用连接!消除了readyState
的使用!
选中https://github.com/nodejs/node/pull/32272
你能理解!
取决于实现!这些核心更改可能会或不会影响很多事情!
因为我想知道变化发生的地方!给你
https://github.com/nodejs/node/blob/master/doc/changelogs/CHANGELOG_V14.md
也可以检查更改日志:
Sequelize pg adapter will call pg client to create a connection and the promise
还要提及重大变化!设为pg
,使流程退出connect() call
。这就是它退出的原因!还有日志记录!
更详细的说明!这是怎么回事!续用postgres方言实现!哪用pg!和pg客户!建立连接!该连接有一个connect
事件!当它连接时发出它!并且因为节点v14将流的行为更改为以open开头!流连接已跳过!由于readyState
检查(预期为关闭,但改为打开!)!流被视为已连接(其他块)!哪里不行! connect
事件将直接发出!当那件事发生时!客户端将调用连接对象的requestSsl()
或startup()
方法!两者都将呼叫this._stream.write
。因为流未连接!发生错误!此错误未捕获!然后承诺在驱动程序中!将保持悬而未决!然后事件循环为空!默认情况下,Nodejs的行为只是退出!
您可以查看代码行的逐步操作:
connect()
call and emit connect
! Thinking the stream is connected because of V14 change connect
event catched and callback run! requestSsl()
or startup()
will be run stream.write
(startup(),Promise)Node exits without error and doesn't await promise (Event callback)
what happens when a Promise never resolves?
{{3}}
答案 1 :(得分:0)
执行查询后,您必须销毁连接。
var knex = new Knex(config)
knex(table)
.where({ id: 1 })
.then((result) => {
callback(output)
})
.catch((err) => {
err.error = true
callback(err)
})
.finally(() => {
knex.destroy()
})
})