Joh*_*ann 6 django postgresql psycopg2
我运行一个 Django 站点,它有一个简单的 ModelForm 类型视图,它会产生游标错误。在过去的两天里,这个视图被发布了几百次,大约 8% 的时间产生了错误。我只有这个观点有这个问题,即使我有另一个非常相似的观点。令人沮丧的是,我还没有弄清楚它有什么特别之处。升级到 Django 2.1/2 后,我才开始看到这些错误,但我认为它们可能已经存在,但没有看到。
完整的堆栈跟踪:https : //gist.github.com/jplehmann/ad8849572e569991bc26da87c81bb8f4
从[error] (internal users edit) OR (psycopg2 errors cursor)用户名被编辑的查询中记录日志的一些示例,以显示时间:
Jun 04 12:42:12 ballprice app/web.1: [ERROR] Internal Server Error: /users/a/edit [log:228]
Jun 04 12:42:12 ballprice app/web.1: psycopg2.errors.InvalidCursorName: cursor "_django_curs_140401754175232_2" does not exist
Jun 04 12:42:12 ballprice app/web.1: psycopg2.errors.InvalidCursorName: cursor "_django_curs_140401754175232_2" does not exist
Jun 04 12:42:27 ballprice app/web.1: [ERROR] Internal Server Error: /users/a/edit [log:228]
Jun 04 12:42:27 ballprice app/web.1: psycopg2.errors.InvalidCursorName: cursor "_django_curs_140401754175232_3" does not exist
Jun 04 12:57:51 ballprice app/web.3: [ERROR] Internal Server Error: /users/a/edit [log:228]
Jun 04 12:57:51 ballprice app/web.3: psycopg2.errors.DuplicateCursor: cursor "_django_curs_140092205262592_2" already exists
Jun 04 12:57:51 ballprice app/web.3: psycopg2.errors.InvalidCursorName: cursor "_django_curs_140092205262592_2" does not exist
Jun 04 13:10:50 ballprice app/web.3: [ERROR] Internal Server Error: /users/b/edit [log:228]
Jun 04 13:10:50 ballprice app/web.3: psycopg2.errors.DuplicateCursor: cursor "_django_curs_140092205262592_2" already exists
Jun 04 15:19:36 ballprice app/web.9: [ERROR] Internal Server Error: /users/c/edit [log:228]
Jun 04 15:19:36 ballprice app/web.9: psycopg2.errors.InvalidCursorName: cursor "_django_curs_140515167295232_1" does not exist
Jun 04 17:28:22 ballprice app/web.5: [ERROR] Internal Server Error: /users/d/edit [log:228]
Jun 04 17:28:22 ballprice app/web.5: psycopg2.errors.InvalidCursorName: cursor "_django_curs_140085445728000_2" does not exist
Jun 04 17:28:22 ballprice app/web.5: psycopg2.errors.InvalidCursorName: cursor "_django_curs_140085445728000_2" does not exist
Jun 04 22:49:15 ballprice app/web.1: [ERROR] Internal Server Error: /users/e/edit [log:228]
Jun 04 22:49:15 ballprice app/web.1: psycopg2.errors.InvalidCursorName: cursor "_django_curs_139902341289728_2" does not exist
Jun 04 22:49:15 ballprice app/web.1: psycopg2.errors.InvalidCursorName: cursor "_django_curs_139902341289728_2" does not exist
Jun 04 23:43:26 ballprice app/web.1: [ERROR] Internal Server Error: /users/f/edit [log:228]
Jun 04 23:43:26 ballprice app/web.1: psycopg2.errors.DuplicateCursor: cursor "_django_curs_139902341289728_2" already exists
Jun 05 02:49:22 ballprice app/web.1: [ERROR] Internal Server Error: /users/g/edit [log:228]
Jun 05 02:49:22 ballprice app/web.1: psycopg2.errors.InvalidCursorName: cursor "_django_curs_140092373694208_1" does not exist
Jun 05 02:49:22 ballprice app/web.1: psycopg2.errors.InvalidCursorName: cursor "_django_curs_140092373694208_1" does not exist
Jun 05 02:49:41 ballprice app/web.1: [ERROR] Internal Server Error: /users/g/edit [log:228]
Jun 05 02:49:41 ballprice app/web.1: psycopg2.errors.DuplicateCursor: cursor "_django_curs_140092373694208_1" already exists
Run Code Online (Sandbox Code Playgroud)
但是,我无法重现此错误。我与之交谈的一位用户说他们尝试过,并且在第三次保存了。
您可以看到命名游标被重复使用了很多,相隔几分钟,我只能假设这是正常的。
版本:
什么可能导致这种情况?
我们确实使用了 PG 保镖,并且建议一起禁用服务器端游标是可靠的,并且似乎已经奏效。
ACi*_*der 10
您使用的是 pgBouncer 还是其他一些池化机制?当使用某种形式的连接池来减轻数据库上的连接负载时,我通常会遇到这种问题(如果您碰巧有很多客户端,这是非常好的和可取的)。
https://docs.djangoproject.com/en/3.0/ref/databases/#transaction-pooling-and-server-side-cursors
在事务池模式下使用连接池(例如 PgBouncer)需要禁用该连接的服务器端游标。
服务器端游标对于连接是本地的,并且在 AUTOCOMMIT 为 True 时在事务结束时保持打开状态。后续事务可能会尝试从服务器端游标获取更多结果。在事务池模式下,不能保证后续事务将使用相同的连接。如果使用不同的连接,则在事务引用服务器端游标时会引发错误,因为服务器端游标只能在创建它们的连接中访问。
一种解决方案是
DATABASES通过设置DISABLE_SERVER_SIDE_CURSORS为 True来禁用连接的服务器端游标。为了在事务池模式下受益于服务器端游标,您可以设置另一个到数据库的连接,以便执行使用服务器端游标的查询。此连接需要直接连接到数据库或连接到会话池模式下的连接池。
另一种选择是使用服务器端游标将每个 QuerySet 包装在 atomic() 块中,因为它在事务期间禁用自动提交。这样,服务器端游标只会在事务期间存活。
因此,如果这适用于您的连接,您的选择是:
禁用游标
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'DISABLE_SERVER_SIDE_CURSORS': True,
}
}
Run Code Online (Sandbox Code Playgroud)
包装成交易
(不保证工作,取决于你的池设置)
with transaction.atomic():
qs = YourModel.objects.filter()
for values in qs.values('id', 'x').iterator():
pass
Run Code Online (Sandbox Code Playgroud)
额外的连接
如果您需要服务器端游标,您还可以使用额外的直接连接到数据库,然后对这些查询使用直接连接。
with transaction.atomic():
qs = YourModel.objects.filter()
for values in qs.values('id', 'x').iterator():
pass
Run Code Online (Sandbox Code Playgroud)