Python “数据库”;正在被其他用户访问”;将ThreadPoolExecutor与Django一起使用时出错

Python “数据库”;正在被其他用户访问”;将ThreadPoolExecutor与Django一起使用时出错,python,django,multithreading,postgresql,Python,Django,Multithreading,Postgresql,我正在从事一个项目,在这个项目中,我们解析一个稍大的文件,并使用ThreadPoolExecutor异步处理每一行(我们对每一行进行API调用)。这过去是同步完成的,我们有一个通过测试的套件。但是,现在,在运行测试时,Django的默认测试运行程序错误会在teardown\u数据库中出现: Traceback (most recent call last): File "manage.py", line 34, in <module> execute_from_comma

我正在从事一个项目,在这个项目中,我们解析一个稍大的文件,并使用
ThreadPoolExecutor
异步处理每一行(我们对每一行进行API调用)。这过去是同步完成的,我们有一个通过测试的套件。但是,现在,在运行测试时,Django的默认测试运行程序错误会在
teardown\u数据库中出现:

Traceback (most recent call last):
  File "manage.py", line 34, in <module>
    execute_from_command_line(sys.argv)
  File "/usr/local/lib/python3.5/site-packages/django/core/management/__init__.py", line 367, in execute_from_command_line
    utility.execute()
  File "/usr/local/lib/python3.5/site-packages/django/core/management/__init__.py", line 359, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/usr/local/lib/python3.5/site-packages/django/core/management/commands/test.py", line 29, in run_from_argv
    super(Command, self).run_from_argv(argv)
  File "/usr/local/lib/python3.5/site-packages/django/core/management/base.py", line 294, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/usr/local/lib/python3.5/site-packages/django/core/management/base.py", line 345, in execute
    output = self.handle(*args, **options)
  File "/usr/local/lib/python3.5/site-packages/django/core/management/commands/test.py", line 72, in handle
    failures = test_runner.run_tests(test_labels)
  File "/usr/local/lib/python3.5/site-packages/django/test/runner.py", line 551, in run_tests
    self.teardown_databases(old_config)
  File "/usr/local/lib/python3.5/site-packages/django/test/runner.py", line 526, in teardown_databases
    connection.creation.destroy_test_db(old_name, self.verbosity, self.keepdb)
  File "/usr/local/lib/python3.5/site-packages/django/db/backends/base/creation.py", line 264, in destroy_test_db
    self._destroy_test_db(test_database_name, verbosity)
  File "/usr/local/lib/python3.5/site-packages/django/db/backends/base/creation.py", line 283, in _destroy_test_db
    % self.connection.ops.quote_name(test_database_name))
  File "/usr/local/lib/python3.5/site-packages/django/db/backends/utils.py", line 64, in execute
    return self.cursor.execute(sql, params)
  File "/usr/local/lib/python3.5/site-packages/django/db/utils.py", line 94, in __exit__
    six.reraise(dj_exc_type, dj_exc_value, traceback)
  File "/usr/local/lib/python3.5/site-packages/django/utils/six.py", line 685, in reraise
    raise value.with_traceback(tb)
  File "/usr/local/lib/python3.5/site-packages/django/db/backends/utils.py", line 62, in execute
    return self.cursor.execute(sql)
