- 
          
- 
                Notifications
    You must be signed in to change notification settings 
- Fork 33.2k
Fixed #31169 -- Adapted the parallel test runner to use spawn. [GSoC] #12646
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,5 +1,7 @@ | ||||||||||
| import multiprocessing | ||||||||||
| import os | ||||||||||
| import shutil | ||||||||||
| import sqlite3 | ||||||||||
| import sys | ||||||||||
| from pathlib import Path | ||||||||||
|  | ||||||||||
|  | @@ -52,7 +54,10 @@ def get_test_db_clone_settings(self, suffix): | |||||||||
| orig_settings_dict = self.connection.settings_dict | ||||||||||
| source_database_name = orig_settings_dict['NAME'] | ||||||||||
| if self.is_in_memory_db(source_database_name): | ||||||||||
| return orig_settings_dict | ||||||||||
| if multiprocessing.get_start_method() == 'spawn': | ||||||||||
| return {**orig_settings_dict, 'NAME': f'{self.connection.alias}_{suffix}.sqlite3'} | ||||||||||
| elif multiprocessing.get_start_method() == 'fork': | ||||||||||
| return orig_settings_dict | ||||||||||
| 
      Comment on lines
    
      +57
     to 
      +60
    
   There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As the start method can also be  In addition, we can remove some indentation here:     def get_test_db_clone_settings(self, suffix):
        settings_dict = self.connection.settings_dict
        source_database_name = settings_dict['NAME']
        if not self.is_in_memory_db(source_database_name):
            root, ext = os.path.splitext(source_database_name)
            return {**settings_dict, 'NAME': f'{root}_{suffix}{ext}'
        start_method = multiprocessing.get_start_method()
        if start_method == 'fork':
            return settings_dict
        if start_method == 'spawn':
            return {**settings_dict, 'NAME': f'{self.connection.alias}_{suffix}.sqlite3'}
        raise NotImplementedError(f'Cloning with start method {start_method!r} is not supported.')Don't forget to add a test for the exception. | ||||||||||
| else: | ||||||||||
| root, ext = os.path.splitext(orig_settings_dict['NAME']) | ||||||||||
| return {**orig_settings_dict, 'NAME': '{}_{}{}'.format(root, suffix, ext)} | ||||||||||
|  | @@ -80,6 +85,10 @@ def _clone_test_db(self, suffix, verbosity, keepdb=False): | |||||||||
| except Exception as e: | ||||||||||
| self.log('Got an error cloning the test database: %s' % e) | ||||||||||
| sys.exit(2) | ||||||||||
| else: | ||||||||||
| if multiprocessing.get_start_method() == 'spawn': | ||||||||||
| 
      Comment on lines
    
      +88
     to 
      +89
    
   There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 
        Suggested change
       
 | ||||||||||
| ondisk_db = sqlite3.connect(target_database_name, uri=True) | ||||||||||
| self.connection.connection.backup(ondisk_db) | ||||||||||
|  | ||||||||||
| def _destroy_test_db(self, test_database_name, verbosity): | ||||||||||
| if test_database_name and not self.is_in_memory_db(test_database_name): | ||||||||||
|  | @@ -101,3 +110,17 @@ def test_db_signature(self): | |||||||||
| else: | ||||||||||
| sig.append(test_database_name) | ||||||||||
| return tuple(sig) | ||||||||||
|  | ||||||||||
| def setup_worker_connection(self, _worker_id): | ||||||||||
| alias = self.connection.alias | ||||||||||
| worker_db = f'file:memorydb_{str(alias)}_{str(_worker_id)}?mode=memory&cache=shared' | ||||||||||
| sourcedb = sqlite3.connect(f'file:{str(alias)}_{str(_worker_id)}.sqlite3', uri=True) | ||||||||||
|          | ||||||||||
| worker_db = f'file:memorydb_{str(alias)}_{str(_worker_id)}?mode=memory&cache=shared' | |
| sourcedb = sqlite3.connect(f'file:{str(alias)}_{str(_worker_id)}.sqlite3', uri=True) | |
| worker_db = 'file:memorydb_%s_%s?mode=memory&cache=shared' % (alias, _worker_id) | |
| source_db = sqlite3.connect('file:%s_%s.sqlite3' % (alias, _worker_id), uri=True) | 
I would try to reuse get_test_db_clone_settings() 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe I'm missing something, but why do these need to be wrapped with str() anyway? You could do {alias!s} instead of {str(alias)}, but if it isn't required, just use {alias}. In that case you can use f-strings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm confused. Is there something incorrect here or can this just be:
| settings_dict = self.connection.settings_dict | |
| settings_dict['NAME'] = worker_db | |
| self.connection.settings_dict.update(settings_dict) | |
| self.connection.settings_dict['NAME'] = worker_db | 
If the intent was to avoid mutating the original then do this (although I'm not sure it's required):
        self.connection.settings_dict = {**self.connection.settings_dict, 'NAME': worker_db}There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Honestly not sure why I did it this way, will change it & see if something breaks
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|  | @@ -5,6 +5,7 @@ | |||||
| from django.db.backends.base.features import BaseDatabaseFeatures | ||||||
| from django.db.utils import OperationalError | ||||||
| from django.utils.functional import cached_property | ||||||
| from django.utils.version import PY37 | ||||||
|  | ||||||
| from .base import Database | ||||||
|  | ||||||
|  | @@ -24,7 +25,7 @@ class DatabaseFeatures(BaseDatabaseFeatures): | |||||
| can_rollback_ddl = True | ||||||
| can_create_inline_fk = False | ||||||
| supports_paramstyle_pyformat = False | ||||||
| can_clone_databases = True | ||||||
| can_clone_databases = PY37 | ||||||
|          | ||||||
| can_clone_databases = PY37 | |
| can_clone_databases = True | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I moved this hook to a separate PR, see #15457.