@toddbarry

Где кроется ошибка в работе с gino?

Для проверки асинхронности обработки подключений, внёс в код view.py - asyncio.sleep(1.5)
Если я верно понимаю, при подключении двоих клиентов к серверу одновременно, при асинхронной обработке оба будут ждать 1.5 секунды, а при синхронной - сперва один дождётся своих 1.5 секунд, а затем начнёт ждать второй.

Однако мои тесты провалились с вылетевшей ошибкой:
Error handling request
Traceback (most recent call last):
  File "/Users/mac/Documents/web/myapp/view.py", line 15, in index
    await User.create(name='jack', fullname='Jack Jones')
  File "/Users/mac/Documents/web/myapp/venv/lib/python3.7/site-packages/gino/crud.py", line 446, in _create_without_instance
    return await cls(**values)._create(bind=bind, timeout=timeout)
  File "/Users/mac/Documents/web/myapp/venv/lib/python3.7/site-packages/gino/crud.py", line 475, in _create
    row = await bind.first(q)
AttributeError: 'NoneType' object has no attribute 'first'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/mac/Documents/web/myapp/venv/lib/python3.7/site-packages/aiohttp/web_protocol.py", line 378, in start
    resp = await self._request_handler(request)
  File "/Users/mac/Documents/web/myapp/venv/lib/python3.7/site-packages/aiohttp/web_app.py", line 341, in _handle
    resp = await handler(request)
  File "/Users/mac/Documents/web/myapp/view.py", line 16, in index
    print(await User.query.gino.all())
  File "/Users/mac/Documents/web/myapp/venv/lib/python3.7/site-packages/gino/api.py", line 183, in __aexit__
    await self._args[0].pop_bind().close()
AttributeError: 'NoneType' object has no attribute 'close'


При этом она возникает только тогда, когда подключаются одновременно двое клиентов (подключаюсь с телефона и ноутбука). Если подключается только один, всё работает хорошо. Кроме того у одного из двоих клиентов никаких ошибок не возникает, а у другому сервер пишет: 500 Internal Server Error.

Добавлю, что сам сервер (на aiohttp) справляется с такими вещами очень хорошо и если убрать подключение with_bind и всю работу с базой данных из вьюхи, оставив только выдачу страницы - никаких проблем нет.

Код программы следующий:
db_init.py:
import asyncio
from gino import Gino
from config import CONFIGURATION

db = Gino() #Создание ссылки на базу данных
loop = asyncio.get_event_loop() #Достаём event loop

async def main():
    async with db.with_bind('postgresql://localhost/postgres') as engine:
        await db.gino.create_all()

loop.run_until_complete(main()) #Запуск ORM Gino - помещаем экземпляр Gino в event loop


model.py
from db_init import db

class User(db.Model):
    __tablename__ = 'users'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String)
    fullname = db.Column(db.String)

    def __init__(self, *args, **kwargs):
    	super(User, self).__init__(*args, **kwargs)


view.py
from aiohttp import web
from model import User
from db_init import db
import asyncio #Unnecessary

routes = web.RouteTableDef()

@routes.get('/')
async def index(request):
	async with db.with_bind('postgresql://localhost/postgres') as engine:
		# await User.delete.gino.all() #Дабы не плодить записи в таблице на этапе разработки
		await asyncio.sleep(1.5) #Для проверки асинхронности обработки подключений
		await User.create(name='jack', fullname='Jack Jones')
		print(await User.query.gino.all())
	with open('app.html') as f:
		return web.Response(text=f.read(), content_type='text/html')


В чём-то похожая ошибка возникла у человека вот здесь, однако у него она появлялась при формировании сложного запроса к базе и среди причин её появления выделяли неправильную настройку движка Gino. Решение заключалось в том, что set_bind устанавливался раньше запуска его бота.
I refractored my code so that set_bind happens before the bot is even initialized. Works like a charm, thanks!!
TitanEmbeds/Titan@de94f87#diff-e9efb373550a65452e49304c8856fe5c

Но я что-то не очень понимаю, почему в мем коде bind устанавливается позднее обращения к базе. Первоначально это должно происходить в файле db_init.py в main(), где создаётся таблица по заданной модели, а затем подключение автоматически закрывается, и открывается снова по необходимости при вызове вьюхи

упрощённая архитектура приложения следующая:

[main.py] <--app--- [app.py] <---routes--- [view.py] <--User, db--- [model.py] <---db---[db_init.py]

стрелочками показаны импорты, db импортируется из db_init.py в model.py и в view.py (не знал как это изобразить)
  • Вопрос задан
  • 1871 просмотр
Решения вопроса 1
@toddbarry Автор вопроса
Проблема была в том, что GinoEngine нужно инициализировать только один раз. А в моём коде выше когда пользователь подключается к вьюхе, каждый раз инициализируется подключение к GinoEngine и каждый раз затем закрывается при использовании инструкции with, по этой причине удавалось провести только одно подключение к базе для одного клиента из двух одновременно подключающихся.

Решением стало изменение кода app.py:
Теперь подключение к движку происходит только здесь
app = web.Application(middlewares=[db]) #Инициализируем приложение
app.add_routes(routes) #Добавление роутов в маршрутизатор
app['config'] = {'dsn': 'postgresql://mac@localhost/mydb'}
db.init_app(app)
app.router.add_static('/static', 'static') #Добавление директории со статическими файлами в маршрутизатор


А обращение к базе данных в view.py теперь следующее
@routes.get('/')
async def index(request):
	await asyncio.sleep(1.5)
	await User.create(name='jack', fullname='Jack Jones')
	print(await User.query.gino.all())
	with open('app.html') as f:
		return web.Response(text=f.read(), content_type='text/html')


При этом вместо from gino import Gino я теперь использую импорт from gino.ext.aiohttp import Gino

Подробнее здесь https://github.com/fantix/gino/issues/295#issuecom...
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

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

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