django.db.utils.OperationalError: database "test_sftpm_db" is being accessed by other users
DETAIL:  There are 10 other sessions using the database.
回溯(最近一次呼叫最后一次):
文件“manage.py”,第34行,在
从命令行(sys.argv)执行命令
文件“/usr/local/lib/python3.5/site packages/django/core/management/___init__.py”,第367行,从命令行执行
utility.execute()
文件“/usr/local/lib/python3.5/site packages/django/core/management/__init__.py”,第359行,在execute中
self.fetch_命令(子命令)。从_argv(self.argv)运行_
文件“/usr/local/lib/python3.5/site packages/django/core/management/commands/test.py”,第29行,在运行时从
超级(命令,自我)。从_argv(argv)运行_
文件“/usr/local/lib/python3.5/site packages/django/core/management/base.py”,第294行,在运行中,从
self.execute(*args,**cmd_选项)
文件“/usr/local/lib/python3.5/site packages/django/core/management/base.py”,执行中第345行
输出=self.handle(*args,**选项)
文件“/usr/local/lib/python3.5/site packages/django/core/management/commands/test.py”,第72行,在handle中
失败=测试运行程序。运行测试(测试标签)
文件“/usr/local/lib/python3.5/site packages/django/test/runner.py”,第551行,运行测试中
self.teardown_数据库(旧配置)
文件“/usr/local/lib/python3.5/site packages/django/test/runner.py”,第526行,位于teardown_数据库中
connection.creation.destroy\u test\u db(旧名称、self.verbosity、self.keepdb)
文件“/usr/local/lib/python3.5/site packages/django/db/backends/base/creation.py”,第264行,在destroy\u test\u db中
self.\u destroy\u test\u db(测试数据库名称、详细程度)
文件“/usr/local/lib/python3.5/site packages/django/db/backends/base/creation.py”,第283行,在数据库中
%self.connection.ops.quote\u name(test\u database\u name))
文件“/usr/local/lib/python3.5/site packages/django/db/backends/utils.py”,执行中的第64行
返回self.cursor.execute(sql,params)
文件“/usr/local/lib/python3.5/site packages/django/db/utils.py”,第94行,在__
6.重新播放(dj_exc_类型、dj_exc_值、回溯)
文件“/usr/local/lib/python3.5/site packages/django/utils/six.py”,第685行,在reraise中
通过_回溯(tb)提升值
文件“/usr/local/lib/python3.5/site packages/django/db/backends/utils.py”,执行中的第62行
返回self.cursor.execute(sql)
django.db.utils.OperationalError:其他用户正在访问数据库“test\u sftpm\u db”
详细信息:还有10个其他会话正在使用该数据库。
(我们使用了10名工人)


我尝试在代码中的许多地方手动关闭连接,但没有成功。有没有合适的方法来解决这个问题?

这看起来更像是Django的问题,而不是Postgres的问题-- 例如,请参见此票证:

从您提供的信息中,我看到Django在尝试删除测试数据库之前并没有关闭到它的所有连接。在这种情况下,Postges试图保护其他会话不受数据丢失的影响,因此它无法在所有会话断开连接之前删除/重命名数据库

如果需要,您可以使用
pg\u terminate\u backend(..)
函数和
pg\u stat\u activity
查看您已经使用的内容,手动删除测试数据库:

select pg_terminate_backend(pid) 
from pg_stat_activity 
where
  datname = 'DATABASE_NAME'
;

drop database DATABASE_NAME;
如果由于某种原因,有人速度很快,并且成功地在这两个命令之间连接,
drop database
将再次失败。在这种情况下,您可以重复此操作,但在此之前,请撤消从
public
连接到此数据库的权限--这将阻止连接到此数据库:

revoke connect on database DATABASE_NAME from public;

…然后重复上述操作。

确保在处理线程时在代码中调用
connection.close()
。这为我解决了问题,而其他建议的解决方案(测试方法的装饰器、更改db设置、上面Nick所示的db函数、重新启动postgres)没有解决问题

我遇到了类似的问题,并通过使用
django.test.testcases.TransactionTestCase
作为我的测试类的超类来解决了它

在我的PyCharm(运行进程)+postgres db案例中,我通过运行以下命令来修复它:

1.以拥有正确db权限的用户身份登录,在我的情况下,默认为“postgres”

二,。然后使用以下命令删除与现有数据库的所有连接(在我的示例中为“mydbname”):

# select pg_terminate_backend(pid) from pg_stat_activity where datname='mydbname';

三,。然后以正常方式单击“运行”按钮。

到数据库的连接是线程本地的。我最终解决了这个问题,为执行者返回的每个future添加了一个回调

from django.db import connections

def on_done(future):
    # Because each thread has a connection, so here you can call close_all() to close all connections under this thread name.
    connections.close_all()

def main():
    # ...
    with ThreadPoolExecutor() as executor:
        while True:
            future = executor.submit(do, get_a_job())
            future.add_done_callback(on_done)
更多信息请参见:

另一个可能的问题是,您正在子类化
TestCase
,它对您的测试持有全局锁。子类化
TransactionTestCase
将修复此问题,并允许您的测试可能产生的任何线程与数据库通信

from django.db import connections

def on_done(future):
    # Because each thread has a connection, so here you can call close_all() to close all connections under this thread name.
    connections.close_all()

def main():
    # ...
    with ThreadPoolExecutor() as executor:
        while True:
            future = executor.submit(do, get_a_job())
            future.add_done_callback(on_done)