背景
收到使用方反馈问题:在并发请求的场景,Gremlin Python SDK
有概率会报错KeyError
。
具体的堆栈如下:
|
|
排查
ResultsDict
更新
从代码来看,从message
中拿到的request_id
在results_dict
中是不可能找不到的。
|
|
所有从results_dict
中删除request_id
都是在data_received
中进行的。
如果找不到request_id
,看起来
极有可能是在data_received
中收到了相同的request_id
的两次消息。
真相渐显
在发送和接收Message
的位置添加日志之后,发现并没有重复收到相同的request_id
的消息:
|
|
虽然之前的推断没有成立,但是从日志中可以看到出错的RequestId
是b847a328-5c77-4419-8009-3d5b99b43a3d
,对应的Request
是在ForkPoolWorker-2
中发送的,但是在ForkPoolWorker-3
中接收到相应的Response
的,随后报错KeyError
的。
考虑到业务方使用了Celery
框架,上面的ForkPoolWorker-2
、ForkPoolWorker-3
是在框架中Fork
出来的子进程。所以,目前看来比较合理的假设是,Gremlin Python SDK
在多进程模型下使用的问题。
水落石出
在单进程情况下,Gremlin Python SDK
的处理流程如下:
在多进程的模型下,现有的Python SDK
的处理是存在问题的。由于client
实例是在主进程中创建的,其数据结构会被其它子进程共享,所以Socket
的句柄是共享的。但是ResultsDict
在子进程中会被写入,每个子进程维护了一份自己的拷贝。
可能子进程1
发送了request
,然后子进程2
收到相应的response
,这个时候会丢失一些上下文的信息。
解决方案
处理上述问题,可以将Socket
的创建延迟到write
时,这样就不会产生不同的子进程同享同一个Socket
的情况。已经提交相关PR
并合并到官方。