Andrew-Chen-Wang / django-async-redis

Full featured async Redis cache backend for Django.
Other
22 stars 4 forks source link

Error when trying to use async redis cache half of the time #7

Open gschurck opened 10 months ago

gschurck commented 10 months ago

Description

I try to use async redis cache in Django (synchronous is working fine)

async def search(request: HtmxHttpRequest) -> HttpResponse:
    print("new search -------------")
    query = request.POST.get('search')
    print(query)
    if not query:
        return render(request, "components/memes-results.html",
                      {"memes_results": [], "starred_memes_ids": []})
    # memes_results = trigram_similarity_search(query)
    memes_results = await get_memes_search_results(query)
    user = request.user

    if user.is_authenticated:
        starred_memes_ids = [meme_id async for meme_id in user.profile.starred_memes.all().values_list('id', flat=True)]
    else:
        starred_memes_ids = []
    # print(memes_results)
    # print(starred_memes_ids)
    if len(memes_results) == 0:
        return render(request, "components/no-results.html")
    return render(request, "components/memes-results.html",
                  {"memes_results": memes_results, "starred_memes_ids": starred_memes_ids})

async def get_memes_search_results(query):
    text_embedding_task = get_text_embedding_async(query)
    full_text_search_results = await process_memes_fulltext_search_async(query)
    print("Full text search results:")
    for result in full_text_search_results:
        print(result.title, result.rank)
    exclude_ids = [result.id for result in full_text_search_results]
    text_embedding = await text_embedding_task
    results = await process_embedding_search_async(text_embedding, exclude_ids)
    # exclude full text search results
    print("Embedding search results:")
    # print results with distance
    for result in results:
        print(result.title, result.distance)
    combined_results = list(full_text_search_results) + results
    return combined_results

async def get_text_embedding_async(text) -> list[float]:
    cached_text_embedding = await caches['async_redis'].aget(text)
    if cached_text_embedding is not None:
        return cached_text_embedding
    text_embedding = (await call_gradio_api_async([text], 'predict_1'))['data'][0]['embedding']
    await caches['async_redis'].aset(text, text_embedding, timeout=None)
    return text_embedding

What I Did

The command manage.py runserver 8000

And when requesting the view, it only works literally half the time. Like working, then error, then working, then error, etc... Not sure if the error comes from the lib or my async code. Here is the traceback for the error RuntimeError: Event loop is closed:

