Django Qcluster后台任务更新PostgreSQL遇SSL SYSCALL错误求助
Django Q 长任务更新PostgreSQL时出现SSL连接错误
我用Django Q cluster运行时长超1小时的后台任务,打算根据任务结果更新PostgreSQL表,但获取Q cluster响应并更新表时,出现psycopg2.OperationalError: SSL SYSCALL error: EOF detected错误。
Qcluster配置
{ "name": "sampleapp", "workers": 8, "recycle": 500, "compress": true, "save_limit": 250, "queue_limit": 500, "cpu_affinity": 1, "label": "Django Q", "max_attempts": 1, "attempt_count": 1, "catch_up": false, "redis": { "host": "127.0.0.1", "port": 6379, "db": 0 } }
使用版本
- Django==3.1.7
- django-q==1.3.9
完整堆栈跟踪
SSL SYSCALL error: EOF detected 15:36:35 [Q] ERROR Failed [kentucky-montana-west-alaska] - connection already closed : Traceback (most recent call last): File "/mnt/c/lyftrondatasync/LyftrondatasyncAPI/venv/lib/python3.9/site-packages/django/db/backends/utils.py", line 84, in _execute return self.cursor.execute(sql, params) psycopg2.OperationalError: SSL SYSCALL error: EOF detected The above exception was the direct cause of the following exception: Traceback (most recent call last): File "/mnt/c/lyftrondatasync/LyftrondatasyncAPI/connector/sync_utils.py", line 1361, in run_data_sync if not table_pipeline_json.count(): File "/mnt/c/lyftrondatasync/LyftrondatasyncAPI/venv/lib/python3.9/site-packages/django/db/models/query.py", line 411, in count return self.query.get_count(using=self.db) File "/mnt/c/lyftrondatasync/LyftrondatasyncAPI/venv/lib/python3.9/site-packages/django/db/models/sql/query.py", line 515, in get_count number = obj.get_aggregation(using, ['__count'])['__count'] File "/mnt/c/lyftrondatasync/LyftrondatasyncAPI/venv/lib/python3.9/site-packages/django/db/models/sql/query.py", line 500, in get_aggregation result = compiler.execute_sql(SINGLE) File "/mnt/c/lyftrondatasync/LyftrondatasyncAPI/venv/lib/python3.9/site-packages/django/db/models/sql/compiler.py", line 1156, in execute_sql cursor.execute(sql, params) File "/mnt/c/lyftrondatasync/LyftrondatasyncAPI/venv/lib/python3.9/site-packages/django/db/backends/utils.py", line 98, in execute return super().execute(sql, params) File "/mnt/c/lyftrondatasync/LyftrondatasyncAPI/venv/lib/python3.9/site-packages/sentry_sdk/integrations/django/__init__.py", line 500, in execute return real_execute(self, sql, params) File "/mnt/c/lyftrondatasync/LyftrondatasyncAPI/venv/lib/python3.9/site-packages/django/db/backends/utils.py", line 66, in execute return self._execute_with_wrappers(sql, params, many=False, executor=self._execute) File "/mnt/c/lyftrondatasync/LyftrondatasyncAPI/venv/lib/python3.9/site-packages/django/db/backends/utils.py", line 75, in _execute_with_wrappers return executor(sql, params, many, context) File "/mnt/c/lyftrondatasync/LyftrondatasyncAPI/venv/lib/python3.9/site-packages/django/db/backends/utils.py", line 84, in _execute return self.cursor.execute(sql, params) File "/mnt/c/lyftrondatasync/LyftrondatasyncAPI/venv/lib/python3.9/site-packages/django/db/utils.py", line 90, in __exit__ raise dj_exc_value.with_traceback(traceback) from exc_value File "/mnt/c/lyftrondatasync/LyftrondatasyncAPI/venv/lib/python3.9/site-packages/django/db/backends/utils.py", line 84, in _execute return self.cursor.execute(sql, params) django.db.utils.OperationalError: SSL SYSCALL error: EOF detected During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/mnt/c/lyftrondatasync/LyftrondatasyncAPI/venv/lib/python3.9/site-packages/django/db/backends/base/base.py", line 237, in _cursor return self._prepare_cursor(self.create_cursor(name)) File "/mnt/c/lyftrondatasync/LyftrondatasyncAPI/venv/lib/python3.9/site-packages/django/utils/asyncio.py", line 26, in inner return func(*args, **kwargs) File "/mnt/c/lyftrondatasync/LyftrondatasyncAPI/venv/lib/python3.9/site-packages/django/db/backends/postgresql/base.py", line 236, in create_cursor cursor = self.connection.cursor() psycopg2.InterfaceError: connection already closed The above exception was the direct cause of the following exception: Traceback (most recent call last): File "/mnt/c/lyftrondatasync/LyftrondatasyncAPI/venv/lib/python3.9/site-packages/django_q/cluster.py", line 381, in worker res = f(*task['args'], **task['kwargs']) File "/mnt/c/lyftrondatasync/LyftrondatasyncAPI/connector/sync_utils.py", line 1858, in run_data_sync save_batch_error(parent_batch_obj, response, integration_obj) File "/mnt/c/lyftrondatasync/LyftrondatasyncAPI/batchesapi/utils.py", line 13, in save_batch_error batch_obj.save() File "/mnt/c/lyftrondatasync/LyftrondatasyncAPI/venv/lib/python3.9/site-packages/django/db/models/base.py", line 753, in save self.save_base(using=using, force_insert=force_insert, File "/mnt/c/lyftrondatasync/LyftrondatasyncAPI/venv/lib/python3.9/site-packages/django/db/models/base.py", line 790, in save_base updated = self._save_table( File "/mnt/c/lyftrondatasync/LyftrondatasyncAPI/venv/lib/python3.9/site-packages/django/db/models/base.py", line 872, in _save_table updated = self._do_update(base_qs, using, pk_val, values, update_fields, File "/mnt/c/lyftrondatasync/LyftrondatasyncAPI/venv/lib/python3.9/site-packages/django/db/models/base.py", line 926, in _do_update return filtered._update(values) > 0 File "/mnt/c/lyftrondatasync/LyftrondatasyncAPI/venv/lib/python3.9/site-packages/django/db/models/query.py", line 803, in _update return query.get_compiler(self.db).execute_sql(CURSOR) File "/mnt/c/lyftrondatasync/LyftrondatasyncAPI/venv/lib/python3.9/site-packages/django/db/models/sql/compiler.py", line 1522, in execute_sql cursor = super().execute_sql(result_type) File "/mnt/c/lyftrondatasync/LyftrondatasyncAPI/venv/lib/python3.9/site-packages/django/db/models/sql/compiler.py", line 1154, in execute_sql cursor = self.connection.cursor() File "/mnt/c/lyftrondatasync/LyftrondatasyncAPI/venv/lib/python3.9/site-packages/django/utils/asyncio.py", line 26, in inner return func(*args, **kwargs) File "/mnt/c/lyftrondatasync/LyftrondatasyncAPI/venv/lib/python3.9/site-packages/django/db/backends/base/base.py", line 259, in cursor return self._cursor() File "/mnt/c/lyftrondatasync/LyftrondatasyncAPI/venv/lib/python3.9/site-packages/django/db/backends/base/base.py", line 237, in _cursor return self._prepare_cursor(self.create_cursor(name)) File "/mnt/c/lyftrondatasync/LyftrondatasyncAPI/venv/lib/python3.9/site-packages/django/db/utils.py", line 90, in __exit__ raise dj_exc_value.with_traceback(traceback) from exc_value File "/mnt/c/lyftrondatasync/LyftrondatasyncAPI/venv/lib/python3.9/site-packages/django/db/backends/base/base.py", line 237, in _cursor return self._prepare_cursor(self.create_cursor(name)) File "/mnt/c/lyftrondatasync/LyftrondatasyncAPI/venv/lib/python3.9/site-packages/django/utils/asyncio.py", line 26, in inner return func(*args, **kwargs) File "/mnt/c/lyftrondatasync/LyftrondatasyncAPI/venv/lib/python3.9/site-packages/django/db/backends/postgresql/base.py", line 236, in create_cursor cursor = self.connection.cursor() django.db.utils.InterfaceError: connection already closed
解决方案
调整PostgreSQL连接超时配置
PostgreSQL默认的空闲事务超时或TCP连接保活设置可能小于任务时长,导致连接被服务器主动断开。修改postgresql.conf文件:# 设置空闲事务超时为2小时(根据任务实际时长调整) idle_in_transaction_session_timeout = 7200000 # 开启TCP保活机制,防止中间网络设备断开连接 tcp_keepalives_idle = 60 tcp_keepalives_interval = 10 tcp_keepalives_count = 10修改后重启PostgreSQL服务生效。
配置Django自动重连数据库
在Django的数据库配置中设置连接最大存活时间,并添加psycopg2的重连参数:DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': 'your_db_name', 'USER': 'your_db_user', 'PASSWORD': 'your_db_pass', 'HOST': 'your_db_host', 'PORT': '5432', # 设置连接最大存活时间,需小于数据库超时时间 'CONN_MAX_AGE': 3600, 'OPTIONS': { 'sslmode': 'require', 'connect_timeout': 10, } } }同时在任务代码中捕获连接异常,手动重置连接:
from django.db import connection, OperationalError, InterfaceError def run_data_sync(): try: # 执行数据库操作 if not table_pipeline_json.count(): pass except (OperationalError, InterfaceError): # 重置数据库连接 connection.close() # 重新执行操作 if not table_pipeline_json.count(): pass # 后续更新逻辑优化Django Q的Worker回收策略
当前配置中recycle=500表示每个Worker处理500个任务后重启,但长任务会导致Worker持有的数据库连接长时间闲置被断开。调整配置减少每个Worker处理的任务数,或设置超时自动重启:{ "name": "sampleapp", "workers": 8, "recycle": 100, # 减少每个Worker处理的任务数,尽早释放连接 "timeout": 3600, # 设置Worker超时时间为1小时,超时自动重启 // 其他配置保持不变 }避免长任务中闲置数据库连接
如果任务中间存在长时间的非数据库操作(比如等待外部API响应),可以在这段时间关闭数据库连接,需要时再重新打开:def run_data_sync(): # 先执行必要的数据库操作 data = SomeModel.objects.all() # 关闭连接 connection.close() # 执行长时间的非数据库操作 sync_external_data() # 重新连接数据库执行更新 connection.connect() SomeModel.objects.filter(...).update(...)
内容的提问来源于stack exchange,提问作者Hassan Shahbaz
相关产品推荐
相关产品推荐

