]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
adjustments for unreliable gc
authorMike Bayer <mike_mp@zzzcomputing.com>
Mon, 5 Dec 2022 04:25:14 +0000 (23:25 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 5 Dec 2022 14:49:37 +0000 (09:49 -0500)
sporadic (and at the moment persistent) test failures
related to aiosqlite seem to have in common that Python
gc stops working fully when we run a lot of tests with
aiosqlite.  The failures are not limited to aiosqlite
as they are more involving places where we assume or
expect gc.collect() to get rid of things, and it doesn't.

Identify (based on reproducible case on the d3 CI runner)
the spots where this happens and add fixes.

test/orm/test_transaction.py test_gced_delete_on_rollback
has always been a very sensitive test with a lot of issues,
so here we move it to the test_memusage suite and limit
it only to when the memusage suite is running.

Change-Id: I683412d0effe8732c45980b40722e5bb63431177
(cherry picked from commit ca46caede4b8d846f3cd48e642922ae821d0be2b)

test/aaa_profiling/test_memusage.py
test/orm/test_transaction.py
test/requirements.py

index bd727a842ac14166a99c375fddc44a8cc3b4c657..4b2699e1bea1f587bcfaf3706703bf9bbd6fd7f3 100644 (file)
@@ -17,6 +17,7 @@ from sqlalchemy import Unicode
 from sqlalchemy import util
 from sqlalchemy.engine import result
 from sqlalchemy.orm import aliased
+from sqlalchemy.orm import attributes
 from sqlalchemy.orm import clear_mappers
 from sqlalchemy.orm import configure_mappers
 from sqlalchemy.orm import declarative_base
@@ -1755,3 +1756,57 @@ class CycleTest(_fixtures.FixtureTest):
             s.close()
 
         go()
+
+
+class MiscMemoryIntensiveTests(fixtures.TestBase):
+    __tags__ = ("memory_intensive",)
+
+    @testing.fixture
+    def user_fixture(self, decl_base):
+        class User(decl_base):
+            __tablename__ = "user"
+
+            id = Column(Integer, primary_key=True)
+            name = Column(String(50))
+
+        decl_base.metadata.create_all(testing.db)
+        yield User
+
+    @testing.requires.predictable_gc
+    def test_gced_delete_on_rollback(self, user_fixture):
+        User = user_fixture
+
+        s = fixture_session()
+        u1 = User(name="ed")
+        s.add(u1)
+        s.commit()
+
+        s.delete(u1)
+        u1_state = attributes.instance_state(u1)
+        assert u1_state in s.identity_map.all_states()
+        assert u1_state in s._deleted
+        s.flush()
+        assert u1_state not in s.identity_map.all_states()
+        assert u1_state not in s._deleted
+        del u1
+        gc_collect()
+        gc_collect()
+        gc_collect()
+        assert u1_state.obj() is None
+
+        s.rollback()
+        # new in 1.1, not in identity map if the object was
+        # gc'ed and we restore snapshot; we've changed update_impl
+        # to just skip this object
+        assert u1_state not in s.identity_map.all_states()
+
+        # in any version, the state is replaced by the query
+        # because the identity map would switch it
+        u1 = s.query(User).filter_by(name="ed").one()
+        assert u1_state not in s.identity_map.all_states()
+
+        eq_(s.scalar(select(func.count("*")).select_from(User.__table__)), 1)
+        s.delete(u1)
+        s.flush()
+        eq_(s.scalar(select(func.count("*")).select_from(User.__table__)), 0)
+        s.commit()
index e077220e19b918158367b90d254f70a81c437853..9d81e95b22d14fd2d34a53b193260f58367da10e 100644 (file)
@@ -1345,43 +1345,6 @@ class AutoExpireTest(_LocalFixture):
         assert u1 in s
         assert u1 not in s.deleted
 
-    @testing.requires.predictable_gc
-    def test_gced_delete_on_rollback(self):
-        User, users = self.classes.User, self.tables.users
-
-        s = fixture_session()
-        u1 = User(name="ed")
-        s.add(u1)
-        s.commit()
-
-        s.delete(u1)
-        u1_state = attributes.instance_state(u1)
-        assert u1_state in s.identity_map.all_states()
-        assert u1_state in s._deleted
-        s.flush()
-        assert u1_state not in s.identity_map.all_states()
-        assert u1_state not in s._deleted
-        del u1
-        gc_collect()
-        assert u1_state.obj() is None
-
-        s.rollback()
-        # new in 1.1, not in identity map if the object was
-        # gc'ed and we restore snapshot; we've changed update_impl
-        # to just skip this object
-        assert u1_state not in s.identity_map.all_states()
-
-        # in any version, the state is replaced by the query
-        # because the identity map would switch it
-        u1 = s.query(User).filter_by(name="ed").one()
-        assert u1_state not in s.identity_map.all_states()
-
-        eq_(s.scalar(select(func.count("*")).select_from(users)), 1)
-        s.delete(u1)
-        s.flush()
-        eq_(s.scalar(select(func.count("*")).select_from(users)), 0)
-        s.commit()
-
     def test_trans_deleted_cleared_on_rollback(self):
         User = self.classes.User
         s = fixture_session()
index b5e9711115cb13d2daaf8ae9d69ae7b115524201..55c3383a42f5e7b031a09117883ad0ebbd118212 100644 (file)
@@ -409,6 +409,7 @@ class DefaultRequirements(SuiteRequirements):
             [
                 no_support("oracle", "Oracle XE usually can't handle these"),
                 no_support("mssql+pyodbc", "MS ODBC drivers struggle"),
+                no_support("+aiosqlite", "very unreliable driver"),
                 self._running_on_windows(),
             ]
         )