|
1 | | -from django.db import close_old_connections |
| 1 | +from concurrent.futures import ThreadPoolExecutor |
| 2 | + |
| 3 | +from django.db import connections |
2 | 4 |
|
3 | 5 | from asgiref.sync import SyncToAsync |
4 | 6 |
|
| 7 | +main_thread_connections = {name: connections[name] for name in connections} |
| 8 | + |
| 9 | + |
| 10 | +def _inherit_main_thread_connections(): |
| 11 | + """Copy/use DB connections in atomic block from main thread. |
| 12 | +
|
| 13 | + This is required for tests using Django's TestCase. |
| 14 | + """ |
| 15 | + for name in main_thread_connections: |
| 16 | + if main_thread_connections[name].in_atomic_block: |
| 17 | + connections[name] = main_thread_connections[name] |
| 18 | + connections[name].inc_thread_sharing() |
| 19 | + |
5 | 20 |
|
6 | 21 | class DatabaseSyncToAsync(SyncToAsync): |
7 | 22 | """ |
8 | | - SyncToAsync version that cleans up old database connections when it exits. |
| 23 | + SyncToAsync version that cleans up old database connections. |
9 | 24 | """ |
10 | 25 |
|
| 26 | + executor = ThreadPoolExecutor( |
| 27 | + # TODO |
| 28 | + # max_workers=settings.N_SYNC_DATABASE_CONNECTIONS, |
| 29 | + thread_name_prefix='our-database-sync-to-async-', |
| 30 | + initializer=_inherit_main_thread_connections, |
| 31 | + ) |
| 32 | + |
| 33 | + def _close_old_connections(self): |
| 34 | + """Like django.db.close_old_connections, but skipping in_atomic_block.""" |
| 35 | + for conn in connections.all(): |
| 36 | + if conn.in_atomic_block: |
| 37 | + continue |
| 38 | + conn.close_if_unusable_or_obsolete() |
| 39 | + |
11 | 40 | def thread_handler(self, loop, *args, **kwargs): |
12 | | - close_old_connections() |
| 41 | + self._close_old_connections() |
13 | 42 | try: |
14 | 43 | return super().thread_handler(loop, *args, **kwargs) |
15 | 44 | finally: |
16 | | - close_old_connections() |
| 45 | + self._close_old_connections() |
17 | 46 |
|
18 | 47 |
|
19 | 48 | # The class is TitleCased, but we want to encourage use as a callable/decorator |
|
0 commit comments