Для проверки асинхронности обработки подключений, внёс в код 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 (не знал как это изобразить)