Internal Server Error: /search/
Traceback (most recent call last):
  File "/home/guillaume/.cache/pypoetry/virtualenvs/ttm-django-YIBsC6me-py3.11/lib/python3.11/site-packages/redis/asyncio/connection.py", line 782, in read_response
    response = await self._parser.read_response(
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/guillaume/.cache/pypoetry/virtualenvs/ttm-django-YIBsC6me-py3.11/lib/python3.11/site-packages/redis/asyncio/connection.py", line 262, in read_response
    response = await self._read_response(disable_decoding=disable_decoding)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/guillaume/.cache/pypoetry/virtualenvs/ttm-django-YIBsC6me-py3.11/lib/python3.11/site-packages/redis/asyncio/connection.py", line 270, in _read_response
    raw = await self._readline()
          ^^^^^^^^^^^^^^^^^^^^^^
  File "/home/guillaume/.cache/pypoetry/virtualenvs/ttm-django-YIBsC6me-py3.11/lib/python3.11/site-packages/redis/asyncio/connection.py", line 344, in _readline
    data = await self._stream.readline()
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/asyncio/streams.py", line 549, in readline
    line = await self.readuntil(sep)
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/asyncio/streams.py", line 641, in readuntil
    await self._wait_for_data('readuntil')
  File "/usr/lib/python3.11/asyncio/streams.py", line 526, in _wait_for_data
    await self._waiter
RuntimeError: Task <Task pending name='Task-4' coro=<AsyncToSync.main_wrap() running at /home/guillaume/.cache/pypoetry/virtualenvs/ttm-django-YIBsC6me-py3.11/lib/python3.11/site-packages/asgiref/sync.py:353> cb=[_run_until_complete_cb() at /usr/lib/python3.11/asyncio/base_events.py:180]> got Future <Future pending> attached to a different loop

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/guillaume/.cache/pypoetry/virtualenvs/ttm-django-YIBsC6me-py3.11/lib/python3.11/site-packages/django/core/handlers/exception.py", line 55, in inner
    response = get_response(request)
               ^^^^^^^^^^^^^^^^^^^^^
  File "/home/guillaume/.cache/pypoetry/virtualenvs/ttm-django-YIBsC6me-py3.11/lib/python3.11/site-packages/django/core/handlers/base.py", line 197, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/guillaume/.cache/pypoetry/virtualenvs/ttm-django-YIBsC6me-py3.11/lib/python3.11/site-packages/asgiref/sync.py", line 277, in __call__
    return call_result.result()
           ^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/concurrent/futures/_base.py", line 449, in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/concurrent/futures/_base.py", line 401, in __get_result
    raise self._exception
  File "/home/guillaume/.cache/pypoetry/virtualenvs/ttm-django-YIBsC6me-py3.11/lib/python3.11/site-packages/asgiref/sync.py", line 353, in main_wrap
    result = await self.awaitable(*args, **kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/guillaume/Personal Projects/ttm-django/trouve_ton_meme/views.py", line 221, in search
    memes_results = await get_memes_search_results(query)
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/guillaume/Personal Projects/ttm-django/trouve_ton_meme/views.py", line 278, in get_memes_search_results
    text_embedding = await text_embedding_task
                     ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/guillaume/Personal Projects/ttm-django/trouve_ton_meme/embeddings.py", line 50, in get_text_embedding_async
    cached_text_embedding = await caches['async_redis'].aget(text)
                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/guillaume/.cache/pypoetry/virtualenvs/ttm-django-YIBsC6me-py3.11/lib/python3.11/site-packages/django_async_redis/cache.py", line 96, in aget
    value = await self._aget(key, default, version, client)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/guillaume/.cache/pypoetry/virtualenvs/ttm-django-YIBsC6me-py3.11/lib/python3.11/site-packages/django_async_redis/cache.py", line 103, in _aget
    return await self.client.get(
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/home/guillaume/.cache/pypoetry/virtualenvs/ttm-django-YIBsC6me-py3.11/lib/python3.11/site-packages/django_async_redis/client/default.py", line 260, in get
    value = await client.get(key)
            ^^^^^^^^^^^^^^^^^^^^^
  File "/home/guillaume/.cache/pypoetry/virtualenvs/ttm-django-YIBsC6me-py3.11/lib/python3.11/site-packages/redis/asyncio/client.py", line 518, in execute_command
    return await conn.retry.call_with_retry(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/guillaume/.cache/pypoetry/virtualenvs/ttm-django-YIBsC6me-py3.11/lib/python3.11/site-packages/redis/asyncio/retry.py", line 59, in call_with_retry
    return await do()
           ^^^^^^^^^^
  File "/home/guillaume/.cache/pypoetry/virtualenvs/ttm-django-YIBsC6me-py3.11/lib/python3.11/site-packages/redis/asyncio/client.py", line 492, in _send_command_parse_response
    return await self.parse_response(conn, command_name, **options)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/guillaume/.cache/pypoetry/virtualenvs/ttm-django-YIBsC6me-py3.11/lib/python3.11/site-packages/redis/asyncio/client.py", line 539, in parse_response
    response = await connection.read_response()
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/guillaume/.cache/pypoetry/virtualenvs/ttm-django-YIBsC6me-py3.11/lib/python3.11/site-packages/redis/asyncio/connection.py", line 802, in read_response
    await self.disconnect(nowait=True)
  File "/home/guillaume/.cache/pypoetry/virtualenvs/ttm-django-YIBsC6me-py3.11/lib/python3.11/site-packages/redis/asyncio/connection.py", line 672, in disconnect
    self._writer.close()  # type: ignore[union-attr]
    ^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/asyncio/streams.py", line 344, in close
    return self._transport.close()
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/asyncio/selector_events.py", line 860, in close
    self._loop.call_soon(self._call_connection_lost, None)
  File "/usr/lib/python3.11/asyncio/base_events.py", line 761, in call_soon
    self._check_closed()
  File "/usr/lib/python3.11/asyncio/base_events.py", line 519, in _check_closed
    raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed
anmv commented 9 months ago

I have a similar problem :(

rob4226 commented 1 month ago

You should try running Django using an actual ASGI server such as uvicorn like:

pip install uvicorn
python -m uvicorn my_django_project.asgi:application

Not sure if it will fix your problem but per the Django docs:

Serving with ASGI in development Django’s runserver command provides a WSGI server. In order to run under ASGI you will need to use an ASGI server. The Django Daphne project provides Integration with runserver that you can use.

It could be the development server is messing up the asyncio event loop.