@3FANG

Почему возникают ошибки «Bad file descriptor» и «Event loop is closed»?

Сразу оговорюсь, что я недавно начал изучать asyncio и буду рад указанию и объяснению любых допускаемых мною ошибок.

У меня есть скрипт, который берет адреса TON кошельков из БД и делает асинхронные запросы для получения информации о них в индексатор toncenter.
import asyncio
import os

import asyncpg
import aiohttp
from dotenv import load_dotenv


load_dotenv()

DB_URL = os.getenv('POSTGRESQL_URL')


async def process_addresses(pool: asyncpg.Pool):
    async with pool.acquire() as connection:
        addresses = [record['address'] for record in await connection.fetch('SELECT * FROM Rawaccount LIMIT 1 OFFSET 7')]

    async with aiohttp.ClientSession() as session:
        for address in addresses:

            proxy = "адрес прокси"
            key = "ключ"
            url = f"https://toncenter.com/api/v3/wallet?address={address}&api_key={key}" 

            async with session.get(url, proxy=proxy) as response:
                connection = response.connection
                print(response.status)


async def main():
    pool = await asyncpg.create_pool(DB_URL)

    get_info_task = asyncio.create_task(process_addresses(pool))

    await asyncio.gather(get_info_task)


asyncio.run(main())

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

С недавних пор, по непонятной для меня причине стала возникать следующая ошибка:
200
Fatal error on SSL transport
protocol: <asyncio.sslproto.SSLProtocol object at 0x73b76b092e90>
transport: <_SelectorSocketTransport closing fd=8>
Traceback (most recent call last):
  File "/usr/lib/python3.10/asyncio/selector_events.py", line 924, in write
    n = self._sock.send(data)
OSError: [Errno 9] Bad file descriptor

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/lib/python3.10/asyncio/sslproto.py", line 690, in _process_write_backlog
    self._transport.write(chunk)
  File "/usr/lib/python3.10/asyncio/selector_events.py", line 930, in write
    self._fatal_error(exc, 'Fatal write error on socket transport')
  File "/usr/lib/python3.10/asyncio/selector_events.py", line 725, in _fatal_error
    self._force_close(exc)
  File "/usr/lib/python3.10/asyncio/selector_events.py", line 737, in _force_close
    self._loop.call_soon(self._call_connection_lost, exc)
  File "/usr/lib/python3.10/asyncio/base_events.py", line 753, in call_soon
    self._check_closed()
  File "/usr/lib/python3.10/asyncio/base_events.py", line 515, in _check_closed
    raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed
Fatal error on SSL transport
protocol: <asyncio.sslproto.SSLProtocol object at 0x73b76b092e30>
transport: <_SelectorSocketTransport closing fd=11>
Traceback (most recent call last):
  File "/usr/lib/python3.10/asyncio/selector_events.py", line 924, in write
    n = self._sock.send(data)
OSError: [Errno 9] Bad file descriptor

During handling of the above exception, another exception occurred:

# так еще несколько раз и в конце:

Traceback (most recent call last):
  File "/usr/lib/python3.10/asyncio/sslproto.py", line 690, in _process_write_backlog
    self._transport.write(chunk)
  File "/usr/lib/python3.10/asyncio/selector_events.py", line 930, in write
    self._fatal_error(exc, 'Fatal write error on socket transport')
  File "/usr/lib/python3.10/asyncio/selector_events.py", line 725, in _fatal_error
    self._force_close(exc)
  File "/usr/lib/python3.10/asyncio/selector_events.py", line 737, in _force_close
    self._loop.call_soon(self._call_connection_lost, exc)
  File "/usr/lib/python3.10/asyncio/base_events.py", line 753, in call_soon
    self._check_closed()
  File "/usr/lib/python3.10/asyncio/base_events.py", line 515, in _check_closed
    raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed

Я погуглил и нашел обсуждение подобной ошибки.

Там в качестве решения предложили использовать
loop = asyncio.get_event_loop()
loop.run_until_complete(main())

Вместо asyncio.run(main()).

И теперь действительно ошибка ушла и я в выводе просто вижу 200, как и должно быть.

Первый возникший вопрос - почему появляется ошибка? Второй - почему изменение способа вызова событийного цикла устраняет эту ошибку?

upd:
В процессе написания этого вопроса я понял причину возникновения этой ошибки - я не закрываю пул соединений с БД.
Т.е. сейчас я добавил в конце main() строку await pool.close() и даже с вызовом asyncio.run(main()) ошибка перестала возникать.

Тем не менее, я все равно до конца не понял, что точно означает эта ошибка и почему изменение вызова цикла ее исправляет.
  • Вопрос задан
  • 322 просмотра
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы