mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	Fixed #27683 -- Made MySQL default to the read committed isolation level.
Thanks Shai Berger for test help and Adam Johnson for review.
This commit is contained in:
		| @@ -217,7 +217,7 @@ class DatabaseWrapper(BaseDatabaseWrapper): | ||||
|         kwargs['client_flag'] = CLIENT.FOUND_ROWS | ||||
|         # Validate the transaction isolation level, if specified. | ||||
|         options = settings_dict['OPTIONS'].copy() | ||||
|         isolation_level = options.pop('isolation_level', None) | ||||
|         isolation_level = options.pop('isolation_level', 'read committed') | ||||
|         if isolation_level: | ||||
|             isolation_level = isolation_level.lower() | ||||
|             if isolation_level not in self.isolation_levels: | ||||
|   | ||||
| @@ -449,8 +449,14 @@ this entry are the four standard isolation levels: | ||||
| * ``'serializable'`` | ||||
|  | ||||
| or ``None`` to use the server's configured isolation level. However, Django | ||||
| works best with read committed rather than MySQL's default, repeatable read. | ||||
| Data loss is possible with repeatable read. | ||||
| works best with and defaults to read committed rather than MySQL's default, | ||||
| repeatable read. Data loss is possible with repeatable read. | ||||
|  | ||||
| .. versionchanged:: 2.0 | ||||
|  | ||||
|     In older versions, the MySQL database backend defaults to using the | ||||
|     database's isolation level (which defaults to repeatable read) rather | ||||
|     than read committed. | ||||
|  | ||||
| .. _transaction isolation level: https://dev.mysql.com/doc/refman/en/innodb-transaction-isolation-levels.html | ||||
|  | ||||
|   | ||||
| @@ -227,6 +227,15 @@ The end of upstream support for Oracle 11.2 is Dec. 2020. Django 1.11 will be | ||||
| supported until April 2020 which almost reaches this date. Django 2.0 | ||||
| officially supports Oracle 12.1+. | ||||
|  | ||||
| Default MySQL isolation level is read committed | ||||
| ----------------------------------------------- | ||||
|  | ||||
| MySQL's default isolation level, repeatable read, may cause data loss in | ||||
| typical Django usage. To prevent that and for consistency with other databases, | ||||
| the default isolation level is now read committed. You can use the | ||||
| :setting:`DATABASES` setting to :ref:`use a different isolation level | ||||
| <mysql-isolation-level>`, if needed. | ||||
|  | ||||
| :attr:`AbstractUser.last_name <django.contrib.auth.models.User.last_name>` ``max_length`` increased to 150 | ||||
| ---------------------------------------------------------------------------------------------------------- | ||||
|  | ||||
|   | ||||
| @@ -70,6 +70,15 @@ class MySQLTests(TestCase): | ||||
|                 self.isolation_values[self.other_isolation_level] | ||||
|             ) | ||||
|  | ||||
|     def test_default_isolation_level(self): | ||||
|         # If not specified in settings, the default is read committed. | ||||
|         with get_connection() as new_connection: | ||||
|             new_connection.settings_dict['OPTIONS'].pop('isolation_level', None) | ||||
|             self.assertEqual( | ||||
|                 self.get_isolation_level(new_connection), | ||||
|                 self.isolation_values[self.read_committed] | ||||
|             ) | ||||
|  | ||||
|     def test_isolation_level_validation(self): | ||||
|         new_connection = connection.copy() | ||||
|         new_connection.settings_dict['OPTIONS']['isolation_level'] = 'xxx' | ||||
|   | ||||
| @@ -375,18 +375,17 @@ class AtomicMySQLTests(TransactionTestCase): | ||||
|     @skipIf(threading is None, "Test requires threading") | ||||
|     def test_implicit_savepoint_rollback(self): | ||||
|         """MySQL implicitly rolls back savepoints when it deadlocks (#22291).""" | ||||
|         Reporter.objects.create(id=1) | ||||
|         Reporter.objects.create(id=2) | ||||
|  | ||||
|         other_thread_ready = threading.Event() | ||||
|         main_thread_ready = threading.Event() | ||||
|  | ||||
|         def other_thread(): | ||||
|             try: | ||||
|                 with transaction.atomic(): | ||||
|                     Reporter.objects.create(id=1, first_name="Tintin") | ||||
|                     other_thread_ready.set() | ||||
|                     # We cannot synchronize the two threads with an event here | ||||
|                     # because the main thread locks. Sleep for a little while. | ||||
|                     time.sleep(1) | ||||
|                     # 2) ... and this line deadlocks. (see below for 1) | ||||
|                     Reporter.objects.select_for_update().get(id=1) | ||||
|                     main_thread_ready.wait() | ||||
|                     # 1) This line locks... (see below for 2) | ||||
|                     Reporter.objects.exclude(id=1).update(id=2) | ||||
|             finally: | ||||
|                 # This is the thread-local connection, not the main connection. | ||||
| @@ -394,14 +393,18 @@ class AtomicMySQLTests(TransactionTestCase): | ||||
|  | ||||
|         other_thread = threading.Thread(target=other_thread) | ||||
|         other_thread.start() | ||||
|         other_thread_ready.wait() | ||||
|  | ||||
|         with self.assertRaisesMessage(OperationalError, 'Deadlock found'): | ||||
|             # Double atomic to enter a transaction and create a savepoint. | ||||
|             with transaction.atomic(): | ||||
|                 with transaction.atomic(): | ||||
|                     # 1) This line locks... (see above for 2) | ||||
|                     Reporter.objects.create(id=1, first_name="Tintin") | ||||
|                     Reporter.objects.select_for_update().get(id=2) | ||||
|                     main_thread_ready.set() | ||||
|                     # The two threads can't be synchronized with an event here | ||||
|                     # because the other thread locks. Sleep for a little while. | ||||
|                     time.sleep(1) | ||||
|                     # 2) ... and this line deadlocks. (see above for 1) | ||||
|                     Reporter.objects.exclude(id=2).update(id=1) | ||||
|  | ||||
|         other_thread.join() | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user