(self)
| 130 | |
| 131 | @client_context.require_transactions |
| 132 | def test_session_gc(self): |
| 133 | client = self.rs_client() |
| 134 | pool = get_pool(client) |
| 135 | session = client.start_session() |
| 136 | session.start_transaction() |
| 137 | client.test_session_gc.test.find_one({}, session=session) |
| 138 | # Cleanup the transaction left open on the server |
| 139 | self.addCleanup(self.client.admin.command, "killSessions", [session.session_id]) |
| 140 | if client_context.load_balancer: |
| 141 | self.assertEqual(pool.active_sockets, 1) # Pinned. |
| 142 | |
| 143 | task = PoolLocker(pool) |
| 144 | task.start() |
| 145 | self.assertTrue(task.wait(task.locked, 5), "timed out") |
| 146 | # Garbage collect the session while the pool is locked to ensure we |
| 147 | # don't deadlock. |
| 148 | del session |
| 149 | # On PyPy it can take a few rounds to collect the session. |
| 150 | for _ in range(3): |
| 151 | gc.collect() |
| 152 | task.unlock.set() |
| 153 | task.join(5) |
| 154 | self.assertFalse(task.is_alive()) |
| 155 | self.assertIsNone(task.exc) |
| 156 | |
| 157 | wait_until(lambda: pool.active_sockets == 0, "return socket") |
| 158 | # Run another operation to ensure the socket still works. |
| 159 | client[self.db.name].test.delete_many({}) |
| 160 | |
| 161 | |
| 162 | class PoolLocker(ExceptionCatchingTask): |
nothing calls this directly
no test coverage detected