cunla / fakeredis-py

Implementation of Redis in python without having a Redis server running. Fully compatible with using redis-py.
https://fakeredis.moransoftware.ca/
BSD 3-Clause "New" or "Revised" License
281 stars 47 forks source link

Mismatch in outputs of blocking XREAD in Fake vs Real redis libraries #308

Closed amitgargOfficial closed 4 months ago

amitgargOfficial commented 4 months ago

I am using pyredis==5.0.3 and fakeredis=2.22.0 in Python3.8.10, and the output of XREAD from the fake library is different from the one of a real Redis client. Specifically, the faker library always returns an empty array when using a blocking call against XREAD, unless count is also set. I have tried using 5.0.7 and 7.2.4 for my redis server, and gotten the same results.

Here is a small demo to reproduce:

import redis
from fakeredis import FakeServer, FakeStrictRedis

def main():
    fake_server = FakeServer()
    fake_client = FakeStrictRedis(server=fake_server)

    k = "key"
    fake_client.xadd(k, {"value": 1234})
    streams = {k: "0"}

    print(f"FAKE XRANGE - {fake_client.xrange(k)}")
    print(f"FAKE BLOCKING XREAD - {fake_client.xread(streams=streams, block=10)}")
    print(f"FAKE NON BLOCKING XREAD - {fake_client.xread(streams=streams)}")
    print(f"FAKE BLOCK COUNT XREAD - {fake_client.xread(streams=streams, block=10, count=10)}")

    real_client = redis.Redis(host='localhost', port=6379, db=1)
    real_client.xadd(k, {"value": 1234})

    print()

    print(f"REAL XRANGE - {real_client.xrange(k)}")
    print(f"REAL BLOCKING XREAD - {real_client.xread(streams=streams, block=10)}")
    print(f"REAL NON BLOCKING XREAD - {real_client.xread(streams=streams)}")
    print(f"REAL BLOCK COUNT XREAD - {real_client.xread(streams=streams, block=10, count=10)}")

    real_client.delete(k)

if __name__ == '__main__':
    main()

Which yields the output:

FAKE XRANGE - [(b'1715212536143-0', {b'value': b'1234'})]
FAKE BLOCKING XREAD - []
FAKE NON BLOCKING XREAD - [[b'key', [(b'1715212536143-0', {b'value': b'1234'})]]]
FAKE BLOCK COUNT XREAD - [[b'key', [(b'1715212536143-0', {b'value': b'1234'})]]]

REAL XRANGE - [(b'1715212536166-0', {b'value': b'1234'})]
REAL BLOCKING XREAD - [[b'key', [(b'1715212536166-0', {b'value': b'1234'})]]]
REAL NON BLOCKING XREAD - [[b'key', [(b'1715212536166-0', {b'value': b'1234'})]]]
REAL BLOCK COUNT XREAD - [[b'key', [(b'1715212536166-0', {b'value': b'1234'})]]]

Looking in the code, a None count parameter of the XREAD is preventing the _xread method from returning the relevant items.

    def _xread(...):
            ...
            stream_results = self._xrange(item.value, start_id, max_inf, False, count)
            if first_pass and (count is None):
                return None
            ...

Is this by design, or a bug? If it's by design, could you share more about the reason why this specific behavior is different from the one in real redis?

Upvote & Fund

Fund with Polar

cunla commented 4 months ago

Thanks, fixed it. It will be published in the next release