igorcoding / asynctnt

A fast Tarantool Database connector for Python/asyncio.

Home Page:https://igorcoding.github.io/asynctnt

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Session push hangs on reconnect

oleynikandrey opened this issue · comments

After asynctnt reconnected to Tarantool, PushIterator is blocked forever on waiting asyncio.Event.

The only solution I've come up with is constantly check id of connection._transport and once it's changed recreate PushIterator, eg:

init.lua

box.cfg{listen=3301}
push = require('push')

push.lua

local fiber = require('fiber')

local x = 0

local listen = function()
    while true do
        fiber.sleep(2)
        x = x + 1
        box.session.push(x)
    end
end

return {
    listen = listen
}

asynctnt client

import asyncio
import asynctnt


def get_push_iterator(connection):
    fut = connection.call("push.listen", push_subscribe=True)
    return asynctnt.PushIterator(fut)


async def main():
    async with asynctnt.Connection(port=3301) as conn:

        it = get_push_iterator(conn)
        transport_id = id(conn._transport)

        while True:
            if (current_transport_id := id(conn._transport)) != transport_id:
                transport_id = current_transport_id
                it = get_push_iterator(conn)

            try:
                result = await asyncio.wait_for(it.__anext__(), timeout=10)
            except asyncio.exceptions.TimeoutError:
                pass


if __name__ == "__main__":
    asyncio.run(main())

Any thoughts on this?

Hi!
I understand the issue, I think I got the fix in the branch infinite_push_bug. So it'll be great if you would try it for yourself (just install from the branch).
And btw I think I want to maybe rework the iterator thing. So, If you have any suggestions - I will be happy to listen.

Forgot to mention: I decided to throw an exception if this situation occurs, hope that is ok, because I think it's the most viable thing.

I've tested and it's work as expected. With an error thrown on connection lost I can handle the error and resubscribe to pushes.

What about moving PushIterator logic to Response class and creating wrapper in Connection which will return async iterator? It will looks like that:

async with asynctnt.Connection(port=3301) as conn:
    async for msg in conn.subscribe("infinite_push_loop"):
        print(msg)

So that there is no need to create iterator from a result of call method.

I'll release soon v1.2 with the fix.
About your suggestions. I like the idea to merge Response object with PusIterator functionality but it requires some refactoring which is a good thing. But making a .subscribe() method is incorrect because in theory any tarantool request may yield stuff back to the client (right now there are call and eval and maybe in future it will be select and others), so it is impractical to make this method.
I guess it'll be fine if every method would return not a plain Future object, which results into a Response object, but a Response itself, which you can await on and also iterate using __aiter__ and __anext__.