T
T
toddbarry2018-08-04 06:26:16
Python
toddbarry, 2018-08-04 06:26:16

Where is the error in working with gino?

To check the asynchrony of processing connections, I added to the view.py code - asyncio.sleep (1.5)
If I understand correctly, when two clients connect to the server at the same time, with asynchronous processing, both will wait 1.5 seconds, and with synchronous processing, first one will wait for its 1.5 seconds, and then start waiting for the second.
However, my tests failed with an error thrown:

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'

At the same time, it occurs only when two clients are connected at the same time (I connect from a phone and a laptop). If only one connects, everything works fine. In addition, one of the two clients does not have any errors, while the other server writes: 500 Internal Server Error.
I will add that the server itself (on aiohttp) copes with such things very well, and if you remove the with_bind connection and all work with the database from the view, leaving only the page output, there are no problems.
The program code is the following:
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')

A somewhat similar error occurred to a person here , however, he had it when forming a complex query to the database, and among the reasons for its appearance, the Gino engine was configured incorrectly. The solution was to set_bind before starting his bot.
I refractored my code so that set_bind happens before the bot is even initialized. Works like a charm, thanks!!
TitanEmbeds/[email protected]#diff-e9efb373550a65452e49304c8856fe5c

But I don’t really understand why in the meme code bind is set later than the call to the database. Initially, this should happen in the db_init.py file in main(), where a table is created according to the given model, and then the connection is automatically closed, and reopened as needed when the view is called. The
simplified application architecture is as follows:
[main.py] <--app- -- [app.py] <---routes--- [view.py] <--User, db--- [model.py] <---db---[db_init.py]
arrows show imports , db is imported from db_init.py to model.py and to view.py (didn't know how to depict this)

Answer the question

In order to leave comments, you need to log in

1 answer(s)
T
toddbarry, 2018-08-05
@toddbarry

The problem was that GinoEngine only needs to be initialized once. And in my code above, when the user connects to the view, the connection to the GinoEngine is initialized each time and then each time it is closed using the with statement, for this reason it was possible to make only one connection to the database for one client out of two simultaneously connecting.
The solution was to change the app.py code:
Now the connection to the engine occurs only here

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

And accessing the database in view.py is now the following
@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')

However, instead of from gino import Gino, I now use the import from gino.ext.aiohttp import Gino
More details here https://github.com/fantix/gino/issues/295#issuecom...

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question