Version in base suite: 17.2.1-2 Base version: python-oslo.db_17.2.1-2 Target version: python-oslo.db_17.2.1-2+deb13u1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/p/python-oslo.db/python-oslo.db_17.2.1-2.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/p/python-oslo.db/python-oslo.db_17.2.1-2+deb13u1.dsc changelog | 7 patches/Add_MariaDB_error_1020_handling_as_DBConsistencyError_subclass_of_DBDeadlock.patch | 186 ++++++++++ patches/series | 1 3 files changed, 194 insertions(+) dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpjsz7nprr/python-oslo.db_17.2.1-2.dsc: no acceptable signature found dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpjsz7nprr/python-oslo.db_17.2.1-2+deb13u1.dsc: no acceptable signature found diff -Nru python-oslo.db-17.2.1/debian/changelog python-oslo.db-17.2.1/debian/changelog --- python-oslo.db-17.2.1/debian/changelog 2025-03-28 09:19:31.000000000 +0000 +++ python-oslo.db-17.2.1/debian/changelog 2025-08-11 09:35:58.000000000 +0000 @@ -1,3 +1,10 @@ +python-oslo.db (17.2.1-2+deb13u1) trixie; urgency=medium + + * Add patch: + Add_MariaDB_error_1020_handling_as_DBConsistencyError_subclass_....patch. + + -- Thomas Goirand Mon, 11 Aug 2025 11:35:58 +0200 + python-oslo.db (17.2.1-2) unstable; urgency=medium * Uploading to unstable. diff -Nru python-oslo.db-17.2.1/debian/patches/Add_MariaDB_error_1020_handling_as_DBConsistencyError_subclass_of_DBDeadlock.patch python-oslo.db-17.2.1/debian/patches/Add_MariaDB_error_1020_handling_as_DBConsistencyError_subclass_of_DBDeadlock.patch --- python-oslo.db-17.2.1/debian/patches/Add_MariaDB_error_1020_handling_as_DBConsistencyError_subclass_of_DBDeadlock.patch 1970-01-01 00:00:00.000000000 +0000 +++ python-oslo.db-17.2.1/debian/patches/Add_MariaDB_error_1020_handling_as_DBConsistencyError_subclass_of_DBDeadlock.patch 2025-08-11 09:35:58.000000000 +0000 @@ -0,0 +1,186 @@ +Author: Hervé Beraud +Date: Wed, 16 Jul 2025 15:02:27 +0200 +Subject: Add MariaDB error 1020 handling as DBConsistencyError subclass of DBDeadlock + MariaDB under REPEATABLE-READ isolation level can raise error 1020 + "Record has changed since last read" when it detects that a row has + changed since it was last read in the current transaction. This is + part of MariaDB's transaction validation model and has been observed + in OpenStack services like Nova and Keystone. + . + This change adds: + * New DBConsistencyError exception class that inherits from DBDeadlock + * Updated DBDeadlock docstring to cover all concurrent access conflicts + * Filter in exc_filters.py to detect and handle MariaDB error 1020 + * Unit tests to verify proper detection and wrapping + . + The inheritance model ensures that existing retry decorators and error + handling code that catch DBDeadlock will automatically handle this new + MariaDB consistency error, providing seamless backward compatibility + while properly categorizing the error as a transient concurrency issue. + . + This provides better error handling for applications using oslo.db + with MariaDB and allows existing retry mechanisms to work without + modification. + . + For more details about this new isolation level in InnoDB, please + see https://mariadb.com/resources/blog/isolation-level-violation-testing-and-debugging-in-mariadb/ +Bug: https://launchpad.net/bugs/2116186 +Change-Id: Ie7c558ee6b82c206263227260806ca55ebdb77a9 +Signed-off-by: Hervé Beraud +Generated-By: Claude Sonnet 4 +Origin: upstream, https://review.opendev.org/c/openstack/oslo.db/+/957009 +Last-Update: 2025-08-11 + +diff --git a/oslo_db/exception.py b/oslo_db/exception.py +index 1205552..379fd0a 100644 +--- a/oslo_db/exception.py ++++ b/oslo_db/exception.py +@@ -167,17 +167,32 @@ + + class DBDeadlock(DBError): + +- """Database dead lock error. ++ """Database deadlock error. + +- Deadlock is a situation that occurs when two or more different database +- sessions have some data locked, and each database session requests a lock +- on the data that another, different, session has already locked. ++ Raised when a database operation fails due to concurrent access conflicts, ++ including traditional deadlocks where two or more database sessions have ++ conflicting locks, as well as other transient concurrency issues like ++ consistency validation failures. These conditions are typically resolved ++ by retrying the transaction. + """ + + def __init__(self, inner_exception=None): + super().__init__(inner_exception) + + ++class DBConsistencyError(DBDeadlock): ++ ++ """Database consistency error. ++ ++ Raised when a database operation fails due to consistency requirements, ++ such as when MariaDB detects that a row has changed since it was last ++ read in the current transaction under REPEATABLE-READ isolation level. ++ This is a subclass of DBDeadlock as it represents the same kind of ++ transient concurrency issue that can be resolved by retrying the ++ transaction. ++ """ ++ ++ + class DBInvalidUnicodeParameter(Exception): + + """Database unicode error. +diff --git a/oslo_db/sqlalchemy/exc_filters.py b/oslo_db/sqlalchemy/exc_filters.py +index 0aa9772..9b7532e 100644 +--- a/oslo_db/sqlalchemy/exc_filters.py ++++ b/oslo_db/sqlalchemy/exc_filters.py +@@ -92,6 +92,25 @@ + raise exception.DBDeadlock(operational_error) + + ++@filters("mysql", sqla_exc.OperationalError, ++ r"^.*\b1020\b.*Record has changed since last read.*") ++def _mariadb_consistency_error(operational_error, match, engine_name, ++ is_disconnect): ++ """Filter for MariaDB consistency error. ++ ++ MariaDB under REPEATABLE-READ isolation level can raise error 1020 ++ when it detects that a row has changed since it was last read in the ++ current transaction. This is part of MariaDB's transaction validation ++ model and typically indicates a transient condition that can be resolved ++ by retrying the transaction. ++ ++ Example error message: ++ (OperationalError) (pymysql.err.OperationalError) (1020, ++ "Record has changed since last read in table 'table_name'") ++ """ ++ raise exception.DBConsistencyError(operational_error) ++ ++ + @filters("mysql", sqla_exc.IntegrityError, + r"^.*\b1062\b.*Duplicate entry '(?P.*)'" + r" for key '(?P[^']+)'.*$") +diff --git a/oslo_db/tests/sqlalchemy/test_exc_filters.py b/oslo_db/tests/sqlalchemy/test_exc_filters.py +index fe00db8..21d806f 100644 +--- a/oslo_db/tests/sqlalchemy/test_exc_filters.py ++++ b/oslo_db/tests/sqlalchemy/test_exc_filters.py +@@ -1053,6 +1053,48 @@ + ) + + ++class TestMariaDBConsistencyError(TestsExceptionFilter): ++ """Test MariaDB consistency error detection.""" ++ ++ statement = ('UPDATE users SET status = :status_1 ' ++ 'WHERE users.id = :id_1') ++ params = { ++ 'status_1': 'active', ++ 'id_1': 123 ++ } ++ ++ def _run_consistency_error_test( ++ self, dialect_name, message, ++ orig_exception_cls=TestsExceptionFilter.OperationalError): ++ self._run_test( ++ dialect_name, self.statement, ++ orig_exception_cls(message), ++ exception.DBConsistencyError, ++ params=self.params ++ ) ++ ++ def test_mariadb_pymysql_consistency_error(self): ++ self._run_consistency_error_test( ++ "mysql", ++ "(1020, \"Record has changed since " ++ "last read in table 'test_table'\")" ++ ) ++ ++ def test_mariadb_mysqlconnector_consistency_error(self): ++ self._run_consistency_error_test( ++ "mysql", ++ "1020 (HY000): Record has changed since " ++ "last read in table 'test_table'" ++ ) ++ ++ def test_mariadb_consistency_error_with_details(self): ++ self._run_consistency_error_test( ++ "mysql", ++ "(1020, \"Record has changed since last read " ++ "in table 'users' (transaction id 12345)\")" ++ ) ++ ++ + class TestDataError(TestsExceptionFilter): + def _run_bad_data_test(self, dialect_name, message, error_class): + self._run_test(dialect_name, +diff --git a/releasenotes/notes/handle-mariadb-transactional-error-d84c95cdb4d3b27f.yaml b/releasenotes/notes/handle-mariadb-transactional-error-d84c95cdb4d3b27f.yaml +new file mode 100644 +index 0000000..e8d2b38 +--- /dev/null ++++ b/releasenotes/notes/handle-mariadb-transactional-error-d84c95cdb4d3b27f.yaml +@@ -0,0 +1,23 @@ ++--- ++features: ++ - | ++ Added support for detecting and handling MariaDB error 1020 "Record has ++ changed since last read" which occurs under REPEATABLE-READ isolation ++ level when MariaDB detects that a row has changed since it was last read ++ in the current transaction. This error is now wrapped in a new ++ ``DBConsistencyError`` exception class that inherits from ``DBDeadlock``, ++ ensuring that existing retry decorators and error handling code will ++ automatically handle this transient concurrency condition. ++fixes: ++ - | ++ Fixed handling of MariaDB error 1020 that was previously not properly ++ categorized by oslo.db's exception filtering system. This error, which ++ can occur in OpenStack services like Nova and Keystone when using MariaDB ++ with REPEATABLE-READ isolation level, is now properly wrapped in a ++ ``DBConsistencyError`` exception (a subclass of ``DBDeadlock``) instead ++ of being passed through as a generic ``OperationalError``. This ensures ++ that existing retry mechanisms that handle deadlock-like conditions will ++ automatically work with this MariaDB-specific consistency error. ++ ++ For more details, see `bug 2116186 ++ `_. diff -Nru python-oslo.db-17.2.1/debian/patches/series python-oslo.db-17.2.1/debian/patches/series --- python-oslo.db-17.2.1/debian/patches/series 1970-01-01 00:00:00.000000000 +0000 +++ python-oslo.db-17.2.1/debian/patches/series 2025-08-11 09:35:58.000000000 +0000 @@ -0,0 +1 @@ +Add_MariaDB_error_1020_handling_as_DBConsistencyError_subclass_of_DBDeadlock.patch