|
8 | 8 | import asyncio |
9 | 9 | import gc |
10 | 10 | import inspect |
| 11 | +import logging |
11 | 12 | import os |
12 | 13 | import pathlib |
13 | 14 | import platform |
@@ -1593,28 +1594,55 @@ async def test_full_reconnect_on_node_change_role(self): |
1593 | 1594 | # Force a new connection to be created |
1594 | 1595 | await pool.fetchval('SELECT 1') |
1595 | 1596 |
|
1596 | | - await self.simulate_cluster_recovery_mode() |
| 1597 | + logs = [] |
1597 | 1598 |
|
1598 | | - # current pool connection info cache is expired, |
1599 | | - # but we don't know it yet |
1600 | | - with self.assertRaises(asyncpg.TargetServerAttributeNotMatched) as cm: |
1601 | | - await pool.execute('SELECT 1') |
| 1599 | + class CaptureLogs(logging.Handler): |
| 1600 | + def emit(self, record): |
| 1601 | + logs.append(record) |
1602 | 1602 |
|
1603 | | - self.assertEqual( |
1604 | | - cm.exception.args[0], |
1605 | | - "None of the hosts match the target attribute requirement " |
1606 | | - "<SessionAttribute.primary: 'primary'>" |
1607 | | - ) |
| 1603 | + # Maintenance may race foreground acquisition, so retries are |
| 1604 | + # optional. Capture their expected warnings through pool shutdown. |
| 1605 | + with mock.patch.object(pg_pool.logger, 'handlers', [CaptureLogs()]), \ |
| 1606 | + mock.patch.object(pg_pool.logger, 'propagate', False): |
| 1607 | + try: |
| 1608 | + await self.simulate_cluster_recovery_mode() |
1608 | 1609 |
|
1609 | | - # force reconnect |
1610 | | - with self.assertRaises(asyncpg.TargetServerAttributeNotMatched) as cm: |
1611 | | - await pool.execute('SELECT 1') |
| 1610 | + # current pool connection info cache is expired, |
| 1611 | + # but we don't know it yet |
| 1612 | + with self.assertRaises( |
| 1613 | + asyncpg.TargetServerAttributeNotMatched) as cm: |
| 1614 | + await pool.execute('SELECT 1') |
1612 | 1615 |
|
1613 | | - self.assertEqual( |
1614 | | - cm.exception.args[0], |
1615 | | - "None of the hosts match the target attribute requirement " |
1616 | | - "<SessionAttribute.primary: 'primary'>" |
1617 | | - ) |
| 1616 | + self.assertEqual( |
| 1617 | + cm.exception.args[0], |
| 1618 | + "None of the hosts match the target attribute requirement " |
| 1619 | + "<SessionAttribute.primary: 'primary'>" |
| 1620 | + ) |
| 1621 | + |
| 1622 | + # force reconnect |
| 1623 | + with self.assertRaises( |
| 1624 | + asyncpg.TargetServerAttributeNotMatched) as cm: |
| 1625 | + await pool.execute('SELECT 1') |
| 1626 | + |
| 1627 | + self.assertEqual( |
| 1628 | + cm.exception.args[0], |
| 1629 | + "None of the hosts match the target attribute requirement " |
| 1630 | + "<SessionAttribute.primary: 'primary'>" |
| 1631 | + ) |
| 1632 | + finally: |
| 1633 | + await pool.close() |
| 1634 | + |
| 1635 | + for record in logs: |
| 1636 | + self.assertEqual(record.levelno, logging.WARNING) |
| 1637 | + self.assertEqual(record.getMessage(), |
| 1638 | + 'Failed to restore the pool connection floor; ' |
| 1639 | + 'retrying') |
| 1640 | + self.assertIsNotNone(record.exc_info) |
| 1641 | + self.assertIsInstance(record.exc_info[1], ( |
| 1642 | + OSError, |
| 1643 | + asyncpg.CannotConnectNowError, |
| 1644 | + asyncpg.TargetServerAttributeNotMatched, |
| 1645 | + )) |
1618 | 1646 |
|
1619 | 1647 |
|
1620 | 1648 | @unittest.skipIf(os.environ.get('PGHOST'), 'using remote cluster for testing') |
|
0 commit comments