Andrew-Chen-Wang / django-async-redis

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

TODO #1

Closed Andrew-Chen-Wang closed 1 year ago

Andrew-Chen-Wang commented 3 years ago

There are still several things remaining before this can be made into a stable package:

I'm going to need help with this, so please do consider making some PRs. I've got midterms for the next two weeks, so lots of GOOD (not shitoberfest quality) contributions would be helpful!

Cheers, Andrew

Andrew-Chen-Wang commented 3 years ago

For the big issue, the solution is to do it the semi-old fashioned way, i.e. intermixing __enter__ and __exit__ magic methods with the old way of try...finally:

    async def touch(
        self, key, timeout=DEFAULT_TIMEOUT, version=None, client: Redis = None
    ):
        """
        Sets a new expiration for a key.
        """

        if timeout is DEFAULT_TIMEOUT:
            timeout = self._backend.default_timeout

        existent = False
        if hasattr(client, "__enter__") and hasattr(client, "__exit__"):
            existent = True
            c = client
        elif client is None:
            client = await self.get_client(write=True)
            c = (await client).__enter__()
        else:
            raise NotImplementedError("You must implement __enter__ and __exit__ for your client.")

        try:
            key = self.make_key(key, version=version)
            if timeout is None:
                return bool(await c.persist(key))
            else:
                # Convert to milliseconds
                timeout = int(timeout * 1000)
                return bool(await c.pexpire(key, timeout))
        finally:
            if not existent:
                c.__exit__()

Unfortunately, this may not be foolproof. I'm seeing some edge cases which might require me to use pytest parametrization to actually see that all combos are working as expected (i.e. only one connection is actually opened).

Andrew-Chen-Wang commented 3 years ago

Before I go on a week long hiatus, a different solution is to have all calls to redis in a private function:

async def _set(client: ContextRedis, *args, **kwargs):
    await c.set(*args, **kwargs)

async def set(*args, **kwargs):
     with await client as c:
         await _set(client: c, *args, **kwargs)
Andrew-Chen-Wang commented 1 year ago

closing issue as we're now using redis-py