добавить graceful shutdown для пула

на текущий момент при закрытии пула мы закрываем все его соединения, в том числе внутри `_used`, то есть используемые в моменте:

    async def close(self) -> None:
        """
        Closes all connections in the pool.

        Note:
            This should be called during application shutdown to clean up resources.
        """
        if self._topology_tracker is not None:
            await self._topology_tracker.stop()

        await self._query_metadata_service.close()

        async with self._lock:
            total = len(self._pool) + len(self._used)
            while self._pool:
                conn = self._pool.popleft()
                try:
                    await conn.close()
                except Exception as e:
                    logger.warning("Pool close: could not close connection: %s", e)
            for conn in self._used:
                try:
                    await conn.close()
                except Exception as e:
                    logger.warning("Pool close: could not close connection: %s", e)
            self._used.clear()
            logger.info("Pool closed (%d connection(s))", total)

предлагается добавить `timeout` в pool.close() и закрывать используемые соединения аккуратно:

  • пометить используемые соединения на закрытие (for conn in pool._used set to_evict = True) (в рамках задачи #68 (closed) было добавлено отложенное удаление)
  • закрыть все неиспользуемые соединения (conn in pool._pool)
  • ждать и проверять _used вплоть до таймаута (тут мы ждем пока _used будут зарелижены и закрыты за счет твоего механизма), возможно timeout минус какое-то время

и в конце есть варианты обработки:

  • если за таймаут не удалось закрыть, то выдавать ошибку закрытия (тогда не будет идемпотентности у закрытия, но зато не будут обрываться ошибками запросы на используемых соединениях)
  • или по истечению таймаута форсировать закрытие все равно

в asyncpg есть похожие механизмы. в рамках задачи необходимо их изучить, возможно почерпнуть еще полезный вариант

Edited by Asya Lomakina