Redis Streams

Introduction to Redis streams

Stream command summary (view reference, 28 commands)

A Redis stream is a data structure that acts like an append-only log but also implements several operations to overcome some of the limits of a typical append-only log. These include random access in O(1) time and complex consumption strategies, such as consumer groups. You can use streams to record and simultaneously syndicate events in real time. Examples of Redis stream use cases include:

  • Event sourcing (e.g., tracking user actions, clicks, etc.)
  • Sensor monitoring (e.g., readings from devices in the field)
  • Notifications (e.g., storing a record of each user's notifications in a separate stream)

Redis generates a unique ID for each stream entry. You can use these IDs to retrieve their associated entries later or to read and process all subsequent entries in the stream. Note that because these IDs are related to time, the ones shown here may vary and will be different from the IDs you see in your own Redis instance.

Redis streams support several trimming strategies (to prevent streams from growing unbounded) and more than one consumption strategy (see XREAD, XREADGROUP, and XRANGE). Starting with Redis 8.2, the XACKDEL, XDELEX, XADD, and XTRIM commands provide fine-grained control over how stream operations interact with multiple consumer groups, simplifying the coordination of message processing across different applications.

Beginning with Redis 8.6, Redis streams support idempotent message processing (at-most-once production) to prevent duplicate entries when using at-least-once delivery patterns. This feature enables reliable message submission with automatic deduplication. See Idempotent Message Processing for more information.

Examples

  • When our racers pass a checkpoint, we add a stream entry for each racer that includes the racer's name, speed, position, and location ID:

    Foundational: Add entries to a stream using XADD with auto-generated IDs (creates new entries with field-value pairs)
    > XADD race:france * rider Castilla speed 30.2 position 1 location_id 1
    "1692632086370-0"
    > XADD race:france * rider Norem speed 28.8 position 3 location_id 1
    "1692632094485-0"
    > XADD race:france * rider Prickett speed 29.7 position 2 location_id 1
    "1692632102976-0"
    """
    Code samples for Stream doc pages:
        https://redis.io/docs/latest/develop/data-types/streams/
    """
    
    import redis
    
    r = redis.Redis(decode_responses=True)
    
    res1 = r.xadd(
        "race:france",
        {"rider": "Castilla", "speed": 30.2, "position": 1, "location_id": 1},
    )
    print(res1)  # >>> 1692629576966-0
    
    res2 = r.xadd(
        "race:france",
        {"rider": "Norem", "speed": 28.8, "position": 3, "location_id": 1},
    )
    print(res2)  # >>> 1692629594113-0
    
    res3 = r.xadd(
        "race:france",
        {"rider": "Prickett", "speed": 29.7, "position": 2, "location_id": 1},
    )
    print(res3)  # >>> 1692629613374-0
    
    
    res4 = r.xrange("race:france", "1691765278160-0", "+", 2)
    print(
        res4
    )  # >>> [
    #   ('1692629576966-0',
    #       {'rider': 'Castilla', 'speed': '30.2', 'position': '1', 'location_id': '1'}
    #   ),
    #   ('1692629594113-0',
    #       {'rider': 'Norem', 'speed': '28.8', 'position': '3', 'location_id': '1'}
    #   )
    # ]
    
    res5 = r.xread(streams={"race:france": 0}, count=100, block=300)
    print(
        res5
    )
    # >>> [
    #   ['race:france',
    #       [('1692629576966-0',
    #           {'rider': 'Castilla', 'speed': '30.2', 'position': '1', 'location_id': '1'}
    #       ),
    #       ('1692629594113-0',
    #           {'rider': 'Norem', 'speed': '28.8', 'position': '3', 'location_id': '1'}
    #       ),
    #       ('1692629613374-0',
    #           {'rider': 'Prickett', 'speed': '29.7', 'position': '2', 'location_id': '1'}
    #       )]
    # ]
    # ]
    
    res6 = r.xadd(
        "race:france",
        {"rider": "Castilla", "speed": 29.9, "position": 1, "location_id": 2},
    )
    print(res6)  # >>> 1692629676124-0
    
    res7 = r.xlen("race:france")
    print(res7)  # >>> 4
    
    
    res8 = r.xadd("race:usa", {"racer": "Castilla"}, id="0-1")
    print(res8)  # >>> 0-1
    
    res9 = r.xadd("race:usa", {"racer": "Norem"}, id="0-2")
    print(res9)  # >>> 0-2
    
    try:
        res10 = r.xadd("race:usa", {"racer": "Prickett"}, id="0-1")
        print(res10)  # >>> 0-1
    except redis.exceptions.ResponseError as e:
        print(e)  # >>> WRONGID
    
    # Not yet implemented
    
    res11 = r.xrange("race:france", "-", "+")
    print(
        res11
    )
    # >>> [
    #   ('1692629576966-0',
    #       {'rider': 'Castilla', 'speed': '30.2', 'position': '1', 'location_id': '1'}
    #   ),
    #   ('1692629594113-0',
    #       {'rider': 'Norem', 'speed': '28.8', 'position': '3', 'location_id': '1'}
    #   ),
    #   ('1692629613374-0',
    #       {'rider': 'Prickett', 'speed': '29.7', 'position': '2', 'location_id': '1'}
    #   ),
    #   ('1692629676124-0',
    #       {'rider': 'Castilla', 'speed': '29.9', 'position': '1', 'location_id': '2'}
    #   )
    # ]
    
    res12 = r.xrange("race:france", 1692629576965, 1692629576967)
    print(
        res12
    )
    # >>> [
    #       ('1692629576966-0',
    #           {'rider': 'Castilla', 'speed': '30.2', 'position': '1', 'location_id': '1'}
    #       )
    # ]
    
    res13 = r.xrange("race:france", "-", "+", 2)
    print(
        res13
    )
    # >>> [
    #   ('1692629576966-0',
    #       {'rider': 'Castilla', 'speed': '30.2', 'position': '1', 'location_id': '1'}
    #   ),
    #   ('1692629594113-0',
    #       {'rider': 'Norem', 'speed': '28.8', 'position': '3', 'location_id': '1'}
    #   )
    # ]
    
    res14 = r.xrange("race:france", "(1692629594113-0", "+", 2)
    print(
        res14
    )
    # >>> [
    #   ('1692629613374-0',
    #       {'rider': 'Prickett', 'speed': '29.7', 'position': '2', 'location_id': '1'}
    #   ),
    #   ('1692629676124-0',
    #       {'rider': 'Castilla', 'speed': '29.9', 'position': '1', 'location_id': '2'}
    #   )
    # ]
    
    res15 = r.xrange("race:france", "(1692629676124-0", "+", 2)
    print(res15)  # >>> []
    
    res16 = r.xrevrange("race:france", "+", "-", 1)
    print(
        res16
    )
    # >>> [
    #       ('1692629676124-0',
    #           {'rider': 'Castilla', 'speed': '29.9', 'position': '1', 'location_id': '2'}
    #       )
    # ]
    
    res17 = r.xread(streams={"race:france": 0}, count=2)
    print(
        res17
    )
    # >>> [
    #       ['race:france', [
    #       ('1692629576966-0',
    #           {'rider': 'Castilla', 'speed': '30.2', 'position': '1', 'location_id': '1'}
    #       ),
    #       ('1692629594113-0',
    #           {'rider': 'Norem', 'speed': '28.8', 'position': '3', 'location_id': '1'}
    #       )
    #       ]
    #       ]
    #   ]
    
    res18 = r.xgroup_create("race:france", "france_riders", "$")
    print(res18)  # >>> True
    
    res19 = r.xgroup_create("race:italy", "italy_riders", "$", mkstream=True)
    print(res19)  # >>> True
    
    r.xadd("race:italy", {"rider": "Castilla"})
    r.xadd("race:italy", {"rider": "Royce"})
    r.xadd("race:italy", {"rider": "Sam-Bodden"})
    r.xadd("race:italy", {"rider": "Prickett"})
    r.xadd("race:italy", {"rider": "Norem"})
    
    res20 = r.xreadgroup(
        streams={"race:italy": ">"},
        consumername="Alice",
        groupname="italy_riders",
        count=1,
    )
    print(res20)  # >>> [['race:italy', [('1692629925771-0', {'rider': 'Castilla'})]]]
    
    res21 = r.xreadgroup(
        streams={"race:italy": 0},
        consumername="Alice",
        groupname="italy_riders",
        count=1,
    )
    print(res21)  # >>> [['race:italy', [('1692629925771-0', {'rider': 'Castilla'})]]]
    
    res22 = r.xack("race:italy", "italy_riders", "1692629925771-0")
    print(res22)  # >>> 1
    
    res23 = r.xreadgroup(
        streams={"race:italy": 0},
        consumername="Alice",
        groupname="italy_riders",
        count=1,
    )
    print(res23)  # >>> [['race:italy', []]]
    
    res24 = r.xreadgroup(
        streams={"race:italy": ">"},
        consumername="Bob",
        groupname="italy_riders",
        count=2,
    )
    print(
        res24
    )
    # >>> [
    #       ['race:italy', [
    #           ('1692629925789-0',
    #               {'rider': 'Royce'}
    #           ),
    #           ('1692629925790-0',
    #               {'rider': 'Sam-Bodden'}
    #           )
    #       ]
    #       ]
    # ]
    
    res25 = r.xpending("race:italy", "italy_riders")
    print(
        res25
    )
    # >>> {
    #       'pending': 2, 'min': '1692629925789-0', 'max': '1692629925790-0',
    #       'consumers': [{'name': 'Bob', 'pending': 2}]
    # }
    
    res26 = r.xpending_range("race:italy", "italy_riders", "-", "+", 10)
    print(
        res26
    )
    # >>> [
    #       {
    #           'message_id': '1692629925789-0', 'consumer': 'Bob',
    #           'time_since_delivered': 31084, 'times_delivered': 1
    #       },
    #       {
    #           'message_id': '1692629925790-0', 'consumer': 'Bob',
    #           'time_since_delivered': 31084, 'times_delivered': 1
    #       }
    # ]
    
    res27 = r.xrange("race:italy", "1692629925789-0", "1692629925789-0")
    print(res27)  # >>> [('1692629925789-0', {'rider': 'Royce'})]
    
    res28 = r.xclaim("race:italy", "italy_riders", "Alice", 60000, ["1692629925789-0"])
    print(res28)  # >>> [('1692629925789-0', {'rider': 'Royce'})]
    
    res29 = r.xautoclaim("race:italy", "italy_riders", "Alice", 1, "0-0", 1)
    print(res29)  # >>> ['1692629925790-0', [('1692629925789-0', {'rider': 'Royce'})]]
    
    res30 = r.xautoclaim("race:italy", "italy_riders", "Alice", 1, "(1692629925789-0", 1)
    print(res30)  # >>> ['0-0', [('1692629925790-0', {'rider': 'Sam-Bodden'})]]
    
    res31 = r.xinfo_stream("race:italy")
    print(
        res31
    )
    # >>> {
    #       'length': 5, 'radix-tree-keys': 1, 'radix-tree-nodes': 2,
    #       'last-generated-id': '1692629926436-0', 'groups': 1,
    #       'first-entry': ('1692629925771-0', {'rider': 'Castilla'}),
    #       'last-entry': ('1692629926436-0', {'rider': 'Norem'})
    # }
    
    res32 = r.xinfo_groups("race:italy")
    print(
        res32
    )
    # >>> [
    #       {
    #           'name': 'italy_riders', 'consumers': 2, 'pending': 2,
    #           'last-delivered-id': '1692629925790-0'
    #       }
    # ]
    
    res33 = r.xinfo_consumers("race:italy", "italy_riders")
    print(
        res33
    )
    # >>> [
    #       {'name': 'Alice', 'pending': 2, 'idle': 199332},
    #       {'name': 'Bob', 'pending': 0, 'idle': 489170}
    # ]
    
    r.xadd("race:italy", {"rider": "Jones"}, maxlen=2)
    r.xadd("race:italy", {"rider": "Wood"}, maxlen=2)
    r.xadd("race:italy", {"rider": "Henshaw"}, maxlen=2)
    
    res34 = r.xlen("race:italy")
    print(res34)  # >>> 8
    
    res35 = r.xrange("race:italy", "-", "+")
    print(
        res35
    )
    # >>> [
    #       ('1692629925771-0', {'rider': 'Castilla'}),
    #       ('1692629925789-0', {'rider': 'Royce'}),
    #       ('1692629925790-0', {'rider': 'Sam-Bodden'}),
    #       ('1692629925791-0', {'rider': 'Prickett'}),
    #       ('1692629926436-0', {'rider': 'Norem'}),
    #       ('1692630612602-0', {'rider': 'Jones'}),
    #       ('1692630641947-0', {'rider': 'Wood'}),
    #       ('1692630648281-0', {'rider': 'Henshaw'})
    # ]
    
    r.xadd("race:italy", {"rider": "Smith"}, maxlen=2, approximate=False)
    
    res36 = r.xrange("race:italy", "-", "+")
    print(
        res36
    )
    # >>> [
    #       ('1692630648281-0', {'rider': 'Henshaw'}),
    #       ('1692631018238-0', {'rider': 'Smith'})
    # ]
    
    res37 = r.xtrim("race:italy", maxlen=10, approximate=False)
    print(res37)  # >>> 0
    
    res38 = r.xtrim("race:italy", maxlen=10)
    print(res38)  # >>> 0
    
    res39 = r.xrange("race:italy", "-", "+")
    print(
        res39
    )
    # >>> [
    #       ('1692630648281-0', {'rider': 'Henshaw'}),
    #       ('1692631018238-0', {'rider': 'Smith'})
    # ]
    
    res40 = r.xdel("race:italy", "1692631018238-0")
    print(res40)  # >>> 1
    
    res41 = r.xrange("race:italy", "-", "+")
    print(res41)  # >>> [('1692630648281-0', {'rider': 'Henshaw'})]
    
    import assert from 'assert';
    import {
      createClient
    } from 'redis';
    
    const client = await createClient();
    await client.connect();
    
    const res1 = await client.xAdd(
      'race:france', '*', {
        'rider': 'Castilla',
        'speed': '30.2',
        'position': '1',
        'location_id': '1'
      }
    );
    console.log(res1); // >>> 1700073067968-0 N.B. actual values will differ from these examples
    
    const res2 = await client.xAdd(
      'race:france', '*', {
        'rider': 'Norem',
        'speed': '28.8',
        'position': '3',
        'location_id': '1'
      },
    );
    console.log(res2); // >>> 1692629594113-0
    
    const res3 = await client.xAdd(
      'race:france', '*', {
        'rider': 'Prickett',
        'speed': '29.7',
        'position': '2',
        'location_id': '1'
      },
    );
    console.log(res3); // >>> 1692629613374-0
    
    
    const res4 = await client.xRange('race:france', '1691765278160-0', '+', {COUNT: 2});
    console.log(res4); // >>> [{ id: '1692629576966-0', message: { rider: 'Castilla', speed: '30.2', position: '1', location_id: '1' } }, { id: '1692629594113-0', message: { rider: 'Norem', speed: '28.8', position: '3', location_id: '1' } }]
    
    const res5 = await client.xRead({
      key: 'race:france',
      id: '0-0'
    }, {
      COUNT: 100,
      BLOCK: 300
    });
    console.log(res5); // >>> [{ name: 'race:france', messages: [{ id: '1692629576966-0', message: { rider: 'Castilla', speed: '30.2', position: '1', location_id: '1' } }, { id: '1692629594113-0', message: { rider: 'Norem', speed: '28.8', position: '3', location_id: '1' } }, { id: '1692629613374-0', message: { rider: 'Prickett', speed: '29.7', position: '2', location_id: '1' } }] }]
    
    const res6 = await client.xAdd(
      'race:france', '*', {
        'rider': 'Castilla',
        'speed': '29.9',
        'position': '1',
        'location_id': '2'
      }
    );
    console.log(res6); // >>> 1692629676124-0
    
    const res7 = await client.xLen('race:france');
    console.log(res7); // >>> 4
    
    
    const res8 = await client.xAdd('race:usa', '0-1', {
      'racer': 'Castilla'
    });
    console.log(res8); // >>> 0-1
    
    const res9 = await client.xAdd('race:usa', '0-2', {
      'racer': 'Norem'
    });
    console.log(res9); // >>> 0-2
    
    try {
      const res10 = await client.xAdd('race:usa', '0-1', {
        'racer': 'Prickett'
      });
      console.log(res10); // >>> 0-1
    } catch (error) {
      console.error(error); // >>> [SimpleError: ERR The ID specified in XADD is equal or smaller than the target stream top item]
    }
    
    const res11a = await client.xAdd('race:usa', '0-*', { racer: 'Norem' });
    console.log(res11a); // >>> 0-3
    
    const res11 = await client.xRange('race:france', '-', '+');
    console.log(res11); // >>> [{ id: '1692629576966-0', message: { rider: 'Castilla', speed: '30.2', position: '1', location_id: '1' } }, { id: '1692629594113-0', message: { rider: 'Norem', speed: '28.8', position: '3', location_id: '1' } }, { id: '1692629613374-0', message: { rider: 'Prickett', speed: '29.7', position: '2', location_id: '1' } }, { id: '1692629676124-0', message: { rider: 'Castilla', speed: '29.9', position: '1', location_id: '2' } }]
    
    const res12 = await client.xRange('race:france', '1692629576965', '1692629576967');
    console.log(res12); // >>> [{ id: '1692629576966-0', message: { rider: 'Castilla', speed: '30.2', position: '1', location_id: '1' } }]
    
    const res13 = await client.xRange('race:france', '-', '+', {COUNT: 2});
    console.log(res13); // >>> [{ id: '1692629576966-0', message: { rider: 'Castilla', speed: '30.2', position: '1', location_id: '1' } }, { id: '1692629594113-0', message: { rider: 'Norem', speed: '28.8', position: '3', location_id: '1' } }]
    
    const res14 = await client.xRange('race:france', '(1692629594113-0', '+', {COUNT: 2});
    console.log(res14); // >>> [{ id: '1692629613374-0', message: { rider: 'Prickett', speed: '29.7', position: '2', location_id: '1' } }, { id: '1692629676124-0', message: { rider: 'Castilla', speed: '29.9', position: '1', location_id: '2' } }]
    
    const res15 = await client.xRange('race:france', '(1692629676124-0', '+', {COUNT: 2});
    console.log(res15); // >>> []
    
    const res16 = await client.xRevRange('race:france', '+', '-', {COUNT: 1});
    console.log(
      res16
    ); // >>> [{ id: '1692629676124-0', message: { rider: 'Castilla', speed: '29.9', position: '1', location_id: '2' } }]
    
    const res17 = await client.xRead({
      key: 'race:france',
      id: '0-0'
    }, {
      COUNT: 2
    });
    console.log(res17); // >>> [{ name: 'race:france', messages: [{ id: '1692629576966-0', message: { rider: 'Castilla', speed: '30.2', position: '1', location_id: '1' } }, { id: '1692629594113-0', message: { rider: 'Norem', speed: '28.8', position: '3', location_id: '1' } }] }]
    
    const res18 = await client.xGroupCreate('race:france', 'france_riders', '$');
    console.log(res18); // >>> OK
    
    const res19 = await client.xGroupCreate('race:italy', 'italy_riders', '$', {
      MKSTREAM: true
    });
    console.log(res19); // >>> OK
    
    await client.xAdd('race:italy', '*', {
      'rider': 'Castilla'
    });
    await client.xAdd('race:italy', '*', {
      'rider': 'Royce'
    });
    await client.xAdd('race:italy', '*', {
      'rider': 'Sam-Bodden'
    });
    await client.xAdd('race:italy', '*', {
      'rider': 'Prickett'
    });
    await client.xAdd('race:italy', '*', {
      'rider': 'Norem'
    });
    
    const res20 = await client.xReadGroup(
      'italy_riders',
      'Alice', {
        key: 'race:italy',
        id: '>'
      }, {
        COUNT: 1
      }
    );
    console.log(res20); // >>> [{ name: 'race:italy', messages: [{ id: '1692629925771-0', message: { rider: 'Castilla' } }] }]
    
    const res21 = await client.xReadGroup(
      'italy_riders',
      'Alice', {
        key: 'race:italy',
        id: '0'
      }, {
        COUNT: 1
      }
    );
    console.log(res21); // >>> [{ name: 'race:italy', messages: [{ id: '1692629925771-0', message: { rider: 'Castilla' } }] }]
    
    const res22 = await client.xAck('race:italy', 'italy_riders', '1692629925771-0')
    console.log(res22); // >>> 1
    
    const res23 = await client.xReadGroup(
      'italy_riders',
      'Alice', {
        key: 'race:italy',
        id: '0'
      }, {
        COUNT: 1
      }
    );
    console.log(res23); // >>> [{ name: 'race:italy', messages: [] }]
    
    const res24 = await client.xReadGroup(
      'italy_riders',
      'Bob', {
        key: 'race:italy',
        id: '>'
      }, {
        COUNT: 2
      }
    );
    console.log(res24); // >>> [{ name: 'race:italy', messages: [{ id: '1692629925789-0', message: { rider: 'Royce' } }, { id: '1692629925790-0', message: { rider: 'Sam-Bodden' } }] }]
    
    const res25 = await client.xPending('race:italy', 'italy_riders');
    console.log(res25); // >>> {'pending': 2, 'firstId': '1692629925789-0', 'lastId': '1692629925790-0', 'consumers': [{'name': 'Bob', 'deliveriesCounter': 2}]}
    
    const res26 = await client.xPendingRange('race:italy', 'italy_riders', '-', '+', 10);
    console.log(res26); // >>> [{'id': '1692629925789-0', 'consumer': 'Bob', 'millisecondsSinceLastDelivery': 31084, 'deliveriesCounter:': 1}, {'id': '1692629925790-0', 'consumer': 'Bob', 'millisecondsSinceLastDelivery': 31084, 'deliveriesCounter': 1}]
    
    const res27 = await client.xRange('race:italy', '1692629925789-0', '1692629925789-0');
    console.log(res27); // >>> [{ id: '1692629925789-0', message: { rider: 'Royce' } }]
    
    const res28 = await client.xClaim(
      'race:italy', 'italy_riders', 'Alice', 60000, ['1692629925789-0']
    );
    console.log(res28); // >>> [{ id: '1692629925789-0', message: { rider: 'Royce' } }]
    
    const res29 = await client.xAutoClaim('race:italy', 'italy_riders', 'Alice', 1, '0-0', {
      COUNT: 1
    });
    console.log(res29); // >>> { nextId: '1692629925790-0', messages: [{ id: '1692629925789-0', message: { rider: 'Royce' } }], deletedMessages: [] }
    
    const res30 = await client.xAutoClaim(
      'race:italy', 'italy_riders', 'Alice', 1, '(1692629925789-0',
      {
        COUNT: 1
      }
    );
    console.log(res30); // >>> { nextId: '0-0', messages: [{ id: '1692629925790-0', message: { rider: 'Sam-Bodden' } }], deletedMessages: [] }
    
    const res31 = await client.xInfoStream('race:italy');
    console.log(res31); // >>> { length: 5, 'radix-tree-keys': 1, 'radix-tree-nodes': 2, 'last-generated-id': '1692629926436-0', 'max-deleted-entry-id': '0-0', 'entries-added': 5, 'recorded-first-entry-id': '1692629925771-0', groups: 1, 'first-entry': { id: '1692629925771-0', message: { rider: 'Castilla' } }, 'last-entry': { id: '1692629926436-0', message: { rider: 'Norem' } } }
    
    const res32 = await client.xInfoGroups('race:italy');
    console.log(res32); // >>> [{ name: 'italy_riders', consumers: 2, pending: 3, 'last-delivered-id': '1692629925790-0', 'entries-read': 3, lag: 2 }]
    
    const res33 = await client.xInfoConsumers('race:italy', 'italy_riders');
    console.log(res33); // >>> [{ name: 'Alice', pending: 3, idle: 170582, inactive: 170582 }, { name: 'Bob', pending: 0, idle: 489404, inactive: 489404 }]
    
    await client.xAdd('race:italy', '*', {
      'rider': 'Jones'
    }, {
      TRIM: {
        strategy: 'MAXLEN',
        strategyModifier: '~',
        threshold: 2
      }
    });
    await client.xAdd('race:italy', '*', {
      'rider': 'Wood'
    }, {
      TRIM: {
        strategy: 'MAXLEN',
        strategyModifier: '~',
        threshold: 2
      }
    });
    await client.xAdd('race:italy', '*', {
      'rider': 'Henshaw'
    }, {
      TRIM: {
        strategy: 'MAXLEN',
        strategyModifier: '~',
        threshold: 2
      }
    });
    
    const res34 = await client.xLen('race:italy');
    console.log(res34); // >>> 8
    
    const res35 = await client.xRange('race:italy', '-', '+');
    console.log(res35); // >>> [{ id: '1692629925771-0', message: { rider: 'Castilla' } }, { id: '1692629925789-0', message: { rider: 'Royce' } }, { id: '1692629925790-0', message: { rider: 'Sam-Bodden' } }, { id: '1692629925791-0', message: { rider: 'Prickett' } }, { id: '1692629926436-0', message: { rider: 'Norem' } }, { id: '1692630612602-0', message: { rider: 'Jones' } }, { id: '1692630641947-0', message: { rider: 'Wood' } }, { id: '1692630648281-0', message: { rider: 'Henshaw' } }]
    
    await client.xAdd('race:italy', '*', {
      'rider': 'Smith'
    }, {
      TRIM: {
        strategy: 'MAXLEN',
        strategyModifier: '=',
        threshold: 2
      }
    });
    
    const res36 = await client.xRange('race:italy', '-', '+');
    console.log(res36); // >>> [{ id: '1692630648281-0', message: { rider: 'Henshaw' } }, { id: '1692631018238-0', message: { rider: 'Smith' } }]
    
    const res37 = await client.xTrim('race:italy', 'MAXLEN', 10, {
      strategyModifier: '=',
    });
    console.log(res37); // >>> 0
    
    const res38 = await client.xTrim('race:italy', "MAXLEN", 10);
    console.log(res38); // >>> 0
    
    const res39 = await client.xRange('race:italy', '-', '+');
    console.log(res39); // >>> [{ id: '1692630648281-0', message: { rider: 'Henshaw' } }, { id: '1692631018238-0', message: { rider: 'Smith' } }]
    
    const res40 = await client.xDel('race:italy', '1692631018238-0');
    console.log(res40); // >>> 1
    
    const res41 = await client.xRange('race:italy', '-', '+');
    console.log(res41); // >>> [{ id: '1692630648281-0', message: { rider: 'Henshaw' } }]
    
    
    package io.redis.examples;
    
    import redis.clients.jedis.StreamEntryID;
    import redis.clients.jedis.RedisClient;
    
    
    public class StreamsExample {
    
      public void run() {
        RedisClient jedis = RedisClient.create("redis://localhost:6379");
    
    
        StreamEntryID res1 = jedis.xadd("race:france",new HashMap<String,String>(){{put("rider","Castilla");put("speed","30.2");put("position","1");put("location_id","1");}} , XAddParams.xAddParams());
    
        System.out.println(res1); // >>> 1701760582225-0
    
        StreamEntryID res2 = jedis.xadd("race:france",new HashMap<String,String>(){{put("rider","Norem");put("speed","28.8");put("position","3");put("location_id","1");}} , XAddParams.xAddParams());
    
        System.out.println(res2); // >>> 1701760582225-1
    
        StreamEntryID res3 = jedis.xadd("race:france",new HashMap<String,String>(){{put("rider","Prickett");put("speed","29.7");put("position","2");put("location_id","1");}} , XAddParams.xAddParams());
    
        System.out.println(res3); // >>> 1701760582226-0
    
    
        List<StreamEntry> res4 = jedis.xrange("race:france","1701760582225-0","+",2);
    
        System.out.println(res4); // >>> [1701760841292-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701760841292-1 {rider=Norem, speed=28.8, location_id=1, position=3}]
    
        List<Map.Entry<String, List<StreamEntry>>> res5= jedis.xread(XReadParams.xReadParams().block(300).count(100),new HashMap<String,StreamEntryID>(){{put("race:france",new StreamEntryID());}});
        System.out.println(
          res5
        ); // >>> [race:france=[1701761996660-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701761996661-0 {rider=Norem, speed=28.8, location_id=1, position=3}, 1701761996661-1 {rider=Prickett, speed=29.7, location_id=1, position=2}]]
    
        StreamEntryID res6 = jedis.xadd("race:france",new HashMap<String,String>(){{put("rider","Castilla");put("speed","29.9");put("position","2");put("location_id","1");}} , XAddParams.xAddParams());
        System.out.println(res6); // >>> 1701762285679-0
    
        long res7 = jedis.xlen("race:france");
        System.out.println(res7); // >>> 4
    
        StreamEntryID res8 = jedis.xadd("race:usa", new HashMap<String,String>(){{put("racer","Castilla");}},XAddParams.xAddParams().id("0-1"));
        System.out.println(res8); // >>> 0-1
    
        StreamEntryID res9 = jedis.xadd("race:usa", new HashMap<String,String>(){{put("racer","Norem");}},XAddParams.xAddParams().id("0-2"));
        System.out.println(res9); // >>> 0-2
    
        try {
          StreamEntryID res10 = jedis.xadd("race:usa", new HashMap<String,String>(){{put("racer","Prickett");}},XAddParams.xAddParams().id("0-1"));
          System.out.println(res10); // >>> 0-1
        }
        catch (JedisDataException e){
          System.out.println(e); // >>> ERR The ID specified in XADD is equal or smaller than the target stream top item
        }
    
        StreamEntryID res11 = jedis.xadd("race:usa", new HashMap<String,String>(){{put("racer","Norem");}},XAddParams.xAddParams().id("0-*"));
        System.out.println(res11);
    
        List<StreamEntry> res12 = jedis.xrange("race:france","-","+");
        System.out.println(
          res12
        ); // >>> [1701764734160-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701764734160-1 {rider=Norem, speed=28.8, location_id=1, position=3}, 1701764734161-0 {rider=Prickett, speed=29.7, location_id=1, position=2}, 1701764734162-0 {rider=Castilla, speed=29.9, location_id=1, position=2}]
    
        List<StreamEntry> res13 = jedis.xrange("race:france",String.valueOf(System.currentTimeMillis()-1000),String.valueOf(System.currentTimeMillis()+1000));
        System.out.println(
          res13
        ); // >>> [1701764734160-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701764734160-1 {rider=Norem, speed=28.8, location_id=1, position=3}, 1701764734161-0 {rider=Prickett, speed=29.7, location_id=1, position=2}, 1701764734162-0 {rider=Castilla, speed=29.9, location_id=1, position=2}]
    
        List<StreamEntry> res14 = jedis.xrange("race:france","-","+",2);
        System.out.println(res14); // >>> [1701764887638-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701764887638-1 {rider=Norem, speed=28.8, location_id=1, position=3}]
    
        List<StreamEntry> res15 = jedis.xrange("race:france",String.valueOf(System.currentTimeMillis()-1000)+"-0","+",2);
        System.out.println(res15); // >>> [1701764887638-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701764887638-1 {rider=Norem, speed=28.8, location_id=1, position=3}]
    
        List<StreamEntry> res16 = jedis.xrange("race:france",String.valueOf(System.currentTimeMillis()+1000)+"-0","+",2);
        System.out.println(res16); // >>> []
    
        List<StreamEntry> res17 = jedis.xrevrange("race:france","+","-",1);
        System.out.println(res17); // >>> [1701765218592-0 {rider=Castilla, speed=29.9, location_id=1, position=2}]
    
        List<Map.Entry<String, List<StreamEntry>>> res18= jedis.xread(XReadParams.xReadParams().count(2),new HashMap<String,StreamEntryID>(){{put("race:france",new StreamEntryID());}});
        System.out.println(
          res18
        ); // >>> [race:france=[1701765384638-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701765384638-1 {rider=Norem, speed=28.8, location_id=1, position=3}]]
    
        String res19 = jedis.xgroupCreate("race:france","france_riders",StreamEntryID.LAST_ENTRY,false);
        System.out.println(res19); // >>> OK
    
        String res20 = jedis.xgroupCreate("race:italy","italy_riders",StreamEntryID.LAST_ENTRY,true);
        System.out.println(res20); // >>> OK
    
        StreamEntryID id1 = jedis.xadd("race:italy", new HashMap<String,String>(){{put("rider","Castilaa");}},XAddParams.xAddParams());
        StreamEntryID id2 = jedis.xadd("race:italy", new HashMap<String,String>(){{put("rider","Royce");}},XAddParams.xAddParams());
        StreamEntryID id3 = jedis.xadd("race:italy", new HashMap<String,String>(){{put("rider","Sam-Bodden");}},XAddParams.xAddParams());
        StreamEntryID id4 = jedis.xadd("race:italy", new HashMap<String,String>(){{put("rider","Prickett");}},XAddParams.xAddParams());
        StreamEntryID id5 = jedis.xadd("race:italy", new HashMap<String,String>(){{put("rider","Norem");}},XAddParams.xAddParams());
    
        List<Map.Entry<String, List<StreamEntry>>> res21 = jedis.xreadGroup("italy_riders","Alice", XReadGroupParams.xReadGroupParams().count(1),new HashMap<String,StreamEntryID>(){{put("race:italy",StreamEntryID.UNRECEIVED_ENTRY);}});
        System.out.println(res21); // >>> [race:italy=[1701766299006-0 {rider=Castilaa}]]
    
        List<Map.Entry<String, List<StreamEntry>>> res22 = jedis.xreadGroup("italy_riders","Alice", XReadGroupParams.xReadGroupParams().count(1),new HashMap<String,StreamEntryID>(){{put("race:italy",new StreamEntryID());}});
        System.out.println(res22); // >>> [race:italy=[1701766299006-0 {rider=Castilaa}]]
    
        long res23 = jedis.xack("race:italy","italy_riders",id1);
        System.out.println(res23); // >>> 1
    
        List<Map.Entry<String, List<StreamEntry>>> res24 = jedis.xreadGroup("italy_riders","Alice", XReadGroupParams.xReadGroupParams().count(1),new HashMap<String,StreamEntryID>(){{put("race:italy",new StreamEntryID());}});
        System.out.println(res24); // >>> [race:italy=[]]
    
        List<Map.Entry<String, List<StreamEntry>>> res25 = jedis.xreadGroup("italy_riders","Bob", XReadGroupParams.xReadGroupParams().count(2),new HashMap<String,StreamEntryID>(){{put("race:italy",StreamEntryID.UNRECEIVED_ENTRY);}});
        System.out.println(res25); // >>> [race:italy=[1701767632261-1 {rider=Royce}, 1701767632262-0 {rider=Sam-Bodden}]]
    
        StreamPendingSummary res26 = jedis.xpending("race:italy","italy_riders");
        System.out.println(res26.getConsumerMessageCount()); // >>> {Bob=2}
    
        List<StreamPendingEntry> res27 = jedis.xpending("race:italy","italy_riders",XPendingParams.xPendingParams().start(StreamEntryID.MINIMUM_ID).end(StreamEntryID.MAXIMUM_ID).count(10));
        System.out.println(res27); // >>> [1701768567412-1 Bob idle:0 times:1, 1701768567412-2 Bob idle:0 times:1]
    
        List<StreamEntry> res28 = jedis.xrange("race:italy",id2.toString(),id2.toString());
        System.out.println(res28); // >>> [1701768744819-1 {rider=Royce}]
    
        List<StreamEntry> res29 = jedis.xclaim("race:italy","italy_riders","Alice", 0L, XClaimParams.xClaimParams().time(60000),id2);
        System.out.println(res29); // >>> [1701769004195-1 {rider=Royce}]
    
        Map.Entry<StreamEntryID, List<StreamEntry>> res30 = jedis.xautoclaim("race:italy","italy_riders","Alice",1L,new StreamEntryID("0-0"),XAutoClaimParams.xAutoClaimParams().count(1));
        System.out.println(res30); // >>> [1701769266831-2=[1701769266831-1 {rider=Royce}]
    
        Map.Entry<StreamEntryID, List<StreamEntry>> res31 = jedis.xautoclaim("race:italy","italy_riders","Alice",1L,new StreamEntryID(id2.toString()),XAutoClaimParams.xAutoClaimParams().count(1));
        System.out.println(res31); // >>> [0-0=[1701769605847-2 {rider=Sam-Bodden}]
    
        StreamInfo res32 = jedis.xinfoStream("race:italy");
        System.out.println(
          res32.getStreamInfo()
        ); // >>> {radix-tree-keys=1, radix-tree-nodes=2, entries-added=5, length=5, groups=1, max-deleted-entry-id=0-0, first-entry=1701769637612-0 {rider=Castilaa}, last-generated-id=1701769637612-4, last-entry=1701769637612-4 {rider=Norem}, recorded-first-entry-id=1701769637612-0}
    
        List<StreamGroupInfo> res33 = jedis.xinfoGroups("race:italy");
        for (StreamGroupInfo a : res33){
          System.out.println(
            a.getGroupInfo()
          ); // >>> {last-delivered-id=1701770253659-0, lag=2, pending=2, name=italy_riders, consumers=2, entries-read=3}
        }
    
        List<StreamConsumersInfo> res34 = jedis.xinfoConsumers("race:italy","italy_riders");
        for (StreamConsumerInfo a : res34){
          System.out.println(
            a.getConsumerInfo()
          ); // {inactive=1, idle=1, pending=1, name=Alice} , {inactive=3, idle=3, pending=1, name=Bob}
        }
    
        jedis.xadd("race:italy", new HashMap<String,String>(){{put("rider","Jones");}},XAddParams.xAddParams().maxLen(10));
        jedis.xadd("race:italy", new HashMap<String,String>(){{put("rider","Wood");}},XAddParams.xAddParams().maxLen(10));
        jedis.xadd("race:italy", new HashMap<String,String>(){{put("rider","Henshaw");}},XAddParams.xAddParams().maxLen(10));
        long res35 = jedis.xlen("race:italy");
        System.out.println(res35); // >>> 8
    
        List<StreamEntry> res36 = jedis.xrange("race:italy","-","+");
        System.out.println(res36); // >>> [1701771219852-0 {rider=Castilaa}, 1701771219852-1 {rider=Royce}, 1701771219853-0 {rider=Sam-Bodden}, 1701771219853-1 {rider=Prickett}, 1701771219853-2 {rider=Norem}, 1701771219858-0 {rider=Jones}, 1701771219858-1 {rider=Wood}, 1701771219859-0 {rider=Henshaw}]
    
        StreamEntryID id6 = jedis.xadd("race:italy", new HashMap<String,String>(){{put("rider","Smith");}},XAddParams.xAddParams().maxLen(2));
    
        List<StreamEntry> res37 = jedis.xrange("race:italy","-","+");
        System.out.println(res37); // >>> [1701771067332-1 {rider=Henshaw}, 1701771067332-2 {rider=Smith}]
    
        long res38 = jedis.xtrim("race:italy",XTrimParams.xTrimParams().maxLen(10).exactTrimming());
        System.out.println(res38); /// >>> 0
    
        long res39 = jedis.xtrim("race:italy",XTrimParams.xTrimParams().maxLen(10));
        System.out.println(res39); /// >>> 0
    
        List<StreamEntry> res40 = jedis.xrange("race:italy","-","+");
        System.out.println(res40); // >>> [1701771356428-2 {rider=Henshaw}, 1701771356429-0 {rider=Smith}]
    
        long res41 = jedis.xdel("race:italy",id6);
        System.out.println(res41); // >>> 1
    
        List<StreamEntry> res42 = jedis.xrange("race:italy","-","+");
        System.out.println(res42); // >>> [1701771517639-1 {rider=Henshaw}]
    
        jedis.close();
      }
    
    }
    
    package example_commands_test
    
    import (
    	"context"
    	"fmt"
    
    	"github.com/redis/go-redis/v9"
    )
    
    
    
    func ExampleClient_xadd() {
    	ctx := context.Background()
    
    	rdb := redis.NewClient(&redis.Options{
    		Addr:     "localhost:6379",
    		Password: "", // no password docs
    		DB:       0,  // use default DB
    	})
    
    
    	res1, err := rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:france",
    		Values: map[string]interface{}{
    			"rider":       "Castilla",
    			"speed":       30.2,
    			"position":    1,
    			"location_id": 1,
    		},
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	// fmt.Println(res1) // >>> 1692632086370-0
    
    	res2, err := rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:france",
    		Values: map[string]interface{}{
    			"rider":       "Norem",
    			"speed":       28.8,
    			"position":    3,
    			"location_id": 1,
    		},
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	// fmt.PrintLn(res2) // >>> 1692632094485-0
    
    	res3, err := rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:france",
    		Values: map[string]interface{}{
    			"rider":       "Prickett",
    			"speed":       29.7,
    			"position":    2,
    			"location_id": 1,
    		},
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	// fmt.Println(res3) // >>> 1692632102976-0
    
    
    	xlen, err := rdb.XLen(ctx, "race:france").Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(xlen) // >>> 3
    
    }
    
    func ExampleClient_racefrance1() {
    	ctx := context.Background()
    
    	rdb := redis.NewClient(&redis.Options{
    		Addr:     "localhost:6379",
    		Password: "", // no password docs
    		DB:       0,  // use default DB
    	})
    
    
    	_, err := rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:france",
    		Values: map[string]interface{}{
    			"rider":       "Castilla",
    			"speed":       30.2,
    			"position":    1,
    			"location_id": 1,
    		},
    		ID: "1692632086370-0",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:france",
    		Values: map[string]interface{}{
    			"rider":       "Norem",
    			"speed":       28.8,
    			"position":    3,
    			"location_id": 1,
    		},
    		ID: "1692632094485-0",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:france",
    		Values: map[string]interface{}{
    			"rider":       "Prickett",
    			"speed":       29.7,
    			"position":    2,
    			"location_id": 1,
    		},
    		ID: "1692632102976-0",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	res4, err := rdb.XRangeN(ctx, "race:france", "1691765278160-0", "+", 2).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res4)
    	// >>> [{1692632086370-0 map[location_id:1 position:1 rider:Castilla...
    
    	res5, err := rdb.XRead(ctx, &redis.XReadArgs{
    		Streams: []string{"race:france", "0"},
    		Count:   100,
    		Block:   300,
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res5)
    	// >>> // [{race:france [{1692632086370-0 map[location_id:1 position:1...
    
    	res6, err := rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:france",
    		Values: map[string]interface{}{
    			"rider":       "Castilla",
    			"speed":       29.9,
    			"position":    1,
    			"location_id": 2,
    		},
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	//fmt.Println(res6) // >>> 1692632147973-0
    
    	res7, err := rdb.XLen(ctx, "race:france").Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res7) // >>> 4
    
    
    }
    
    func ExampleClient_raceusa() {
    	ctx := context.Background()
    
    	rdb := redis.NewClient(&redis.Options{
    		Addr:     "localhost:6379",
    		Password: "", // no password docs
    		DB:       0,  // use default DB
    	})
    
    
    	res8, err := rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:usa",
    		Values: map[string]interface{}{
    			"racer": "Castilla",
    		},
    		ID: "0-1",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res8) // >>> 0-1
    
    	res9, err := rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:usa",
    		Values: map[string]interface{}{
    			"racer": "Norem",
    		},
    		ID: "0-2",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res9) // >>> 0-2
    
    	res10, err := rdb.XAdd(ctx, &redis.XAddArgs{
    		Values: map[string]interface{}{
    			"racer": "Prickett",
    		},
    		ID: "0-1",
    	}).Result()
    
    	if err != nil {
    		// fmt.Println(err)
    		// >>> ERR The ID specified in XADD is equal or smaller than the target stream top item
    	}
    
    	res11, err := rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:usa",
    		Values: map[string]interface{}{
    			"racer": "Prickett",
    		},
    		ID: "0-*",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res11) // >>> 0-3
    
    
    }
    
    func ExampleClient_racefrance2() {
    	ctx := context.Background()
    
    	rdb := redis.NewClient(&redis.Options{
    		Addr:     "localhost:6379",
    		Password: "", // no password docs
    		DB:       0,  // use default DB
    	})
    
    
    	_, err := rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:france",
    		Values: map[string]interface{}{
    			"rider":       "Castilla",
    			"speed":       30.2,
    			"position":    1,
    			"location_id": 1,
    		},
    		ID: "1692632086370-0",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:france",
    		Values: map[string]interface{}{
    			"rider":       "Norem",
    			"speed":       28.8,
    			"position":    3,
    			"location_id": 1,
    		},
    		ID: "1692632094485-0",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:france",
    		Values: map[string]interface{}{
    			"rider":       "Prickett",
    			"speed":       29.7,
    			"position":    2,
    			"location_id": 1,
    		},
    		ID: "1692632102976-0",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:france",
    		Values: map[string]interface{}{
    			"rider":       "Castilla",
    			"speed":       29.9,
    			"position":    1,
    			"location_id": 2,
    		},
    		ID: "1692632147973-0",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    	res12, err := rdb.XRange(ctx, "race:france", "-", "+").Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res12)
    	// >>> [{1692632086370-0 map[location_id:1 position:1 rider:Castilla...
    
    	res13, err := rdb.XRange(ctx, "race:france",
    		"1692632086369", "1692632086371",
    	).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res13)
    	// >>> [{1692632086370-0 map[location_id:1 position:1 rider:Castilla speed:30.2] 0 0}]
    
    	res14, err := rdb.XRangeN(ctx, "race:france", "-", "+", 2).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res14)
    	// >>> [{1692632086370-0 map[location_id:1 position:1 rider:Castilla speed:30.2] 0 0} {1692632094485-0 map[location_id:1 position:3 rider:Norem speed:28.8] 0 0}]
    
    	res15, err := rdb.XRangeN(ctx, "race:france",
    		"(1692632094485-0", "+", 2,
    	).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res15)
    	// >>> [{1692632102976-0 map[location_id:1 position:2 rider:Prickett speed:29.7] 0 0} {1692632147973-0 map[location_id:2 position:1 rider:Castilla speed:29.9] 0 0}]
    
    	res16, err := rdb.XRangeN(ctx, "race:france",
    		"(1692632147973-0", "+", 2,
    	).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res16)
    	// >>> []
    
    	res17, err := rdb.XRevRangeN(ctx, "race:france", "+", "-", 1).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res17)
    	// >>> [{1692632147973-0 map[location_id:2 position:1 rider:Castilla speed:29.9] 0 0}]
    
    	res18, err := rdb.XRead(ctx, &redis.XReadArgs{
    		Streams: []string{"race:france", "0"},
    		Count:   2,
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res18)
    	// >>> [{race:france [{1692632086370-0 map[location_id:1 position:1 rider:Castilla speed:30.2] 0 0} {1692632094485-0 map[location_id:1 position:3 rider:Norem speed:28.8] 0 0}]}]
    
    }
    
    func ExampleClient_xgroupcreate() {
    	ctx := context.Background()
    
    	rdb := redis.NewClient(&redis.Options{
    		Addr:     "localhost:6379",
    		Password: "", // no password docs
    		DB:       0,  // use default DB
    	})
    
    
    	_, err := rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:france",
    		Values: map[string]interface{}{
    			"rider":       "Castilla",
    			"speed":       30.2,
    			"position":    1,
    			"location_id": 1,
    		},
    		ID: "1692632086370-0",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	res19, err := rdb.XGroupCreate(ctx, "race:france", "france_riders", "$").Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res19) // >>> OK
    
    }
    
    func ExampleClient_xgroupcreatemkstream() {
    	ctx := context.Background()
    
    	rdb := redis.NewClient(&redis.Options{
    		Addr:     "localhost:6379",
    		Password: "", // no password docs
    		DB:       0,  // use default DB
    	})
    
    
    	res20, err := rdb.XGroupCreateMkStream(ctx,
    		"race:italy", "italy_riders", "$",
    	).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res20) // >>> OK
    
    }
    
    func ExampleClient_xgroupread() {
    	ctx := context.Background()
    
    	rdb := redis.NewClient(&redis.Options{
    		Addr:     "localhost:6379",
    		Password: "", // no password docs
    		DB:       0,  // use default DB
    	})
    
    
    	_, err := rdb.XGroupCreateMkStream(ctx,
    		"race:italy", "italy_riders", "$",
    	).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:italy",
    		Values: map[string]interface{}{"rider": "Castilla"},
    	}).Result()
    	// >>> 1692632639151-0
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:italy",
    		Values: map[string]interface{}{"rider": "Royce"},
    	}).Result()
    	// >>> 1692632647899-0
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:italy",
    		Values: map[string]interface{}{"rider": "Sam-Bodden"},
    	}).Result()
    	// >>> 1692632662819-0
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:italy",
    		Values: map[string]interface{}{"rider": "Prickett"},
    	}).Result()
    	// >>> 1692632670501-0
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:italy",
    		Values: map[string]interface{}{"rider": "Norem"},
    	}).Result()
    	// >>> 1692632678249-0
    
    	if err != nil {
    		panic(err)
    	}
    
    	// fmt.Println(res25)
    
    	res21, err := rdb.XReadGroup(ctx, &redis.XReadGroupArgs{
    		Streams:  []string{"race:italy", ">"},
    		Group:    "italy_riders",
    		Consumer: "Alice",
    		Count:    1,
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	// fmt.Println(res21)
    	// >>> [{race:italy [{1692632639151-0 map[rider:Castilla] 0 0}]}]
    
    
    	xlen, err := rdb.XLen(ctx, "race:italy").Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(xlen)
    
    }
    
    func ExampleClient_raceitaly() {
    	ctx := context.Background()
    
    	rdb := redis.NewClient(&redis.Options{
    		Addr:     "localhost:6379",
    		Password: "", // no password docs
    		DB:       0,  // use default DB
    	})
    
    
    	_, err := rdb.XGroupCreateMkStream(ctx,
    		"race:italy", "italy_riders", "$",
    	).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:italy",
    		Values: map[string]interface{}{"rider": "Castilla"},
    		ID:     "1692632639151-0",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:italy",
    		Values: map[string]interface{}{"rider": "Royce"},
    		ID:     "1692632647899-0",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:italy",
    		Values: map[string]interface{}{"rider": "Sam-Bodden"},
    		ID:     "1692632662819-0",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:italy",
    		Values: map[string]interface{}{"rider": "Prickett"},
    		ID:     "1692632670501-0",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:italy",
    		Values: map[string]interface{}{"rider": "Norem"},
    		ID:     "1692632678249-0",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XReadGroup(ctx, &redis.XReadGroupArgs{
    		Streams:  []string{"race:italy", ">"},
    		Group:    "italy_riders",
    		Consumer: "Alice",
    		Count:    1,
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    	res22, err := rdb.XReadGroup(ctx, &redis.XReadGroupArgs{
    		Streams:  []string{"race:italy", "0"},
    		Group:    "italy_riders",
    		Consumer: "Alice",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res22)
    	// >>> [{race:italy [{1692632639151-0 map[rider:Castilla] 0 0}]}]
    
    	res23, err := rdb.XAck(ctx,
    		"race:italy", "italy_riders", "1692632639151-0",
    	).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res23) // >>> 1
    
    	res24, err := rdb.XReadGroup(ctx, &redis.XReadGroupArgs{
    		Streams:  []string{"race:italy", "0"},
    		Group:    "italy_riders",
    		Consumer: "Alice",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res24)
    	// >>> [{race:italy []}]
    
    	res25, err := rdb.XReadGroup(ctx, &redis.XReadGroupArgs{
    		Streams:  []string{"race:italy", ">"},
    		Group:    "italy_riders",
    		Consumer: "Bob",
    		Count:    2,
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res25)
    	// >>> [{race:italy [{1692632647899-0 map[rider:Royce] 0 0} {1692632662819-0 map[rider:Sam-Bodden] 0 0}]}]
    
    
    	res26, err := rdb.XPending(ctx, "race:italy", "italy_riders").Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res26)
    	// >>> &{2 1692632647899-0 1692632662819-0 map[Bob:2]}
    
    	res27, err := rdb.XPendingExt(ctx, &redis.XPendingExtArgs{
    		Stream: "race:italy",
    		Group:  "italy_riders",
    		Start:  "-",
    		End:    "+",
    		Count:  10,
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	// fmt.Println(res27)
    	// >>> [{1692632647899-0 Bob 0s 1} {1692632662819-0 Bob 0s 1}]
    
    	res28, err := rdb.XRange(ctx, "race:italy",
    		"1692632647899-0", "1692632647899-0",
    	).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res28) // >>> [{1692632647899-0 map[rider:Royce] 0 0}]
    
    	res29, err := rdb.XClaim(ctx, &redis.XClaimArgs{
    		Stream:   "race:italy",
    		Group:    "italy_riders",
    		Consumer: "Alice",
    		MinIdle:  0,
    		Messages: []string{"1692632647899-0"},
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res29)
    
    	res30, res30a, err := rdb.XAutoClaim(ctx, &redis.XAutoClaimArgs{
    		Stream:   "race:italy",
    		Group:    "italy_riders",
    		Consumer: "Alice",
    		Start:    "0-0",
    		Count:    1,
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res30)  // >>> [{1692632647899-0 map[rider:Royce] 0 0}]
    	fmt.Println(res30a) // >>> 1692632662819-0
    
    	res31, res31a, err := rdb.XAutoClaim(ctx, &redis.XAutoClaimArgs{
    		Stream:   "race:italy",
    		Group:    "italy_riders",
    		Consumer: "Lora",
    		Start:    "(1692632662819-0",
    		Count:    1,
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res31)  // >>> []
    	fmt.Println(res31a) // >>> 0-0
    
    	res32, err := rdb.XInfoStream(ctx, "race:italy").Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res32.Length)
    	// >>> 5
    	fmt.Println(res32.FirstEntry)
    	// >>> {1692632639151-0 map[rider:Castilla] 0 0}
    
    	res33, err := rdb.XInfoGroups(ctx, "race:italy").Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res33)
    	// >>> [{italy_riders 3 2 1692632662819-0 3 2}]
    
    	res34, err := rdb.XInfoConsumers(ctx, "race:italy", "italy_riders").Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	// fmt.Println(res34)
    	// >>> [{Alice 1 1ms 1ms} {Bob 1 2ms 2ms} {Lora 0 1ms -1ms}]
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:italy",
    		MaxLen: 2,
    		Values: map[string]interface{}{"rider": "Jones"},
    	},
    	).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:italy",
    		MaxLen: 2,
    		Values: map[string]interface{}{"rider": "Wood"},
    	},
    	).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:italy",
    		MaxLen: 2,
    		Values: map[string]interface{}{"rider": "Henshaw"},
    	},
    	).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	res35, err := rdb.XLen(ctx, "race:italy").Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res35) // >>> 2
    
    	res36, err := rdb.XRange(ctx, "race:italy", "-", "+").Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	// fmt.Println(res36)
    	// >>> [{1726649529170-1 map[rider:Wood] 0 0} {1726649529171-0 map[rider:Henshaw] 0 0}]
    
    	res37, err := rdb.XTrimMaxLen(ctx, "race:italy", 10).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res37) // >>> 0
    
    	res38, err := rdb.XTrimMaxLenApprox(ctx, "race:italy", 10, 20).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res38) // >>> 0
    
    
    }
    
    func ExampleClient_xdel() {
    	ctx := context.Background()
    
    	rdb := redis.NewClient(&redis.Options{
    		Addr:     "localhost:6379",
    		Password: "", // no password docs
    		DB:       0,  // use default DB
    	})
    
    
    	_, err := rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:italy",
    		MaxLen: 2,
    		Values: map[string]interface{}{"rider": "Wood"},
    		ID:     "1692633198206-0",
    	},
    	).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:italy",
    		MaxLen: 2,
    		Values: map[string]interface{}{"rider": "Henshaw"},
    		ID:     "1692633208557-0",
    	},
    	).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	res39, err := rdb.XRangeN(ctx, "race:italy", "-", "+", 2).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res39)
    	// >>> [{1692633198206-0 map[rider:Wood] 0 0} {1692633208557-0 map[rider:Henshaw] 0 0}]
    
    	res40, err := rdb.XDel(ctx, "race:italy", "1692633208557-0").Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res40) // 1
    
    	res41, err := rdb.XRangeN(ctx, "race:italy", "-", "+", 2).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res41)
    	// >>> [{1692633198206-0 map[rider:Wood] 0 0}]
    
    }
    
    
    using NRedisStack.Tests;
    using StackExchange.Redis;
    
    
    
    public class StreamTutorial
    {
        public void Run()
        {
            var muxer = ConnectionMultiplexer.Connect("localhost:6379");
            var db = muxer.GetDatabase();
    
            RedisValue res1 = db.StreamAdd(
                "race:france",
                [
                    new("rider", "Castilla"),
                    new("speed", 30.2),
                    new("position", 1),
                    new("location_id", 1)
                ]
            );
            Console.WriteLine(res1);    // >>> 1712668482289-0
    
            RedisValue res2 = db.StreamAdd(
                "race:france",
                [
                    new("rider", "Norem"),
                    new("speed", 28.8),
                    new("position", 3),
                    new("location_id", 1)
                ]
            );
            Console.WriteLine(res2);    // >>> 1712668766534-1
    
            RedisValue res3 = db.StreamAdd(
                "race:france",
                [
                    new("rider", "Prickett"),
                    new("speed", 29.7),
                    new("position", 2),
                    new("location_id", 1)
                ]
            );
            Console.WriteLine(res3);    // >>> 1712669055705-0
    
    
            // Tests for 'xadd' step.
    
    
            StreamEntry[] res4 = db.StreamRange("race:france", "1712668482289-0", "+", 2);
    
            foreach (StreamEntry entry in res4)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
    
            // >>> 1712668482289-0: [rider: Castilla, speed: 30.199999999999999, position: 1, location_id: 1]
            // >>> 1712668766534-1: [rider: Norem, speed: 28.800000000000001, position: 3, location_id: 1]
    
            // Tests for 'xrange' step.
    
    
            StreamEntry[] res5 = db.StreamRead("race:france", 0, 100);
    
            foreach (StreamEntry entry in res4)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
    
            // >>> 1712668482289-0: [rider: Castilla, speed: 30.199999999999999, position: 1, location_id: 1]
            // >>> 1712668766534-1: [rider: Norem, speed: 28.800000000000001, position: 3, location_id: 1]
            // >>> 1712669055705-0: [rider: Prickett, speed: 29.699999999999999, position: 2, location_id: 1]
    
            // Tests for 'xread_block' step.
    
    
            RedisValue res6 = db.StreamAdd(
                "race:france",
                [
                    new("rider", "Castilla"),
                    new("speed", 29.9),
                    new("position", 1),
                    new("location_id", 2)
                ]
            );
    
            Console.WriteLine(res6);    // >>> 1712675674750-0
    
            // Tests for 'xadd_2' step.
    
    
            long res7 = db.StreamLength("race:france");
            Console.WriteLine(res7);    // >>> 4
    
            // Tests for 'xlen' step.
    
    
            RedisValue res8 = db.StreamAdd(
                "race:usa",
                [
                    new("racer", "Castilla")
                ],
                "0-1"
            );
            Console.WriteLine(res8);    // >>> 0-1
    
            RedisValue res9 = db.StreamAdd(
                "race:usa",
                [
                    new("racer", "Norem")
                ],
                "0-2"
            );
            Console.WriteLine(res9);    // >>> 0-2
    
            // Tests for 'xadd_id' step.
    
    
            try
            {
                RedisValue res10 = db.StreamAdd(
                    "race:usa",
                    [
                        new("racer", "Prickett")
                    ],
                    "0-1"
                );
            }
            catch (RedisServerException ex)
            {
                Console.WriteLine(ex);  // >>> ERR The ID specified in XADD is equal or smaller than the target stream top item
            }
    
            // Tests for 'xadd_bad_id' step.
    
    
            RedisValue res11 = "";
            Version version = muxer.GetServer("localhost:6379").Version;
            if (version.Major >= 7)
            {
                res11 = db.StreamAdd(
                    "race:usa",
                    [
                        new("rider", "Norem")
                    ],
                    "0-*"
                );
    
                Console.WriteLine(res11);   // >>> "0-3"
            }
    
            // Tests for 'xadd_7' step.
    
    
            StreamEntry[] res12 = db.StreamRange("race:france", "-", "+");
    
            foreach (StreamEntry entry in res12)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
            // >>> 1712668482289-0: [rider: Castilla, speed: 30.199999999999999, position: 1, location_id: 1]
            // >>> 1712668766534-1: [rider: Norem, speed: 28.800000000000001, position: 3, location_id: 1]
            // >>> 1712669055705-0: [rider: Prickett, speed: 29.699999999999999, position: 2, location_id: 1]
            // >>> 1712675674750-0: [rider: Castilla, speed: 29.899999999999999, position: 1, location_id: 2]
    
            // Tests for 'xrange_all' step.
    
    
            StreamEntry[] res13 = db.StreamRange("race:france", 1712668482289, 1712668482291);
    
            foreach (StreamEntry entry in res13)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
            // >>> 1712668482289-0: [rider: Castilla, speed: 30.199999999999999, position: 1, location_id: 1]
    
            // Tests for 'xrange_time' step.
    
    
            StreamEntry[] res14 = db.StreamRange("race:france", "-", "+", 2);
    
            foreach (StreamEntry entry in res14)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
            // >>> 1712668482289-0: [rider: Castilla, speed: 30.199999999999999, position: 1, location_id: 1]
            // >>> 1712668766534-1: [rider: Norem, speed: 28.800000000000001, position: 3, location_id: 1]
    
            // Tests for 'xrange_step_1' step.
    
    
            StreamEntry[] res15 = db.StreamRange("race:france", "(1712668766534-1", "+", 2);
    
            foreach (StreamEntry entry in res15)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
            // >>> 1712669055705-0: [rider: Prickett, speed: 29.699999999999999, position: 2, location_id: 1]
            // >>> 1712675674750-0: [rider: Castilla, speed: 29.899999999999999, position: 1, location_id: 2]
    
            // Tests for 'xrange_step_2' step.
    
    
            StreamEntry[] res16 = db.StreamRange("race:france", "(1712675674750-0", "+", 2);
    
            foreach (StreamEntry entry in res16)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
            // >>> <empty array>
    
            // Tests for 'xrange_empty' step.
    
    
            StreamEntry[] res17 = db.StreamRange("race:france", "+", "-", 1, Order.Descending);
    
            foreach (StreamEntry entry in res17)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
            // >>> 1712675674750-0: [rider: Castilla, speed: 29.899999999999999, position: 1, location_id: 2]
    
            // Tests for 'xrevrange' step.
    
    
            StreamEntry[] res18 = db.StreamRead("race:france", 0, 2);
    
            foreach (StreamEntry entry in res18)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
            // >>> 1712668482289-0: [rider: Castilla, speed: 30.199999999999999, position: 1, location_id: 1]
            // >>> 1712668766534-1: [rider: Norem, speed: 28.800000000000001, position: 3, location_id: 1]
    
            // Tests for 'xread' step.
    
    
            bool res19 = db.StreamCreateConsumerGroup("race:france", "france_riders", "$");
            Console.WriteLine(res19);   // >>> true
    
            // Tests for 'xgroup_create' step.
    
    
            bool res20 = db.StreamCreateConsumerGroup("race:italy", "italy_riders", "$", true);
            Console.WriteLine(res20);   // >>> true
    
            // Tests for 'xgroup_create_mkstream' step.
    
    
            RedisValue groupRes = db.StreamAdd(
                "race:italy",
                [new("rider", "Castilla")]
            ); // 1712744323758-0
    
            groupRes = db.StreamAdd(
                "race:italy",
                [new("rider", "Royce")]
            ); // 1712744358384-0
    
            groupRes = db.StreamAdd(
                "race:italy",
                [new("rider", "Sam-Bodden")]
            ); // 1712744379676-0
    
            groupRes = db.StreamAdd(
                "race:italy",
                [new("rider", "Prickett")]
            ); // 1712744399401-0
    
            groupRes = db.StreamAdd(
                "race:italy",
                [new("rider", "Norem")]
            ); // 1712744413117-0
    
            StreamEntry[] res21 = db.StreamReadGroup("race:italy", "italy_riders", "Alice", ">", 1);
    
            foreach (StreamEntry entry in res21)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
            // >>> 1712744323758-0: [rider: Castilla]
    
            // Tests for 'xgroup_read' step.
    
    
            StreamEntry[] res22 = db.StreamReadGroup("race:italy", "italy_riders", "Alice", "0");
    
            foreach (StreamEntry entry in res22)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
                // >>> 1712744323758-0: [rider: Castilla]
            }
    
            // Tests for 'xgroup_read_id' step.
    
    
            long res23 = db.StreamAcknowledge("race:italy", "italy_riders", "1712744323758-0");
            Console.WriteLine(res23);   // >>> 1
    
            StreamEntry[] res24 = db.StreamReadGroup("race:italy", "italy_riders", "Alice", "0");
    
            foreach (StreamEntry entry in res24)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
            // >>> <empty array>
    
            // Tests for 'xack' step.
    
    
            StreamEntry[] res25 = db.StreamReadGroup("race:italy", "italy_riders", "Bob", ">", 2);
    
            foreach (StreamEntry entry in res25)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
            // >>> 1712744358384-0: [rider: Royce]
            // >>> 1712744379676-0: [rider: Sam-Bodden]
    
            // Tests for 'xgroup_read_bob' step.
    
    
            StreamPendingInfo res26 = db.StreamPending("race:italy", "italy_riders");
            Console.WriteLine($"pending: {res26.PendingMessageCount}, min: {res26.LowestPendingMessageId}, max: {res26.HighestPendingMessageId}, consumers:[{string.Join(", ", res26.Consumers.Select(c => $"{c.Name}: {c.PendingMessageCount}"))}]");
            // >>> pending: 2, min: 1712747506906-0, max: 1712747506907-0, consumers:[name: Bob, pending:2]
    
            // Tests for 'xpending' step.
    
    
            StreamPendingMessageInfo[] res27 = db.StreamPendingMessages(
                "race:italy", "italy_riders", 10, "", "-", "+"
            );
    
            foreach (StreamPendingMessageInfo info in res27)
            {
                Console.WriteLine($"message_id: {info.MessageId}, consumer: {info.ConsumerName}, time_since_delivered: {info.IdleTimeInMilliseconds}, times_delivered: {info.DeliveryCount}");
            }
            // >>> message_id: min: 1712747506906-0, consumer: Bob, time_since_delivered: 31084, times_delivered: 1
            // >>> message_id: min: 1712747506907-0, consumer: Bob, time_since_delivered: 31084, times_delivered: 1
    
            // Tests for 'xpending_plus_minus' step.
    
    
            StreamEntry[] res28 = db.StreamRange("race:italy", "1712744358384-0", "1712744358384-0");
    
            foreach (StreamEntry entry in res28)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
            // >>> 1712744358384-0: [rider: Royce]
    
            // Tests for 'xrange_pending' step.
    
    
            StreamEntry[] res29 = db.StreamClaim(
                "race:italy", "italy_riders", "Alice", 60000, [1712744358384 - 0]
            );
    
            foreach (StreamEntry entry in res29)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
            // >>> 1712744358384-0: [rider: Royce]
    
            // Tests for 'xclaim' step.
    
            StreamAutoClaimResult res30 = db.StreamAutoClaim(
                "race:italy", "italy_riders", "Alice", 1, "0-0", 1
            );
    
            Console.WriteLine($"{res30.NextStartId}, ({string.Join(", ", res30.ClaimedEntries.Select(entry => $"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]"))})");
            // >>> 1712744379676-0, (1712744358384-0: [rider: Royce])
    
            // Tests for 'xautoclaim' step.
    
    
            StreamAutoClaimResult res31 = db.StreamAutoClaim(
                "race:italy", "italy_riders", "Alice", 1, "(1712744358384-0", 1
            );
    
            Console.WriteLine($"{res31.NextStartId}, ({string.Join(", ", res31.ClaimedEntries.Select(entry => $"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]"))})");
            // >>> 0-0, (1712744379676-0: [rider: Sam-Bodden])
    
            // Tests for 'xautoclaim_cursor' step.
    
    
            StreamInfo res32 = db.StreamInfo("race:italy");
            Console.WriteLine($"length: {res32.Length}, radix-tree-keys: {res32.RadixTreeKeys}, radix-tree-nodes: {res32.RadixTreeNodes}, last-generated-id: {res32.LastGeneratedId}, first-entry: {$"{res32.FirstEntry.Id}: [{string.Join(", ", res32.FirstEntry.Values.Select(b => $"{b.Name}: {b.Value}"))}]"}, last-entry: {$"{res32.LastEntry.Id}: [{string.Join(", ", res32.LastEntry.Values.Select(b => $"{b.Name}: {b.Value}"))}]"}");
            // >>> length: 5, radix-tree-keys: 1, radix-tree-nodes: 2, last-generated-id: 1712756762686-1, first-entry: 1712756762685-0: [rider: Castilla], last-entry: 1712756762686-1: [rider: Norem]
    
            // Tests for 'xinfo' step.
    
    
            StreamGroupInfo[] res33 = db.StreamGroupInfo("race:italy");
    
            foreach (StreamGroupInfo info in res33)
            {
                Console.WriteLine($"name: {info.Name}, consumers: {info.ConsumerCount}, pending: {info.PendingMessageCount}, last-delivered-id: {info.LastDeliveredId}");
            }
            // >>> name: italy_riders, consumers: 2, pending: 2, last-delivered-id: 1712757192730-2
    
            // Tests for 'xinfo_groups' step.
    
    
            StreamConsumerInfo[] res34 = db.StreamConsumerInfo("race:italy", "italy_riders");
    
            foreach (StreamConsumerInfo info in res34)
            {
                Console.WriteLine($"name: {info.Name}, pending: {info.PendingMessageCount}, idle: {info.IdleTimeInMilliseconds}");
            }
            // >>> name: Alice, pending: 1, idle: 7717
            // >>> name: Bob, pending: 0, idle: 7722
    
            // Tests for 'xinfo_consumers' step.
    
    
            db.StreamAdd(
                "race:italy", [new("rider", "Jones")], null, 2, true
            );
    
            db.StreamAdd(
                "race:italy", [new("rider", "Wood")], null, 2, true
            );
    
            db.StreamAdd(
                "race:italy", [new("rider", "Henshaw")], null, 2, true
            );
    
            long res35 = db.StreamLength("race:italy");
            Console.WriteLine(res35); // >>> 8
    
            StreamEntry[] res36 = db.StreamRange("race:italy", "-", "+");
    
            foreach (StreamEntry entry in res36)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
            // >>> 1712758336128-0: [rider: Castilla]
            // >>> 1712758336128-1: [rider: Royce]
            // >>> 1712758336128-2: [rider: Sam-Bodden]
            // >>> 1712758336129-0: [rider: Prickett]
            // >>> 1712758336139-0: [rider: Norem]
            // >>> 1712758340854-0: [rider: Jones]
            // >>> 1712758341645-0: [rider: Wood]
            // >>> 1712758342134-0: [rider: Henshaw]
    
            db.StreamAdd(
                "race:italy", [new("rider", "Smith")], null, 2, false
            );
    
            StreamEntry[] res37 = db.StreamRange("race:italy", "-", "+");
    
            foreach (StreamEntry entry in res37)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
            // 1712758746476-1: [rider: Henshaw]
            // 1712758746477-0: [rider: Smith]
    
            // Tests for 'maxlen' step.
    
    
            long res38 = db.StreamTrim("race:italy", 10, false);
            Console.WriteLine(res38);   // >>> 0
    
            // Tests for 'xtrim' step.
    
    
            long res39 = db.StreamTrim("race:italy", 10, true);
            Console.WriteLine(res39);   // >>> 0
    
            // Tests for 'xtrim2' step.
    
    
            StreamEntry[] res40 = db.StreamRange("race:italy", "-", "+");
    
            foreach (StreamEntry entry in res40)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
            // >>> 1712759694003-0: [rider: Henshaw]
            // >>> 1712759694003-1: [rider: Smith]
    
            long res41 = db.StreamDelete("race:italy", ["1712759694003-1"]);
            Console.WriteLine(res41);   // >>> 1
    
            StreamEntry[] res42 = db.StreamRange("race:italy", "-", "+");
    
            foreach (StreamEntry entry in res42)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
    
            }
            // >>> 1712759694003-0: [rider: Henshaw]
    
            // Tests for 'xdel' step.
    
    
        }
    }
    
    
    require 'redis'
    
    r = Redis.new
    
    
    res1 = r.xadd('race:france', {
      'rider' => 'Castilla',
      'speed' => 30.2,
      'position' => 1,
      'location_id' => 1
    })
    puts res1 # 1692632086370-0, for example
    
    res2 = r.xadd('race:france', {
      'rider' => 'Norem',
      'speed' => 28.8,
      'position' => 3,
      'location_id' => 1
    })
    puts res2 # 1692632094485-0, for example
    
    res3 = r.xadd('race:france', {
      'rider' => 'Prickett',
      'speed' => 29.7,
      'position' => 2,
      'location_id' => 1
    })
    puts res3 # 1692632102976-0, for example
    
    
    r.del('race:france')
    r.xadd('race:france', {
      'rider' => 'Castilla',
      'speed' => '30.2',
      'position' => '1',
      'location_id' => '1'
    }, id: '1692632086370-0')
    r.xadd('race:france', {
      'rider' => 'Norem',
      'speed' => '28.8',
      'position' => '3',
      'location_id' => '1'
    }, id: '1692632094485-0')
    r.xadd('race:france', {
      'rider' => 'Prickett',
      'speed' => '29.7',
      'position' => '2',
      'location_id' => '1'
    }, id: '1692632102976-0')
    r.xadd('race:france', {
      'rider' => 'Castilla',
      'speed' => '29.9',
      'position' => '1',
      'location_id' => '2'
    }, id: '1692632147973-0')
    res4 = r.xrange('race:france', '1692632086370-0', '+', count: 2)
    puts res4.inspect
    # [["1692632086370-0", {"rider"=>"Castilla", "speed"=>"30.2", "position"=>"1", "location_id"=>"1"}],
    #  ["1692632094485-0", {"rider"=>"Norem", "speed"=>"28.8", "position"=>"3", "location_id"=>"1"}]]
    
    
    r.del('race:france')
    r.xadd('race:france', {'rider' => 'Castilla'}, id: '1692632086370-0')
    res5 = r.xread(['race:france'], ['$'], count: 100, block: 300)
    puts res5.inspect # {}
    
    
    res6 = r.xadd('race:france', {
      'rider' => 'Castilla',
      'speed' => 29.9,
      'position' => 1,
      'location_id' => 2
    })
    puts res6 # 1692632147973-0, for example
    
    
    r.del('race:france')
    r.xadd('race:france', {'rider' => 'Castilla'}, id: '1692632086370-0')
    r.xadd('race:france', {'rider' => 'Norem'}, id: '1692632094485-0')
    r.xadd('race:france', {'rider' => 'Prickett'}, id: '1692632102976-0')
    r.xadd('race:france', {'rider' => 'Castilla'}, id: '1692632147973-0')
    res7 = r.xlen('race:france')
    puts res7 # 4
    
    
    r.del('race:usa')
    res8 = r.xadd('race:usa', {'racer' => 'Castilla'}, id: '0-1')
    puts res8 # 0-1
    
    res9 = r.xadd('race:usa', {'racer' => 'Norem'}, id: '0-2')
    puts res9 # 0-2
    
    
    begin
      r.xadd('race:usa', {'racer' => 'Prickett'}, id: '0-1')
    rescue Redis::CommandError => e
      puts e.message
      # ERR The ID specified in XADD is equal or smaller than the target stream top item
    end
    
    
    r.del('race:usa')
    r.xadd('race:usa', {'racer' => 'Castilla'}, id: '0-1')
    r.xadd('race:usa', {'racer' => 'Norem'}, id: '0-2')
    res10 = r.xadd('race:usa', {'racer' => 'Prickett'}, id: '0-*')
    puts res10 # 0-3
    
    
    r.del('race:france')
    r.xadd('race:france', {'rider' => 'Castilla', 'speed' => '30.2', 'position' => '1', 'location_id' => '1'}, id: '1692632086370-0')
    r.xadd('race:france', {'rider' => 'Norem', 'speed' => '28.8', 'position' => '3', 'location_id' => '1'}, id: '1692632094485-0')
    r.xadd('race:france', {'rider' => 'Prickett', 'speed' => '29.7', 'position' => '2', 'location_id' => '1'}, id: '1692632102976-0')
    r.xadd('race:france', {'rider' => 'Castilla', 'speed' => '29.9', 'position' => '1', 'location_id' => '2'}, id: '1692632147973-0')
    res11 = r.xrange('race:france', '-', '+')
    puts res11.inspect
    # [["1692632086370-0", {"rider"=>"Castilla", "speed"=>"30.2", "position"=>"1", "location_id"=>"1"}],
    #  ["1692632094485-0", {"rider"=>"Norem", "speed"=>"28.8", "position"=>"3", "location_id"=>"1"}],
    #  ["1692632102976-0", {"rider"=>"Prickett", "speed"=>"29.7", "position"=>"2", "location_id"=>"1"}],
    #  ["1692632147973-0", {"rider"=>"Castilla", "speed"=>"29.9", "position"=>"1", "location_id"=>"2"}]]
    
    
    r.del('race:france')
    r.xadd('race:france', {'rider' => 'Castilla', 'speed' => '30.2', 'position' => '1', 'location_id' => '1'}, id: '1692632086370-0')
    r.xadd('race:france', {'rider' => 'Norem', 'speed' => '28.8', 'position' => '3', 'location_id' => '1'}, id: '1692632094485-0')
    res12 = r.xrange('race:france', '1692632086369', '1692632086371')
    puts res12.inspect
    # [["1692632086370-0", {"rider"=>"Castilla", "speed"=>"30.2", "position"=>"1", "location_id"=>"1"}]]
    
    
    r.del('race:france')
    r.xadd('race:france', {'rider' => 'Castilla', 'speed' => '30.2', 'position' => '1', 'location_id' => '1'}, id: '1692632086370-0')
    r.xadd('race:france', {'rider' => 'Norem', 'speed' => '28.8', 'position' => '3', 'location_id' => '1'}, id: '1692632094485-0')
    r.xadd('race:france', {'rider' => 'Prickett', 'speed' => '29.7', 'position' => '2', 'location_id' => '1'}, id: '1692632102976-0')
    r.xadd('race:france', {'rider' => 'Castilla', 'speed' => '29.9', 'position' => '1', 'location_id' => '2'}, id: '1692632147973-0')
    res13 = r.xrange('race:france', '-', '+', count: 2)
    puts res13.inspect
    # [["1692632086370-0", {"rider"=>"Castilla", "speed"=>"30.2", "position"=>"1", "location_id"=>"1"}],
    #  ["1692632094485-0", {"rider"=>"Norem", "speed"=>"28.8", "position"=>"3", "location_id"=>"1"}]]
    
    
    r.del('race:france')
    r.xadd('race:france', {'rider' => 'Castilla', 'speed' => '30.2', 'position' => '1', 'location_id' => '1'}, id: '1692632086370-0')
    r.xadd('race:france', {'rider' => 'Norem', 'speed' => '28.8', 'position' => '3', 'location_id' => '1'}, id: '1692632094485-0')
    r.xadd('race:france', {'rider' => 'Prickett', 'speed' => '29.7', 'position' => '2', 'location_id' => '1'}, id: '1692632102976-0')
    r.xadd('race:france', {'rider' => 'Castilla', 'speed' => '29.9', 'position' => '1', 'location_id' => '2'}, id: '1692632147973-0')
    res14 = r.xrange('race:france', '(1692632094485-0', '+', count: 2)
    puts res14.inspect
    # [["1692632102976-0", {"rider"=>"Prickett", "speed"=>"29.7", "position"=>"2", "location_id"=>"1"}],
    #  ["1692632147973-0", {"rider"=>"Castilla", "speed"=>"29.9", "position"=>"1", "location_id"=>"2"}]]
    
    
    res15 = r.xrange('race:france', '(1692632147973-0', '+', count: 2)
    puts res15.inspect # []
    
    
    res16 = r.xrevrange('race:france', '+', '-', count: 1)
    puts res16.inspect
    # [["1692632147973-0", {"rider"=>"Castilla", "speed"=>"29.9", "position"=>"1", "location_id"=>"2"}]]
    
    
    res17 = r.xread(['race:france'], ['0'], count: 2)
    puts res17.inspect
    # {"race:france"=>[["1692632086370-0", {"rider"=>"Castilla", "speed"=>"30.2", "position"=>"1", "location_id"=>"1"}],
    #                  ["1692632094485-0", {"rider"=>"Norem", "speed"=>"28.8", "position"=>"3", "location_id"=>"1"}]]}
    
    
    r.del('race:france')
    r.xadd('race:france', {'rider' => 'Castilla'}, id: '1692632086370-0')
    res18 = r.xgroup(:create, 'race:france', 'france_riders', '$')
    puts res18 # OK
    
    
    r.del('race:italy')
    res19 = r.xgroup(:create, 'race:italy', 'italy_riders', '$', mkstream: true)
    puts res19 # OK
    
    
    r.del('race:italy')
    r.xgroup(:create, 'race:italy', 'italy_riders', '$', mkstream: true)
    r.xadd('race:italy', {'rider' => 'Castilla'}, id: '1692632639151-0')
    r.xadd('race:italy', {'rider' => 'Royce'}, id: '1692632647899-0')
    r.xadd('race:italy', {'rider' => 'Sam-Bodden'}, id: '1692632662819-0')
    r.xadd('race:italy', {'rider' => 'Prickett'}, id: '1692632670501-0')
    r.xadd('race:italy', {'rider' => 'Norem'}, id: '1692632678249-0')
    
    res20 = r.xreadgroup('italy_riders', 'Alice', ['race:italy'], ['>'], count: 1)
    puts res20.inspect
    # {"race:italy"=>[["1692632639151-0", {"rider"=>"Castilla"}]]}
    
    
    res21 = r.xreadgroup('italy_riders', 'Alice', ['race:italy'], ['0'], count: 1)
    puts res21.inspect
    # {"race:italy"=>[["1692632639151-0", {"rider"=>"Castilla"}]]}
    
    
    res22 = r.xack('race:italy', 'italy_riders', '1692632639151-0')
    puts res22 # 1
    
    res23 = r.xreadgroup('italy_riders', 'Alice', ['race:italy'], ['0'])
    puts res23.inspect
    # {"race:italy"=>[]}
    
    
    res24 = r.xreadgroup('italy_riders', 'Bob', ['race:italy'], ['>'], count: 2)
    puts res24.inspect
    # {"race:italy"=>[["1692632647899-0", {"rider"=>"Royce"}],
    #                 ["1692632662819-0", {"rider"=>"Sam-Bodden"}]]}
    
    
    res25 = r.xpending('race:italy', 'italy_riders')
    puts res25.inspect
    # {"size"=>2, "min_entry_id"=>"1692632647899-0", "max_entry_id"=>"1692632662819-0", "consumers"=>{"Bob"=>"2"}}
    
    
    res26 = r.xpending('race:italy', 'italy_riders', '-', '+', 10)
    puts res26.inspect
    
    
    res27 = r.xrange('race:italy', '1692632647899-0', '1692632647899-0')
    puts res27.inspect
    # [["1692632647899-0", {"rider"=>"Royce"}]]
    
    
    res28 = r.xclaim('race:italy', 'italy_riders', 'Alice', 0, '1692632647899-0')
    puts res28.inspect
    # [["1692632647899-0", {"rider"=>"Royce"}]]
    
    
    res29 = r.xautoclaim('race:italy', 'italy_riders', 'Alice', 0, '0-0', count: 1)
    puts res29.inspect
    # {"next"=>"1692632662819-0", "entries"=>[["1692632647899-0", {"rider"=>"Royce"}]]}
    
    
    res30 = r.xautoclaim('race:italy', 'italy_riders', 'Lora', 0, res29['next'], count: 1)
    puts res30.inspect
    # {"next"=>"0-0", "entries"=>[["1692632662819-0", {"rider"=>"Sam-Bodden"}]]}
    
    
    res31 = r.xinfo(:stream, 'race:italy')
    puts res31.inspect
    
    
    res32 = r.xinfo(:groups, 'race:italy')
    puts res32.inspect
    
    
    res33 = r.xinfo(:consumers, 'race:italy', 'italy_riders')
    puts res33.inspect
    
    
    r.del('race:italy')
    r.xadd('race:italy', {'rider' => 'Castilla'}, id: '1692632639151-0')
    r.xadd('race:italy', {'rider' => 'Royce'}, id: '1692632647899-0')
    r.xadd('race:italy', {'rider' => 'Sam-Bodden'}, id: '1692632662819-0')
    r.xadd('race:italy', {'rider' => 'Prickett'}, id: '1692632670501-0')
    r.xadd('race:italy', {'rider' => 'Norem'}, id: '1692632678249-0')
    r.xadd('race:italy', {'rider' => 'Jones'}, id: '1692633189161-0', maxlen: 2)
    r.xadd('race:italy', {'rider' => 'Wood'}, id: '1692633198206-0', maxlen: 2)
    r.xadd('race:italy', {'rider' => 'Henshaw'}, id: '1692633208557-0', maxlen: 2)
    
    res34 = r.xlen('race:italy')
    puts res34 # 2
    
    res35 = r.xrange('race:italy', '-', '+')
    puts res35.inspect
    # [["1692633198206-0", {"rider"=>"Wood"}], ["1692633208557-0", {"rider"=>"Henshaw"}]]
    
    
    res36 = r.xtrim('race:italy', 10, approximate: false)
    puts res36 # 0
    
    
    r.del('mystream')
    1.upto(10) do |n|
      r.xadd('mystream', {'field' => 'value'}, id: "#{n}-0")
    end
    res37 = r.xtrim('mystream', 10, approximate: true)
    puts res37 # 0
    
    
    r.del('race:italy')
    r.xadd('race:italy', {'rider' => 'Wood'}, id: '1692633198206-0')
    r.xadd('race:italy', {'rider' => 'Henshaw'}, id: '1692633208557-0')
    res38 = r.xrange('race:italy', '-', '+', count: 2)
    puts res38.inspect
    # [["1692633198206-0", {"rider"=>"Wood"}], ["1692633208557-0", {"rider"=>"Henshaw"}]]
    
    res39 = r.xdel('race:italy', '1692633208557-0')
    puts res39 # 1
    
    res40 = r.xrange('race:italy', '-', '+', count: 2)
    puts res40.inspect
    # [["1692633198206-0", {"rider"=>"Wood"}]]
    
    
    mod stream_tests {
        use redis::{
            streams::{
                StreamAutoClaimOptions, StreamInfoConsumersReply, StreamInfoGroupsReply,
                StreamInfoStreamReply, StreamMaxlen, StreamPendingCountReply, StreamPendingReply,
                StreamRangeReply, StreamReadOptions, StreamReadReply, StreamTrimmingMode,
                StreamTrimOptions,
            },
            Commands,
        };
        use std::{thread::sleep, time::Duration};
    
        fn delete_keys(r: &mut redis::Connection, keys: &[&str]) {
            let _: usize = r.del(keys).unwrap_or(0);
        }
    
        fn add_france_fixed(r: &mut redis::Connection) {
            delete_keys(r, &["race:france"]);
            let _: Option<String> = r
                .xadd(
                    "race:france",
                    "1692632086370-0",
                    &[
                        ("rider", "Castilla"),
                        ("speed", "30.2"),
                        ("position", "1"),
                        ("location_id", "1"),
                    ],
                )
                .expect("add france 1");
            let _: Option<String> = r
                .xadd(
                    "race:france",
                    "1692632094485-0",
                    &[
                        ("rider", "Norem"),
                        ("speed", "28.8"),
                        ("position", "3"),
                        ("location_id", "1"),
                    ],
                )
                .expect("add france 2");
            let _: Option<String> = r
                .xadd(
                    "race:france",
                    "1692632102976-0",
                    &[
                        ("rider", "Prickett"),
                        ("speed", "29.7"),
                        ("position", "2"),
                        ("location_id", "1"),
                    ],
                )
                .expect("add france 3");
            let _: Option<String> = r
                .xadd(
                    "race:france",
                    "1692632147973-0",
                    &[
                        ("rider", "Castilla"),
                        ("speed", "29.9"),
                        ("position", "1"),
                        ("location_id", "2"),
                    ],
                )
                .expect("add france 4");
        }
    
        fn seed_usa_fixed(r: &mut redis::Connection) {
            delete_keys(r, &["race:usa"]);
            let _: Option<String> = r
                .xadd("race:usa", "0-1", &[("racer", "Castilla")])
                .expect("add usa 1");
            let _: Option<String> = r
                .xadd("race:usa", "0-2", &[("racer", "Norem")])
                .expect("add usa 2");
        }
    
        fn seed_italy_group_base(r: &mut redis::Connection) {
            delete_keys(r, &["race:italy"]);
            let _: () = r
                .xgroup_create_mkstream("race:italy", "italy_riders", "$")
                .expect("create italy group");
            let _: Option<String> = r
                .xadd("race:italy", "1692632639151-0", &[("rider", "Castilla")])
                .expect("add italy 1");
            let _: Option<String> = r
                .xadd("race:italy", "1692632647899-0", &[("rider", "Royce")])
                .expect("add italy 2");
            let _: Option<String> = r
                .xadd("race:italy", "1692632662819-0", &[("rider", "Sam-Bodden")])
                .expect("add italy 3");
            let _: Option<String> = r
                .xadd("race:italy", "1692632670501-0", &[("rider", "Prickett")])
                .expect("add italy 4");
            let _: Option<String> = r
                .xadd("race:italy", "1692632678249-0", &[("rider", "Norem")])
                .expect("add italy 5");
        }
    
        fn seed_italy_alice_pending(r: &mut redis::Connection) {
            seed_italy_group_base(r);
            let opts = StreamReadOptions::default().group("italy_riders", "Alice").count(1);
            let _: Option<StreamReadReply> = r
                .xread_options(&["race:italy"], &[">"], &opts)
                .expect("alice read pending");
        }
    
        fn seed_italy_after_ack(r: &mut redis::Connection) {
            seed_italy_alice_pending(r);
            let _: usize = r
                .xack("race:italy", "italy_riders", &["1692632639151-0"])
                .expect("ack first italy message");
        }
    
        fn seed_italy_bob_pending(r: &mut redis::Connection) {
            seed_italy_after_ack(r);
            let opts = StreamReadOptions::default().group("italy_riders", "Bob").count(2);
            let _: Option<StreamReadReply> = r
                .xread_options(&["race:italy"], &[">"], &opts)
                .expect("bob read pending");
        }
    
        fn seed_italy_info_state(r: &mut redis::Connection) {
            seed_italy_bob_pending(r);
            sleep(Duration::from_millis(5));
            let _: redis::streams::StreamClaimReply = r
                .xclaim("race:italy", "italy_riders", "Alice", 1, &["1692632647899-0"])
                .expect("alice claim");
            sleep(Duration::from_millis(5));
            let _: redis::streams::StreamClaimReply = r
                .xclaim("race:italy", "italy_riders", "Lora", 1, &["1692632662819-0"])
                .expect("lora claim");
        }
    
        fn seed_trim_stream(r: &mut redis::Connection) {
            delete_keys(r, &["mystream"]);
            for id in ["1-0", "2-0", "3-0", "4-0", "5-0", "6-0", "7-0", "8-0", "9-0", "10-0"] {
                let _: Option<String> = r
                    .xadd("mystream", id, &[("field", "value")])
                    .expect("seed mystream");
            }
        }
    
        fn run() {
            let mut r = match redis::Client::open("redis://127.0.0.1") {
                Ok(client) => match client.get_connection() {
                    Ok(conn) => conn,
                    Err(e) => {
                        println!("Failed to connect to Redis: {e}");
                        return;
                    }
                },
                Err(e) => {
                    println!("Failed to create Redis client: {e}");
                    return;
                }
            };
    
            let res1 = {
                let res: Option<String> = r
                    .xadd(
                        "race:france",
                        "*",
                        &[
                            ("rider", "Castilla"),
                            ("speed", "30.2"),
                            ("position", "1"),
                            ("location_id", "1"),
                        ],
                    )
                    .expect("xadd 1");
                res.expect("missing stream id")
            };
            println!("{res1}"); // >>> 1692632086370-0
    
            let res2 = {
                let res: Option<String> = r
                    .xadd(
                        "race:france",
                        "*",
                        &[
                            ("rider", "Norem"),
                            ("speed", "28.8"),
                            ("position", "3"),
                            ("location_id", "1"),
                        ],
                    )
                    .expect("xadd 2");
                res.expect("missing stream id")
            };
            println!("{res2}"); // >>> 1692632094485-0
    
            let res3 = {
                let res: Option<String> = r
                    .xadd(
                        "race:france",
                        "*",
                        &[
                            ("rider", "Prickett"),
                            ("speed", "29.7"),
                            ("position", "2"),
                            ("location_id", "1"),
                        ],
                    )
                    .expect("xadd 3");
                res.expect("missing stream id")
            };
            println!("{res3}"); // >>> 1692632102976-0
    
            add_france_fixed(&mut r);
            if let Ok(res) = r.xrange_count("race:france", "1692632086370-0", "+", 2) {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![
                                ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                (
                                    "location_id".to_string(),
                                    entry.get::<String>("location_id").expect("missing location_id"),
                                ),
                            ],
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("1692632086370-0", [("rider", "Castilla"), ("speed", "30.2"), ("position", "1"), ("location_id", "1")]), ("1692632094485-0", [("rider", "Norem"), ("speed", "28.8"), ("position", "3"), ("location_id", "1")])]
            }
    
            add_france_fixed(&mut r);
            let opts = StreamReadOptions::default().count(100).block(300);
            if let Ok(res) = r.xread_options(&["race:france"], &["$"], &opts) {
                let res: Option<StreamReadReply> = res;
                println!("{res:?}"); // >>> None
            }
    
            if let Ok(res) = r.xadd(
                "race:france",
                "*",
                &[
                    ("rider", "Castilla"),
                    ("speed", "29.9"),
                    ("position", "1"),
                    ("location_id", "2"),
                ],
            ) {
                let res: Option<String> = res;
                let res = res.expect("missing stream id");
                println!("{res}"); // >>> 1692632147973-0
            }
    
            add_france_fixed(&mut r);
            if let Ok(res) = r.xlen("race:france") {
                let res: usize = res;
                println!("{res}"); // >>> 4
            }
    
            delete_keys(&mut r, &["race:usa"]);
            if let Ok(res) = r.xadd("race:usa", "0-1", &[("racer", "Castilla")]) {
                let res: Option<String> = res;
                let res = res.expect("missing stream id");
                println!("{res}"); // >>> 0-1
            }
    
            if let Ok(res) = r.xadd("race:usa", "0-2", &[("racer", "Norem")]) {
                let res: Option<String> = res;
                let res = res.expect("missing stream id");
                println!("{res}"); // >>> 0-2
            }
    
            let res: redis::RedisResult<Option<String>> =
                r.xadd("race:usa", "0-1", &[("racer", "Prickett")]);
            match res {
                Ok(_) => {}
                Err(e) => {
                    let msg = e.to_string();
                    println!("{msg}");
                    // >>> An error was signalled by the server - ResponseError: The ID specified in XADD is equal or smaller than the target stream top item
                }
            }
    
            seed_usa_fixed(&mut r);
            if let Ok(res) = r.xadd("race:usa", "0-*", &[("racer", "Prickett")]) {
                let res: Option<String> = res;
                let res = res.expect("missing stream id");
                println!("{res}"); // >>> 0-3
            }
    
            add_france_fixed(&mut r);
            if let Ok(res) = r.xrange_all("race:france") {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![
                                ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                (
                                    "location_id".to_string(),
                                    entry.get::<String>("location_id").expect("missing location_id"),
                                ),
                            ],
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("1692632086370-0", [("rider", "Castilla"), ("speed", "30.2"), ("position", "1"), ("location_id", "1")]), ("1692632094485-0", [("rider", "Norem"), ("speed", "28.8"), ("position", "3"), ("location_id", "1")]), ("1692632102976-0", [("rider", "Prickett"), ("speed", "29.7"), ("position", "2"), ("location_id", "1")]), ("1692632147973-0", [("rider", "Castilla"), ("speed", "29.9"), ("position", "1"), ("location_id", "2")])]
            }
    
            add_france_fixed(&mut r);
            if let Ok(res) = r.xrange("race:france", "1692632086369", "1692632086371") {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![
                                ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                (
                                    "location_id".to_string(),
                                    entry.get::<String>("location_id").expect("missing location_id"),
                                ),
                            ],
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("1692632086370-0", [("rider", "Castilla"), ("speed", "30.2"), ("position", "1"), ("location_id", "1")])]
            }
    
            add_france_fixed(&mut r);
            if let Ok(res) = r.xrange_count("race:france", "-", "+", 2) {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![
                                ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                (
                                    "location_id".to_string(),
                                    entry.get::<String>("location_id").expect("missing location_id"),
                                ),
                            ],
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("1692632086370-0", [("rider", "Castilla"), ("speed", "30.2"), ("position", "1"), ("location_id", "1")]), ("1692632094485-0", [("rider", "Norem"), ("speed", "28.8"), ("position", "3"), ("location_id", "1")])]
            }
    
            add_france_fixed(&mut r);
            if let Ok(res) = r.xrange_count("race:france", "(1692632094485-0", "+", 2) {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![
                                ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                (
                                    "location_id".to_string(),
                                    entry.get::<String>("location_id").expect("missing location_id"),
                                ),
                            ],
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("1692632102976-0", [("rider", "Prickett"), ("speed", "29.7"), ("position", "2"), ("location_id", "1")]), ("1692632147973-0", [("rider", "Castilla"), ("speed", "29.9"), ("position", "1"), ("location_id", "2")])]
            }
    
            add_france_fixed(&mut r);
            if let Ok(res) = r.xrange_count("race:france", "(1692632147973-0", "+", 2) {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![
                                ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                (
                                    "location_id".to_string(),
                                    entry.get::<String>("location_id").expect("missing location_id"),
                                ),
                            ],
                        )
                    })
                    .collect();
                println!("{view:?}"); // >>> []
            }
    
            add_france_fixed(&mut r);
            if let Ok(res) = r.xrevrange_count("race:france", "+", "-", 1) {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![
                                ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                (
                                    "location_id".to_string(),
                                    entry.get::<String>("location_id").expect("missing location_id"),
                                ),
                            ],
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("1692632147973-0", [("rider", "Castilla"), ("speed", "29.9"), ("position", "1"), ("location_id", "2")])]
            }
    
            add_france_fixed(&mut r);
            let opts = StreamReadOptions::default().count(2);
            if let Ok(res) = r.xread_options(&["race:france"], &["0"], &opts) {
                let res: Option<StreamReadReply> = res;
                let reply = res.expect("xread should return data");
                let view: Vec<_> = reply
                    .keys
                    .iter()
                    .map(|stream| {
                        (
                            stream.key.clone(),
                            stream
                                .ids
                                .iter()
                                .map(|entry| {
                                    (
                                        entry.id.clone(),
                                        vec![
                                            ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                            ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                            ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                            (
                                                "location_id".to_string(),
                                                entry.get::<String>("location_id").expect("missing location_id"),
                                            ),
                                        ],
                                    )
                                })
                                .collect::<Vec<_>>(),
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("race:france", [("1692632086370-0", [("rider", "Castilla"), ("speed", "30.2"), ("position", "1"), ("location_id", "1")]), ("1692632094485-0", [("rider", "Norem"), ("speed", "28.8"), ("position", "3"), ("location_id", "1")])])]
            }
    
            add_france_fixed(&mut r);
            if let Ok(res) = r.xgroup_create("race:france", "france_riders", "$") {
                let res: () = res;
                let _ = res;
                println!("OK"); // >>> OK
            }
    
            delete_keys(&mut r, &["race:italy"]);
            if let Ok(res) = r.xgroup_create_mkstream("race:italy", "italy_riders", "$") {
                let res: () = res;
                let _ = res;
                println!("OK"); // >>> OK
            }
    
            delete_keys(&mut r, &["race:italy"]);
            let _: () = r
                .xgroup_create_mkstream("race:italy", "italy_riders", "$")
                .expect("create italy group");
            let italy_1: Option<String> = r
                .xadd("race:italy", "1692632639151-0", &[("rider", "Castilla")])
                .expect("italy1");
            let italy_1 = italy_1.expect("missing stream id");
            println!("{italy_1}"); // >>> 1692632639151-0
            let italy_2: Option<String> = r
                .xadd("race:italy", "1692632647899-0", &[("rider", "Royce")])
                .expect("italy2");
            let italy_2 = italy_2.expect("missing stream id");
            println!("{italy_2}"); // >>> 1692632647899-0
            let italy_3: Option<String> = r
                .xadd("race:italy", "1692632662819-0", &[("rider", "Sam-Bodden")])
                .expect("italy3");
            let italy_3 = italy_3.expect("missing stream id");
            println!("{italy_3}"); // >>> 1692632662819-0
            let italy_4: Option<String> = r
                .xadd("race:italy", "1692632670501-0", &[("rider", "Prickett")])
                .expect("italy4");
            let italy_4 = italy_4.expect("missing stream id");
            println!("{italy_4}"); // >>> 1692632670501-0
            let italy_5: Option<String> = r
                .xadd("race:italy", "1692632678249-0", &[("rider", "Norem")])
                .expect("italy5");
            let italy_5 = italy_5.expect("missing stream id");
            println!("{italy_5}"); // >>> 1692632678249-0
    
            let opts = StreamReadOptions::default().group("italy_riders", "Alice").count(1);
            if let Ok(res) = r.xread_options(&["race:italy"], &[">"], &opts) {
                let res: Option<StreamReadReply> = res;
                let view: Vec<_> = res
                    .expect("xgroup read should return data")
                    .keys
                    .iter()
                    .map(|stream| {
                        (
                            stream.key.clone(),
                            stream
                                .ids
                                .iter()
                                .map(|entry| {
                                    (
                                        entry.id.clone(),
                                        vec![(
                                            "rider".to_string(),
                                            entry.get::<String>("rider").expect("missing rider"),
                                        )],
                                    )
                                })
                                .collect::<Vec<_>>(),
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("race:italy", [("1692632639151-0", [("rider", "Castilla")])])]
            }
    
            seed_italy_alice_pending(&mut r);
            let opts = StreamReadOptions::default().group("italy_riders", "Alice");
            if let Ok(res) = r.xread_options(&["race:italy"], &["0"], &opts) {
                let res: Option<StreamReadReply> = res;
                let view: Vec<_> = res
                    .expect("xgroup history")
                    .keys
                    .iter()
                    .map(|stream| {
                        (
                            stream.key.clone(),
                            stream
                                .ids
                                .iter()
                                .map(|entry| {
                                    (
                                        entry.id.clone(),
                                        vec![(
                                            "rider".to_string(),
                                            entry.get::<String>("rider").expect("missing rider"),
                                        )],
                                    )
                                })
                                .collect::<Vec<_>>(),
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("race:italy", [("1692632639151-0", [("rider", "Castilla")])])]
            }
    
            seed_italy_alice_pending(&mut r);
            if let Ok(res) = r.xack("race:italy", "italy_riders", &["1692632639151-0"]) {
                let res: usize = res;
                println!("{res}"); // >>> 1
            }
    
            let opts = StreamReadOptions::default().group("italy_riders", "Alice");
            if let Ok(res) = r.xread_options(&["race:italy"], &["0"], &opts) {
                let res: Option<StreamReadReply> = res;
                let view: Vec<_> = res
                    .expect("xgroup history")
                    .keys
                    .iter()
                    .map(|stream| {
                        (
                            stream.key.clone(),
                            stream
                                .ids
                                .iter()
                                .map(|entry| {
                                    (
                                        entry.id.clone(),
                                        vec![(
                                            "rider".to_string(),
                                            entry.get::<String>("rider").expect("missing rider"),
                                        )],
                                    )
                                })
                                .collect::<Vec<_>>(),
                        )
                    })
                    .collect();
                println!("{view:?}"); // >>> [("race:italy", [])]
            }
    
            seed_italy_after_ack(&mut r);
            let opts = StreamReadOptions::default().group("italy_riders", "Bob").count(2);
            if let Ok(res) = r.xread_options(&["race:italy"], &[">"], &opts) {
                let res: Option<StreamReadReply> = res;
                let view: Vec<_> = res
                    .expect("bob should receive data")
                    .keys
                    .iter()
                    .map(|stream| {
                        (
                            stream.key.clone(),
                            stream
                                .ids
                                .iter()
                                .map(|entry| {
                                    (
                                        entry.id.clone(),
                                        vec![(
                                            "rider".to_string(),
                                            entry.get::<String>("rider").expect("missing rider"),
                                        )],
                                    )
                                })
                                .collect::<Vec<_>>(),
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("race:italy", [("1692632647899-0", [("rider", "Royce")]), ("1692632662819-0", [("rider", "Sam-Bodden")])])]
            }
    
            seed_italy_bob_pending(&mut r);
            if let Ok(res) = r.xpending("race:italy", "italy_riders") {
                let res: StreamPendingReply = res;
                let view = match res {
                    StreamPendingReply::Empty => None,
                    StreamPendingReply::Data(data) => Some((
                        data.count,
                        data.start_id.clone(),
                        data.end_id.clone(),
                        data.consumers
                            .iter()
                            .map(|consumer| (consumer.name.clone(), consumer.pending))
                            .collect::<Vec<_>>(),
                    )),
                }
                .expect("pending summary");
                println!("{view:?}");
                // >>> (2, "1692632647899-0", "1692632662819-0", [("Bob", 2)])
            }
    
            seed_italy_bob_pending(&mut r);
            sleep(Duration::from_millis(5));
            if let Ok(res) = r.xpending_count("race:italy", "italy_riders", "-", "+", 10) {
                let res: StreamPendingCountReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            entry.consumer.clone(),
                            entry.last_delivered_ms,
                            entry.times_delivered,
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("1692632647899-0", "Bob", 5, 1), ("1692632662819-0", "Bob", 5, 1)]
            }
    
            seed_italy_bob_pending(&mut r);
            if let Ok(res) = r.xrange("race:italy", "1692632647899-0", "1692632647899-0") {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![(
                                "rider".to_string(),
                                entry.get::<String>("rider").expect("missing rider"),
                            )],
                        )
                    })
                    .collect();
                println!("{view:?}"); // >>> [("1692632647899-0", [("rider", "Royce")])]
            }
    
            seed_italy_bob_pending(&mut r);
            sleep(Duration::from_millis(5));
            if let Ok(res) = r.xclaim("race:italy", "italy_riders", "Alice", 1, &["1692632647899-0"]) {
                let res: redis::streams::StreamClaimReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![(
                                "rider".to_string(),
                                entry.get::<String>("rider").expect("missing rider"),
                            )],
                        )
                    })
                    .collect();
                println!("{view:?}"); // >>> [("1692632647899-0", [("rider", "Royce")])]
            }
    
            seed_italy_bob_pending(&mut r);
            sleep(Duration::from_millis(5));
            let opts = StreamAutoClaimOptions::default().count(1);
            if let Ok(res) = r.xautoclaim_options("race:italy", "italy_riders", "Alice", 1, "0-0", opts) {
                let res: redis::streams::StreamAutoClaimReply = res;
                let claimed: Vec<_> = res
                    .claimed
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![(
                                "rider".to_string(),
                                entry.get::<String>("rider").expect("missing rider"),
                            )],
                        )
                    })
                    .collect();
                println!("{:?}", (res.next_stream_id.clone(), &claimed));
                // >>> ("1692632662819-0", [("1692632647899-0", [("rider", "Royce")])])
            }
    
            seed_italy_bob_pending(&mut r);
            sleep(Duration::from_millis(5));
            let first_opts = StreamAutoClaimOptions::default().count(1);
            let _: redis::streams::StreamAutoClaimReply = r
                .xautoclaim_options("race:italy", "italy_riders", "Alice", 1, "0-0", first_opts)
                .expect("first autoclaim");
            let next_opts = StreamAutoClaimOptions::default().count(1);
            if let Ok(res) = r.xautoclaim_options(
                "race:italy",
                "italy_riders",
                "Lora",
                1,
                "(1692632647899-0",
                next_opts,
            ) {
                let res: redis::streams::StreamAutoClaimReply = res;
                let claimed: Vec<_> = res
                    .claimed
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![(
                                "rider".to_string(),
                                entry.get::<String>("rider").expect("missing rider"),
                            )],
                        )
                    })
                    .collect();
                println!("{:?}", (res.next_stream_id.clone(), &claimed));
                // >>> ("0-0", [("1692632662819-0", [("rider", "Sam-Bodden")])])
            }
    
            seed_italy_info_state(&mut r);
            if let Ok(res) = r.xinfo_stream("race:italy") {
                let res: StreamInfoStreamReply = res;
                let view = (
                    res.length,
                    res.radix_tree_keys,
                    res.groups,
                    res.last_generated_id.clone(),
                    res.first_entry.id.clone(),
                    res.last_entry.id.clone(),
                );
                println!("{view:?}");
                // >>> (5, 1, 1, "1692632678249-0", "1692632639151-0", "1692632678249-0")
            }
    
            seed_italy_info_state(&mut r);
            if let Ok(res) = r.xinfo_groups("race:italy") {
                let res: StreamInfoGroupsReply = res;
                let view: Vec<_> = res
                    .groups
                    .iter()
                    .map(|group| {
                        (
                            group.name.clone(),
                            group.consumers,
                            group.pending,
                            group.last_delivered_id.clone(),
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("italy_riders", 3, 2, "1692632662819-0")]
            }
    
            seed_italy_info_state(&mut r);
            if let Ok(res) = r.xinfo_consumers("race:italy", "italy_riders") {
                let res: StreamInfoConsumersReply = res;
                let mut view: Vec<_> = res
                    .consumers
                    .iter()
                    .map(|consumer| (consumer.name.clone(), consumer.pending, consumer.idle))
                    .collect();
                view.sort_by(|a, b| a.0.cmp(&b.0));
                println!("{view:?}");
                // >>> [("Alice", 1, 5), ("Bob", 0, 5), ("Lora", 1, 5)]
            }
    
            delete_keys(&mut r, &["race:italy"]);
            let max1: Option<String> = r
                .xadd_maxlen("race:italy", StreamMaxlen::Equals(2), "1-0", &[("rider", "Jones")])
                .expect("maxlen add 1");
            let max1 = max1.expect("missing stream id");
            println!("{max1}"); // >>> 1-0
            let max2: Option<String> = r
                .xadd_maxlen("race:italy", StreamMaxlen::Equals(2), "2-0", &[("rider", "Wood")])
                .expect("maxlen add 2");
            let max2 = max2.expect("missing stream id");
            println!("{max2}"); // >>> 2-0
            let max3: Option<String> = r
                .xadd_maxlen("race:italy", StreamMaxlen::Equals(2), "3-0", &[("rider", "Henshaw")])
                .expect("maxlen add 3");
            let max3 = max3.expect("missing stream id");
            println!("{max3}"); // >>> 3-0
    
            if let Ok(res) = r.xlen("race:italy") {
                let res: usize = res;
                println!("{res}"); // >>> 2
            }
    
            if let Ok(res) = r.xrange_all("race:italy") {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![(
                                "rider".to_string(),
                                entry.get::<String>("rider").expect("missing rider"),
                            )],
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("2-0", [("rider", "Wood")]), ("3-0", [("rider", "Henshaw")])]
            }
    
            delete_keys(&mut r, &["race:italy"]);
            let _: Option<String> = r.xadd("race:italy", "1-0", &[("rider", "Wood")]).expect("trim seed 1");
            let _: Option<String> = r.xadd("race:italy", "2-0", &[("rider", "Henshaw")]).expect("trim seed 2");
            if let Ok(res) = r.xtrim("race:italy", StreamMaxlen::Equals(10)) {
                let res: usize = res;
                println!("{res}"); // >>> 0
            }
    
            seed_trim_stream(&mut r);
            if let Ok(res) = r.xtrim_options(
                "mystream",
                &StreamTrimOptions::maxlen(StreamTrimmingMode::Approx, 10),
            ) {
                let res: usize = res;
                println!("{res}"); // >>> 0
            }
    
            delete_keys(&mut r, &["race:italy"]);
            let _: Option<String> = r.xadd("race:italy", "2-0", &[("rider", "Wood")]).expect("xdel seed 1");
            let _: Option<String> = r.xadd("race:italy", "3-0", &[("rider", "Henshaw")]).expect("xdel seed 2");
            if let Ok(res) = r.xrange_count("race:italy", "-", "+", 2) {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![(
                                "rider".to_string(),
                                entry.get::<String>("rider").expect("missing rider"),
                            )],
                        )
                    })
                    .collect();
                println!("{view:?}"); // >>> [("2-0", [("rider", "Wood")]), ("3-0", [("rider", "Henshaw")])]
            }
    
            if let Ok(res) = r.xdel("race:italy", &["3-0"]) {
                let res: usize = res;
                println!("{res}"); // >>> 1
            }
    
            if let Ok(res) = r.xrange_count("race:italy", "-", "+", 2) {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![(
                                "rider".to_string(),
                                entry.get::<String>("rider").expect("missing rider"),
                            )],
                        )
                    })
                    .collect();
                println!("{view:?}"); // >>> [("2-0", [("rider", "Wood")])]
            }
        }
    }
    
    mod tests {
        use redis::{
            streams::{
                StreamAutoClaimOptions, StreamInfoConsumersReply, StreamInfoGroupsReply,
                StreamInfoStreamReply, StreamMaxlen, StreamPendingCountReply, StreamPendingReply,
                StreamRangeReply, StreamReadOptions, StreamReadReply, StreamTrimmingMode, StreamTrimOptions,
            },
            AsyncCommands,
        };
        use tokio::time::{sleep, Duration};
    
        async fn delete_keys(r: &mut redis::aio::MultiplexedConnection, keys: &[&str]) {
            let _: usize = r.del(keys).await.unwrap_or(0);
        }
    
        async fn add_france_fixed(r: &mut redis::aio::MultiplexedConnection) {
            delete_keys(r, &["race:france"]).await;
            let _: Option<String> = r
                .xadd(
                    "race:france",
                    "1692632086370-0",
                    &[
                        ("rider", "Castilla"),
                        ("speed", "30.2"),
                        ("position", "1"),
                        ("location_id", "1"),
                    ],
                )
                .await
                .expect("add france 1");
            let _: Option<String> = r
                .xadd(
                    "race:france",
                    "1692632094485-0",
                    &[
                        ("rider", "Norem"),
                        ("speed", "28.8"),
                        ("position", "3"),
                        ("location_id", "1"),
                    ],
                )
                .await
                .expect("add france 2");
            let _: Option<String> = r
                .xadd(
                    "race:france",
                    "1692632102976-0",
                    &[
                        ("rider", "Prickett"),
                        ("speed", "29.7"),
                        ("position", "2"),
                        ("location_id", "1"),
                    ],
                )
                .await
                .expect("add france 3");
            let _: Option<String> = r
                .xadd(
                    "race:france",
                    "1692632147973-0",
                    &[
                        ("rider", "Castilla"),
                        ("speed", "29.9"),
                        ("position", "1"),
                        ("location_id", "2"),
                    ],
                )
                .await
                .expect("add france 4");
        }
    
        async fn seed_usa_fixed(r: &mut redis::aio::MultiplexedConnection) {
            delete_keys(r, &["race:usa"]).await;
            let _: Option<String> = r
                .xadd("race:usa", "0-1", &[("racer", "Castilla")])
                .await
                .expect("add usa 1");
            let _: Option<String> = r
                .xadd("race:usa", "0-2", &[("racer", "Norem")])
                .await
                .expect("add usa 2");
        }
    
        async fn seed_italy_group_base(r: &mut redis::aio::MultiplexedConnection) {
            delete_keys(r, &["race:italy"]).await;
            let _: () = r
                .xgroup_create_mkstream("race:italy", "italy_riders", "$")
                .await
                .expect("create italy group");
            let _: Option<String> = r
                .xadd("race:italy", "1692632639151-0", &[("rider", "Castilla")])
                .await
                .expect("add italy 1");
            let _: Option<String> = r
                .xadd("race:italy", "1692632647899-0", &[("rider", "Royce")])
                .await
                .expect("add italy 2");
            let _: Option<String> = r
                .xadd("race:italy", "1692632662819-0", &[("rider", "Sam-Bodden")])
                .await
                .expect("add italy 3");
            let _: Option<String> = r
                .xadd("race:italy", "1692632670501-0", &[("rider", "Prickett")])
                .await
                .expect("add italy 4");
            let _: Option<String> = r
                .xadd("race:italy", "1692632678249-0", &[("rider", "Norem")])
                .await
                .expect("add italy 5");
        }
    
        async fn seed_italy_alice_pending(r: &mut redis::aio::MultiplexedConnection) {
            seed_italy_group_base(r).await;
            let opts = StreamReadOptions::default().group("italy_riders", "Alice").count(1);
            let _: Option<StreamReadReply> = r
                .xread_options(&["race:italy"], &[">"], &opts)
                .await
                .expect("alice read pending");
        }
    
        async fn seed_italy_after_ack(r: &mut redis::aio::MultiplexedConnection) {
            seed_italy_alice_pending(r).await;
            let _: usize = r
                .xack("race:italy", "italy_riders", &["1692632639151-0"])
                .await
                .expect("ack first italy message");
        }
    
        async fn seed_italy_bob_pending(r: &mut redis::aio::MultiplexedConnection) {
            seed_italy_after_ack(r).await;
            let opts = StreamReadOptions::default().group("italy_riders", "Bob").count(2);
            let _: Option<StreamReadReply> = r
                .xread_options(&["race:italy"], &[">"], &opts)
                .await
                .expect("bob read pending");
        }
    
        async fn seed_italy_info_state(r: &mut redis::aio::MultiplexedConnection) {
            seed_italy_bob_pending(r).await;
            sleep(Duration::from_millis(5)).await;
            let _: redis::streams::StreamClaimReply = r
                .xclaim("race:italy", "italy_riders", "Alice", 1, &["1692632647899-0"])
                .await
                .expect("alice claim");
            sleep(Duration::from_millis(5)).await;
            let _: redis::streams::StreamClaimReply = r
                .xclaim("race:italy", "italy_riders", "Lora", 1, &["1692632662819-0"])
                .await
                .expect("lora claim");
        }
    
        async fn seed_trim_stream(r: &mut redis::aio::MultiplexedConnection) {
            delete_keys(r, &["mystream"]).await;
            for id in ["1-0", "2-0", "3-0", "4-0", "5-0", "6-0", "7-0", "8-0", "9-0", "10-0"] {
                let _: Option<String> = r
                    .xadd("mystream", id, &[("field", "value")])
                    .await
                    .expect("seed mystream");
            }
        }
    
        async fn run() {
            let mut r = match redis::Client::open("redis://127.0.0.1") {
                Ok(client) => match client.get_multiplexed_async_connection().await {
                    Ok(conn) => conn,
                    Err(e) => {
                        println!("Failed to connect to Redis: {e}");
                        return;
                    }
                },
                Err(e) => {
                    println!("Failed to create Redis client: {e}");
                    return;
                }
            };
    
            let res1: Option<String> = r
                .xadd(
                    "race:france",
                    "*",
                    &[
                        ("rider", "Castilla"),
                        ("speed", "30.2"),
                        ("position", "1"),
                        ("location_id", "1"),
                    ],
                )
                .await
                .expect("xadd 1");
            let res1 = res1.expect("missing stream id");
            println!("{res1}"); // >>> 1692632086370-0
    
            let res2: Option<String> = r
                .xadd(
                    "race:france",
                    "*",
                    &[
                        ("rider", "Norem"),
                        ("speed", "28.8"),
                        ("position", "3"),
                        ("location_id", "1"),
                    ],
                )
                .await
                .expect("xadd 2");
            let res2 = res2.expect("missing stream id");
            println!("{res2}"); // >>> 1692632094485-0
    
            let res3: Option<String> = r
                .xadd(
                    "race:france",
                    "*",
                    &[
                        ("rider", "Prickett"),
                        ("speed", "29.7"),
                        ("position", "2"),
                        ("location_id", "1"),
                    ],
                )
                .await
                .expect("xadd 3");
            let res3 = res3.expect("missing stream id");
            println!("{res3}"); // >>> 1692632102976-0
    
            add_france_fixed(&mut r).await;
            if let Ok(res) = r.xrange_count("race:france", "1692632086370-0", "+", 2).await {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![
                                ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                (
                                    "location_id".to_string(),
                                    entry.get::<String>("location_id").expect("missing location_id"),
                                ),
                            ],
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("1692632086370-0", [("rider", "Castilla"), ("speed", "30.2"), ("position", "1"), ("location_id", "1")]), ("1692632094485-0", [("rider", "Norem"), ("speed", "28.8"), ("position", "3"), ("location_id", "1")])]
            }
    
            add_france_fixed(&mut r).await;
            let opts = StreamReadOptions::default().count(100).block(300);
            if let Ok(res) = r.xread_options(&["race:france"], &["$"], &opts).await {
                let res: Option<StreamReadReply> = res;
                println!("{res:?}"); // >>> None
            }
    
            if let Ok(res) = r
                .xadd(
                    "race:france",
                    "*",
                    &[
                        ("rider", "Castilla"),
                        ("speed", "29.9"),
                        ("position", "1"),
                        ("location_id", "2"),
                    ],
                )
                .await
            {
                let res: Option<String> = res;
                let res = res.expect("missing stream id");
                println!("{res}"); // >>> 1692632147973-0
            }
    
            add_france_fixed(&mut r).await;
            if let Ok(res) = r.xlen("race:france").await {
                let res: usize = res;
                println!("{res}"); // >>> 4
            }
    
            delete_keys(&mut r, &["race:usa"]).await;
            if let Ok(res) = r.xadd("race:usa", "0-1", &[("racer", "Castilla")]).await {
                let res: Option<String> = res;
                let res = res.expect("missing stream id");
                println!("{res}"); // >>> 0-1
            }
            if let Ok(res) = r.xadd("race:usa", "0-2", &[("racer", "Norem")]).await {
                let res: Option<String> = res;
                let res = res.expect("missing stream id");
                println!("{res}"); // >>> 0-2
            }
    
            let res: redis::RedisResult<Option<String>> =
                r.xadd("race:usa", "0-1", &[("racer", "Prickett")]).await;
            match res {
                Ok(_) => {}
                Err(e) => {
                    let msg = e.to_string();
                    println!("{msg}");
                    // >>> An error was signalled by the server - ResponseError: The ID specified in XADD is equal or smaller than the target stream top item
                }
            }
    
            seed_usa_fixed(&mut r).await;
            if let Ok(res) = r.xadd("race:usa", "0-*", &[("racer", "Prickett")]).await {
                let res: Option<String> = res;
                let res = res.expect("missing stream id");
                println!("{res}"); // >>> 0-3
            }
    
            add_france_fixed(&mut r).await;
            if let Ok(res) = r.xrange_all("race:france").await {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![
                                ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                (
                                    "location_id".to_string(),
                                    entry.get::<String>("location_id").expect("missing location_id"),
                                ),
                            ],
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("1692632086370-0", [("rider", "Castilla"), ("speed", "30.2"), ("position", "1"), ("location_id", "1")]), ("1692632094485-0", [("rider", "Norem"), ("speed", "28.8"), ("position", "3"), ("location_id", "1")]), ("1692632102976-0", [("rider", "Prickett"), ("speed", "29.7"), ("position", "2"), ("location_id", "1")]), ("1692632147973-0", [("rider", "Castilla"), ("speed", "29.9"), ("position", "1"), ("location_id", "2")])]
            }
    
            add_france_fixed(&mut r).await;
            if let Ok(res) = r.xrange("race:france", "1692632086369", "1692632086371").await {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![
                                ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                (
                                    "location_id".to_string(),
                                    entry.get::<String>("location_id").expect("missing location_id"),
                                ),
                            ],
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("1692632086370-0", [("rider", "Castilla"), ("speed", "30.2"), ("position", "1"), ("location_id", "1")])]
            }
    
            add_france_fixed(&mut r).await;
            if let Ok(res) = r.xrange_count("race:france", "-", "+", 2).await {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![
                                ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                (
                                    "location_id".to_string(),
                                    entry.get::<String>("location_id").expect("missing location_id"),
                                ),
                            ],
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("1692632086370-0", [("rider", "Castilla"), ("speed", "30.2"), ("position", "1"), ("location_id", "1")]), ("1692632094485-0", [("rider", "Norem"), ("speed", "28.8"), ("position", "3"), ("location_id", "1")])]
            }
    
            add_france_fixed(&mut r).await;
            if let Ok(res) = r.xrange_count("race:france", "(1692632094485-0", "+", 2).await {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![
                                ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                (
                                    "location_id".to_string(),
                                    entry.get::<String>("location_id").expect("missing location_id"),
                                ),
                            ],
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("1692632102976-0", [("rider", "Prickett"), ("speed", "29.7"), ("position", "2"), ("location_id", "1")]), ("1692632147973-0", [("rider", "Castilla"), ("speed", "29.9"), ("position", "1"), ("location_id", "2")])]
            }
    
            add_france_fixed(&mut r).await;
            if let Ok(res) = r.xrange_count("race:france", "(1692632147973-0", "+", 2).await {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![
                                ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                (
                                    "location_id".to_string(),
                                    entry.get::<String>("location_id").expect("missing location_id"),
                                ),
                            ],
                        )
                    })
                    .collect();
                println!("{view:?}"); // >>> []
            }
    
            add_france_fixed(&mut r).await;
            if let Ok(res) = r.xrevrange_count("race:france", "+", "-", 1).await {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![
                                ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                (
                                    "location_id".to_string(),
                                    entry.get::<String>("location_id").expect("missing location_id"),
                                ),
                            ],
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("1692632147973-0", [("rider", "Castilla"), ("speed", "29.9"), ("position", "1"), ("location_id", "2")])]
            }
    
            add_france_fixed(&mut r).await;
            let opts = StreamReadOptions::default().count(2);
            if let Ok(res) = r.xread_options(&["race:france"], &["0"], &opts).await {
                let res: Option<StreamReadReply> = res;
                let view: Vec<_> = res
                    .expect("xread should return data")
                    .keys
                    .iter()
                    .map(|stream| {
                        (
                            stream.key.clone(),
                            stream
                                .ids
                                .iter()
                                .map(|entry| {
                                    (
                                        entry.id.clone(),
                                        vec![
                                            ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                            ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                            ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                            (
                                                "location_id".to_string(),
                                                entry.get::<String>("location_id").expect("missing location_id"),
                                            ),
                                        ],
                                    )
                                })
                                .collect::<Vec<_>>(),
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("race:france", [("1692632086370-0", [("rider", "Castilla"), ("speed", "30.2"), ("position", "1"), ("location_id", "1")]), ("1692632094485-0", [("rider", "Norem"), ("speed", "28.8"), ("position", "3"), ("location_id", "1")])])]
            }
    
            add_france_fixed(&mut r).await;
            if let Ok(res) = r.xgroup_create("race:france", "france_riders", "$").await {
                let res: () = res;
                let _ = res;
                println!("OK"); // >>> OK
            }
    
            delete_keys(&mut r, &["race:italy"]).await;
            if let Ok(res) = r.xgroup_create_mkstream("race:italy", "italy_riders", "$").await {
                let res: () = res;
                let _ = res;
                println!("OK"); // >>> OK
            }
    
            delete_keys(&mut r, &["race:italy"]).await;
            let _: () = r
                .xgroup_create_mkstream("race:italy", "italy_riders", "$")
                .await
                .expect("create italy group");
            let italy_1: Option<String> = r
                .xadd("race:italy", "1692632639151-0", &[("rider", "Castilla")])
                .await
                .expect("italy1");
            let italy_1 = italy_1.expect("missing stream id");
            println!("{italy_1}"); // >>> 1692632639151-0
            let italy_2: Option<String> = r
                .xadd("race:italy", "1692632647899-0", &[("rider", "Royce")])
                .await
                .expect("italy2");
            let italy_2 = italy_2.expect("missing stream id");
            println!("{italy_2}"); // >>> 1692632647899-0
            let italy_3: Option<String> = r
                .xadd("race:italy", "1692632662819-0", &[("rider", "Sam-Bodden")])
                .await
                .expect("italy3");
            let italy_3 = italy_3.expect("missing stream id");
            println!("{italy_3}"); // >>> 1692632662819-0
            let italy_4: Option<String> = r
                .xadd("race:italy", "1692632670501-0", &[("rider", "Prickett")])
                .await
                .expect("italy4");
            let italy_4 = italy_4.expect("missing stream id");
            println!("{italy_4}"); // >>> 1692632670501-0
            let italy_5: Option<String> = r
                .xadd("race:italy", "1692632678249-0", &[("rider", "Norem")])
                .await
                .expect("italy5");
            let italy_5 = italy_5.expect("missing stream id");
            println!("{italy_5}"); // >>> 1692632678249-0
            let opts = StreamReadOptions::default().group("italy_riders", "Alice").count(1);
            if let Ok(res) = r.xread_options(&["race:italy"], &[">"], &opts).await {
                let res: Option<StreamReadReply> = res;
                let view: Vec<_> = res
                    .expect("xgroup read should return data")
                    .keys
                    .iter()
                    .map(|stream| {
                        (
                            stream.key.clone(),
                            stream
                                .ids
                                .iter()
                                .map(|entry| {
                                    (
                                        entry.id.clone(),
                                        vec![(
                                            "rider".to_string(),
                                            entry.get::<String>("rider").expect("missing rider"),
                                        )],
                                    )
                                })
                                .collect::<Vec<_>>(),
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("race:italy", [("1692632639151-0", [("rider", "Castilla")])])]
            }
    
            seed_italy_alice_pending(&mut r).await;
            let opts = StreamReadOptions::default().group("italy_riders", "Alice");
            if let Ok(res) = r.xread_options(&["race:italy"], &["0"], &opts).await {
                let res: Option<StreamReadReply> = res;
                let view: Vec<_> = res
                    .expect("xgroup history")
                    .keys
                    .iter()
                    .map(|stream| {
                        (
                            stream.key.clone(),
                            stream
                                .ids
                                .iter()
                                .map(|entry| {
                                    (
                                        entry.id.clone(),
                                        vec![(
                                            "rider".to_string(),
                                            entry.get::<String>("rider").expect("missing rider"),
                                        )],
                                    )
                                })
                                .collect::<Vec<_>>(),
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("race:italy", [("1692632639151-0", [("rider", "Castilla")])])]
            }
    
            seed_italy_alice_pending(&mut r).await;
            if let Ok(res) = r.xack("race:italy", "italy_riders", &["1692632639151-0"]).await {
                let res: usize = res;
                println!("{res}"); // >>> 1
            }
            let opts = StreamReadOptions::default().group("italy_riders", "Alice");
            if let Ok(res) = r.xread_options(&["race:italy"], &["0"], &opts).await {
                let res: Option<StreamReadReply> = res;
                let view: Vec<_> = res
                    .expect("xgroup history")
                    .keys
                    .iter()
                    .map(|stream| {
                        (
                            stream.key.clone(),
                            stream
                                .ids
                                .iter()
                                .map(|entry| {
                                    (
                                        entry.id.clone(),
                                        vec![(
                                            "rider".to_string(),
                                            entry.get::<String>("rider").expect("missing rider"),
                                        )],
                                    )
                                })
                                .collect::<Vec<_>>(),
                        )
                    })
                    .collect();
                println!("{view:?}"); // >>> [("race:italy", [])]
            }
    
            seed_italy_after_ack(&mut r).await;
            let opts = StreamReadOptions::default().group("italy_riders", "Bob").count(2);
            if let Ok(res) = r.xread_options(&["race:italy"], &[">"], &opts).await {
                let res: Option<StreamReadReply> = res;
                let view: Vec<_> = res
                    .expect("bob should receive data")
                    .keys
                    .iter()
                    .map(|stream| {
                        (
                            stream.key.clone(),
                            stream
                                .ids
                                .iter()
                                .map(|entry| {
                                    (
                                        entry.id.clone(),
                                        vec![(
                                            "rider".to_string(),
                                            entry.get::<String>("rider").expect("missing rider"),
                                        )],
                                    )
                                })
                                .collect::<Vec<_>>(),
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("race:italy", [("1692632647899-0", [("rider", "Royce")]), ("1692632662819-0", [("rider", "Sam-Bodden")])])]
            }
    
            seed_italy_bob_pending(&mut r).await;
            if let Ok(res) = r.xpending("race:italy", "italy_riders").await {
                let res: StreamPendingReply = res;
                let view = match res {
                    StreamPendingReply::Empty => None,
                    StreamPendingReply::Data(data) => Some((
                        data.count,
                        data.start_id.clone(),
                        data.end_id.clone(),
                        data.consumers
                            .iter()
                            .map(|consumer| (consumer.name.clone(), consumer.pending))
                            .collect::<Vec<_>>(),
                    )),
                }
                .expect("pending summary");
                println!("{view:?}");
                // >>> (2, "1692632647899-0", "1692632662819-0", [("Bob", 2)])
            }
    
            seed_italy_bob_pending(&mut r).await;
            sleep(Duration::from_millis(5)).await;
            if let Ok(res) = r.xpending_count("race:italy", "italy_riders", "-", "+", 10).await {
                let res: StreamPendingCountReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            entry.consumer.clone(),
                            entry.last_delivered_ms,
                            entry.times_delivered,
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("1692632647899-0", "Bob", 5, 1), ("1692632662819-0", "Bob", 5, 1)]
            }
    
            seed_italy_bob_pending(&mut r).await;
            if let Ok(res) = r.xrange("race:italy", "1692632647899-0", "1692632647899-0").await {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![(
                                "rider".to_string(),
                                entry.get::<String>("rider").expect("missing rider"),
                            )],
                        )
                    })
                    .collect();
                println!("{view:?}"); // >>> [("1692632647899-0", [("rider", "Royce")])]
            }
    
            seed_italy_bob_pending(&mut r).await;
            sleep(Duration::from_millis(5)).await;
            if let Ok(res) = r
                .xclaim("race:italy", "italy_riders", "Alice", 1, &["1692632647899-0"])
                .await
            {
                let res: redis::streams::StreamClaimReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![(
                                "rider".to_string(),
                                entry.get::<String>("rider").expect("missing rider"),
                            )],
                        )
                    })
                    .collect();
                println!("{view:?}"); // >>> [("1692632647899-0", [("rider", "Royce")])]
            }
    
            seed_italy_bob_pending(&mut r).await;
            sleep(Duration::from_millis(5)).await;
            let opts = StreamAutoClaimOptions::default().count(1);
            if let Ok(res) = r
                .xautoclaim_options("race:italy", "italy_riders", "Alice", 1, "0-0", opts)
                .await
            {
                let res: redis::streams::StreamAutoClaimReply = res;
                let claimed: Vec<_> = res
                    .claimed
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![(
                                "rider".to_string(),
                                entry.get::<String>("rider").expect("missing rider"),
                            )],
                        )
                    })
                    .collect();
                println!("{:?}", (res.next_stream_id.clone(), &claimed));
                // >>> ("1692632662819-0", [("1692632647899-0", [("rider", "Royce")])])
            }
    
            seed_italy_bob_pending(&mut r).await;
            sleep(Duration::from_millis(5)).await;
            let first_opts = StreamAutoClaimOptions::default().count(1);
            let _: redis::streams::StreamAutoClaimReply = r
                .xautoclaim_options("race:italy", "italy_riders", "Alice", 1, "0-0", first_opts)
                .await
                .expect("first autoclaim");
            let next_opts = StreamAutoClaimOptions::default().count(1);
            if let Ok(res) = r
                .xautoclaim_options(
                    "race:italy",
                    "italy_riders",
                    "Lora",
                    1,
                    "(1692632647899-0",
                    next_opts,
                )
                .await
            {
                let res: redis::streams::StreamAutoClaimReply = res;
                let claimed: Vec<_> = res
                    .claimed
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![(
                                "rider".to_string(),
                                entry.get::<String>("rider").expect("missing rider"),
                            )],
                        )
                    })
                    .collect();
                println!("{:?}", (res.next_stream_id.clone(), &claimed));
                // >>> ("0-0", [("1692632662819-0", [("rider", "Sam-Bodden")])])
            }
    
            seed_italy_info_state(&mut r).await;
            if let Ok(res) = r.xinfo_stream("race:italy").await {
                let res: StreamInfoStreamReply = res;
                let view = (
                    res.length,
                    res.radix_tree_keys,
                    res.groups,
                    res.last_generated_id.clone(),
                    res.first_entry.id.clone(),
                    res.last_entry.id.clone(),
                );
                println!("{view:?}");
                // >>> (5, 1, 1, "1692632678249-0", "1692632639151-0", "1692632678249-0")
            }
    
            seed_italy_info_state(&mut r).await;
            if let Ok(res) = r.xinfo_groups("race:italy").await {
                let res: StreamInfoGroupsReply = res;
                let view: Vec<_> = res
                    .groups
                    .iter()
                    .map(|group| {
                        (
                            group.name.clone(),
                            group.consumers,
                            group.pending,
                            group.last_delivered_id.clone(),
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("italy_riders", 3, 2, "1692632662819-0")]
            }
    
            seed_italy_info_state(&mut r).await;
            if let Ok(res) = r.xinfo_consumers("race:italy", "italy_riders").await {
                let res: StreamInfoConsumersReply = res;
                let mut view: Vec<_> = res
                    .consumers
                    .iter()
                    .map(|consumer| (consumer.name.clone(), consumer.pending, consumer.idle))
                    .collect();
                view.sort_by(|a, b| a.0.cmp(&b.0));
                println!("{view:?}");
                // >>> [("Alice", 1, 5), ("Bob", 0, 5), ("Lora", 1, 5)]
            }
    
            delete_keys(&mut r, &["race:italy"]).await;
            let max1: Option<String> = r
                .xadd_maxlen("race:italy", StreamMaxlen::Equals(2), "1-0", &[("rider", "Jones")])
                .await
                .expect("maxlen add 1");
            let max1 = max1.expect("missing stream id");
            println!("{max1}"); // >>> 1-0
            let max2: Option<String> = r
                .xadd_maxlen("race:italy", StreamMaxlen::Equals(2), "2-0", &[("rider", "Wood")])
                .await
                .expect("maxlen add 2");
            let max2 = max2.expect("missing stream id");
            println!("{max2}"); // >>> 2-0
            let max3: Option<String> = r
                .xadd_maxlen("race:italy", StreamMaxlen::Equals(2), "3-0", &[("rider", "Henshaw")])
                .await
                .expect("maxlen add 3");
            let max3 = max3.expect("missing stream id");
            println!("{max3}"); // >>> 3-0
            if let Ok(res) = r.xlen("race:italy").await {
                let res: usize = res;
                println!("{res}"); // >>> 2
            }
            if let Ok(res) = r.xrange_all("race:italy").await {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![(
                                "rider".to_string(),
                                entry.get::<String>("rider").expect("missing rider"),
                            )],
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("2-0", [("rider", "Wood")]), ("3-0", [("rider", "Henshaw")])]
            }
    
            delete_keys(&mut r, &["race:italy"]).await;
            let _: Option<String> = r.xadd("race:italy", "1-0", &[("rider", "Wood")]).await.expect("trim seed 1");
            let _: Option<String> = r.xadd("race:italy", "2-0", &[("rider", "Henshaw")]).await.expect("trim seed 2");
            if let Ok(res) = r.xtrim("race:italy", StreamMaxlen::Equals(10)).await {
                let res: usize = res;
                println!("{res}"); // >>> 0
            }
    
            seed_trim_stream(&mut r).await;
            if let Ok(res) = r
                .xtrim_options(
                    "mystream",
                    &StreamTrimOptions::maxlen(StreamTrimmingMode::Approx, 10),
                )
                .await
            {
                let res: usize = res;
                println!("{res}"); // >>> 0
            }
    
            delete_keys(&mut r, &["race:italy"]).await;
            let _: Option<String> = r.xadd("race:italy", "2-0", &[("rider", "Wood")]).await.expect("xdel seed 1");
            let _: Option<String> = r.xadd("race:italy", "3-0", &[("rider", "Henshaw")]).await.expect("xdel seed 2");
            if let Ok(res) = r.xrange_count("race:italy", "-", "+", 2).await {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![(
                                "rider".to_string(),
                                entry.get::<String>("rider").expect("missing rider"),
                            )],
                        )
                    })
                    .collect();
                println!("{view:?}"); // >>> [("2-0", [("rider", "Wood")]), ("3-0", [("rider", "Henshaw")])]
            }
            if let Ok(res) = r.xdel("race:italy", &["3-0"]).await {
                let res: usize = res;
                println!("{res}"); // >>> 1
            }
            if let Ok(res) = r.xrange_count("race:italy", "-", "+", 2).await {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![(
                                "rider".to_string(),
                                entry.get::<String>("rider").expect("missing rider"),
                            )],
                        )
                    })
                    .collect();
                println!("{view:?}"); // >>> [("2-0", [("rider", "Wood")])]
            }
        }
    }
    

  • Read two stream entries starting at ID 1692632086370-0:

    Foundational: Retrieve stream entries within a range of IDs using XRANGE when you need to access historical data
    > XRANGE race:france 1692632086370-0 + COUNT 2
    1) 1) "1692632086370-0"
       2) 1) "rider"
          2) "Castilla"
          3) "speed"
          4) "30.2"
          5) "position"
          6) "1"
          7) "location_id"
          8) "1"
    2) 1) "1692632094485-0"
       2) 1) "rider"
          2) "Norem"
          3) "speed"
          4) "28.8"
          5) "position"
          6) "3"
          7) "location_id"
          8) "1"
    """
    Code samples for Stream doc pages:
        https://redis.io/docs/latest/develop/data-types/streams/
    """
    
    import redis
    
    r = redis.Redis(decode_responses=True)
    
    res1 = r.xadd(
        "race:france",
        {"rider": "Castilla", "speed": 30.2, "position": 1, "location_id": 1},
    )
    print(res1)  # >>> 1692629576966-0
    
    res2 = r.xadd(
        "race:france",
        {"rider": "Norem", "speed": 28.8, "position": 3, "location_id": 1},
    )
    print(res2)  # >>> 1692629594113-0
    
    res3 = r.xadd(
        "race:france",
        {"rider": "Prickett", "speed": 29.7, "position": 2, "location_id": 1},
    )
    print(res3)  # >>> 1692629613374-0
    
    
    res4 = r.xrange("race:france", "1691765278160-0", "+", 2)
    print(
        res4
    )  # >>> [
    #   ('1692629576966-0',
    #       {'rider': 'Castilla', 'speed': '30.2', 'position': '1', 'location_id': '1'}
    #   ),
    #   ('1692629594113-0',
    #       {'rider': 'Norem', 'speed': '28.8', 'position': '3', 'location_id': '1'}
    #   )
    # ]
    
    res5 = r.xread(streams={"race:france": 0}, count=100, block=300)
    print(
        res5
    )
    # >>> [
    #   ['race:france',
    #       [('1692629576966-0',
    #           {'rider': 'Castilla', 'speed': '30.2', 'position': '1', 'location_id': '1'}
    #       ),
    #       ('1692629594113-0',
    #           {'rider': 'Norem', 'speed': '28.8', 'position': '3', 'location_id': '1'}
    #       ),
    #       ('1692629613374-0',
    #           {'rider': 'Prickett', 'speed': '29.7', 'position': '2', 'location_id': '1'}
    #       )]
    # ]
    # ]
    
    res6 = r.xadd(
        "race:france",
        {"rider": "Castilla", "speed": 29.9, "position": 1, "location_id": 2},
    )
    print(res6)  # >>> 1692629676124-0
    
    res7 = r.xlen("race:france")
    print(res7)  # >>> 4
    
    
    res8 = r.xadd("race:usa", {"racer": "Castilla"}, id="0-1")
    print(res8)  # >>> 0-1
    
    res9 = r.xadd("race:usa", {"racer": "Norem"}, id="0-2")
    print(res9)  # >>> 0-2
    
    try:
        res10 = r.xadd("race:usa", {"racer": "Prickett"}, id="0-1")
        print(res10)  # >>> 0-1
    except redis.exceptions.ResponseError as e:
        print(e)  # >>> WRONGID
    
    # Not yet implemented
    
    res11 = r.xrange("race:france", "-", "+")
    print(
        res11
    )
    # >>> [
    #   ('1692629576966-0',
    #       {'rider': 'Castilla', 'speed': '30.2', 'position': '1', 'location_id': '1'}
    #   ),
    #   ('1692629594113-0',
    #       {'rider': 'Norem', 'speed': '28.8', 'position': '3', 'location_id': '1'}
    #   ),
    #   ('1692629613374-0',
    #       {'rider': 'Prickett', 'speed': '29.7', 'position': '2', 'location_id': '1'}
    #   ),
    #   ('1692629676124-0',
    #       {'rider': 'Castilla', 'speed': '29.9', 'position': '1', 'location_id': '2'}
    #   )
    # ]
    
    res12 = r.xrange("race:france", 1692629576965, 1692629576967)
    print(
        res12
    )
    # >>> [
    #       ('1692629576966-0',
    #           {'rider': 'Castilla', 'speed': '30.2', 'position': '1', 'location_id': '1'}
    #       )
    # ]
    
    res13 = r.xrange("race:france", "-", "+", 2)
    print(
        res13
    )
    # >>> [
    #   ('1692629576966-0',
    #       {'rider': 'Castilla', 'speed': '30.2', 'position': '1', 'location_id': '1'}
    #   ),
    #   ('1692629594113-0',
    #       {'rider': 'Norem', 'speed': '28.8', 'position': '3', 'location_id': '1'}
    #   )
    # ]
    
    res14 = r.xrange("race:france", "(1692629594113-0", "+", 2)
    print(
        res14
    )
    # >>> [
    #   ('1692629613374-0',
    #       {'rider': 'Prickett', 'speed': '29.7', 'position': '2', 'location_id': '1'}
    #   ),
    #   ('1692629676124-0',
    #       {'rider': 'Castilla', 'speed': '29.9', 'position': '1', 'location_id': '2'}
    #   )
    # ]
    
    res15 = r.xrange("race:france", "(1692629676124-0", "+", 2)
    print(res15)  # >>> []
    
    res16 = r.xrevrange("race:france", "+", "-", 1)
    print(
        res16
    )
    # >>> [
    #       ('1692629676124-0',
    #           {'rider': 'Castilla', 'speed': '29.9', 'position': '1', 'location_id': '2'}
    #       )
    # ]
    
    res17 = r.xread(streams={"race:france": 0}, count=2)
    print(
        res17
    )
    # >>> [
    #       ['race:france', [
    #       ('1692629576966-0',
    #           {'rider': 'Castilla', 'speed': '30.2', 'position': '1', 'location_id': '1'}
    #       ),
    #       ('1692629594113-0',
    #           {'rider': 'Norem', 'speed': '28.8', 'position': '3', 'location_id': '1'}
    #       )
    #       ]
    #       ]
    #   ]
    
    res18 = r.xgroup_create("race:france", "france_riders", "$")
    print(res18)  # >>> True
    
    res19 = r.xgroup_create("race:italy", "italy_riders", "$", mkstream=True)
    print(res19)  # >>> True
    
    r.xadd("race:italy", {"rider": "Castilla"})
    r.xadd("race:italy", {"rider": "Royce"})
    r.xadd("race:italy", {"rider": "Sam-Bodden"})
    r.xadd("race:italy", {"rider": "Prickett"})
    r.xadd("race:italy", {"rider": "Norem"})
    
    res20 = r.xreadgroup(
        streams={"race:italy": ">"},
        consumername="Alice",
        groupname="italy_riders",
        count=1,
    )
    print(res20)  # >>> [['race:italy', [('1692629925771-0', {'rider': 'Castilla'})]]]
    
    res21 = r.xreadgroup(
        streams={"race:italy": 0},
        consumername="Alice",
        groupname="italy_riders",
        count=1,
    )
    print(res21)  # >>> [['race:italy', [('1692629925771-0', {'rider': 'Castilla'})]]]
    
    res22 = r.xack("race:italy", "italy_riders", "1692629925771-0")
    print(res22)  # >>> 1
    
    res23 = r.xreadgroup(
        streams={"race:italy": 0},
        consumername="Alice",
        groupname="italy_riders",
        count=1,
    )
    print(res23)  # >>> [['race:italy', []]]
    
    res24 = r.xreadgroup(
        streams={"race:italy": ">"},
        consumername="Bob",
        groupname="italy_riders",
        count=2,
    )
    print(
        res24
    )
    # >>> [
    #       ['race:italy', [
    #           ('1692629925789-0',
    #               {'rider': 'Royce'}
    #           ),
    #           ('1692629925790-0',
    #               {'rider': 'Sam-Bodden'}
    #           )
    #       ]
    #       ]
    # ]
    
    res25 = r.xpending("race:italy", "italy_riders")
    print(
        res25
    )
    # >>> {
    #       'pending': 2, 'min': '1692629925789-0', 'max': '1692629925790-0',
    #       'consumers': [{'name': 'Bob', 'pending': 2}]
    # }
    
    res26 = r.xpending_range("race:italy", "italy_riders", "-", "+", 10)
    print(
        res26
    )
    # >>> [
    #       {
    #           'message_id': '1692629925789-0', 'consumer': 'Bob',
    #           'time_since_delivered': 31084, 'times_delivered': 1
    #       },
    #       {
    #           'message_id': '1692629925790-0', 'consumer': 'Bob',
    #           'time_since_delivered': 31084, 'times_delivered': 1
    #       }
    # ]
    
    res27 = r.xrange("race:italy", "1692629925789-0", "1692629925789-0")
    print(res27)  # >>> [('1692629925789-0', {'rider': 'Royce'})]
    
    res28 = r.xclaim("race:italy", "italy_riders", "Alice", 60000, ["1692629925789-0"])
    print(res28)  # >>> [('1692629925789-0', {'rider': 'Royce'})]
    
    res29 = r.xautoclaim("race:italy", "italy_riders", "Alice", 1, "0-0", 1)
    print(res29)  # >>> ['1692629925790-0', [('1692629925789-0', {'rider': 'Royce'})]]
    
    res30 = r.xautoclaim("race:italy", "italy_riders", "Alice", 1, "(1692629925789-0", 1)
    print(res30)  # >>> ['0-0', [('1692629925790-0', {'rider': 'Sam-Bodden'})]]
    
    res31 = r.xinfo_stream("race:italy")
    print(
        res31
    )
    # >>> {
    #       'length': 5, 'radix-tree-keys': 1, 'radix-tree-nodes': 2,
    #       'last-generated-id': '1692629926436-0', 'groups': 1,
    #       'first-entry': ('1692629925771-0', {'rider': 'Castilla'}),
    #       'last-entry': ('1692629926436-0', {'rider': 'Norem'})
    # }
    
    res32 = r.xinfo_groups("race:italy")
    print(
        res32
    )
    # >>> [
    #       {
    #           'name': 'italy_riders', 'consumers': 2, 'pending': 2,
    #           'last-delivered-id': '1692629925790-0'
    #       }
    # ]
    
    res33 = r.xinfo_consumers("race:italy", "italy_riders")
    print(
        res33
    )
    # >>> [
    #       {'name': 'Alice', 'pending': 2, 'idle': 199332},
    #       {'name': 'Bob', 'pending': 0, 'idle': 489170}
    # ]
    
    r.xadd("race:italy", {"rider": "Jones"}, maxlen=2)
    r.xadd("race:italy", {"rider": "Wood"}, maxlen=2)
    r.xadd("race:italy", {"rider": "Henshaw"}, maxlen=2)
    
    res34 = r.xlen("race:italy")
    print(res34)  # >>> 8
    
    res35 = r.xrange("race:italy", "-", "+")
    print(
        res35
    )
    # >>> [
    #       ('1692629925771-0', {'rider': 'Castilla'}),
    #       ('1692629925789-0', {'rider': 'Royce'}),
    #       ('1692629925790-0', {'rider': 'Sam-Bodden'}),
    #       ('1692629925791-0', {'rider': 'Prickett'}),
    #       ('1692629926436-0', {'rider': 'Norem'}),
    #       ('1692630612602-0', {'rider': 'Jones'}),
    #       ('1692630641947-0', {'rider': 'Wood'}),
    #       ('1692630648281-0', {'rider': 'Henshaw'})
    # ]
    
    r.xadd("race:italy", {"rider": "Smith"}, maxlen=2, approximate=False)
    
    res36 = r.xrange("race:italy", "-", "+")
    print(
        res36
    )
    # >>> [
    #       ('1692630648281-0', {'rider': 'Henshaw'}),
    #       ('1692631018238-0', {'rider': 'Smith'})
    # ]
    
    res37 = r.xtrim("race:italy", maxlen=10, approximate=False)
    print(res37)  # >>> 0
    
    res38 = r.xtrim("race:italy", maxlen=10)
    print(res38)  # >>> 0
    
    res39 = r.xrange("race:italy", "-", "+")
    print(
        res39
    )
    # >>> [
    #       ('1692630648281-0', {'rider': 'Henshaw'}),
    #       ('1692631018238-0', {'rider': 'Smith'})
    # ]
    
    res40 = r.xdel("race:italy", "1692631018238-0")
    print(res40)  # >>> 1
    
    res41 = r.xrange("race:italy", "-", "+")
    print(res41)  # >>> [('1692630648281-0', {'rider': 'Henshaw'})]
    
    import assert from 'assert';
    import {
      createClient
    } from 'redis';
    
    const client = await createClient();
    await client.connect();
    
    const res1 = await client.xAdd(
      'race:france', '*', {
        'rider': 'Castilla',
        'speed': '30.2',
        'position': '1',
        'location_id': '1'
      }
    );
    console.log(res1); // >>> 1700073067968-0 N.B. actual values will differ from these examples
    
    const res2 = await client.xAdd(
      'race:france', '*', {
        'rider': 'Norem',
        'speed': '28.8',
        'position': '3',
        'location_id': '1'
      },
    );
    console.log(res2); // >>> 1692629594113-0
    
    const res3 = await client.xAdd(
      'race:france', '*', {
        'rider': 'Prickett',
        'speed': '29.7',
        'position': '2',
        'location_id': '1'
      },
    );
    console.log(res3); // >>> 1692629613374-0
    
    
    const res4 = await client.xRange('race:france', '1691765278160-0', '+', {COUNT: 2});
    console.log(res4); // >>> [{ id: '1692629576966-0', message: { rider: 'Castilla', speed: '30.2', position: '1', location_id: '1' } }, { id: '1692629594113-0', message: { rider: 'Norem', speed: '28.8', position: '3', location_id: '1' } }]
    
    const res5 = await client.xRead({
      key: 'race:france',
      id: '0-0'
    }, {
      COUNT: 100,
      BLOCK: 300
    });
    console.log(res5); // >>> [{ name: 'race:france', messages: [{ id: '1692629576966-0', message: { rider: 'Castilla', speed: '30.2', position: '1', location_id: '1' } }, { id: '1692629594113-0', message: { rider: 'Norem', speed: '28.8', position: '3', location_id: '1' } }, { id: '1692629613374-0', message: { rider: 'Prickett', speed: '29.7', position: '2', location_id: '1' } }] }]
    
    const res6 = await client.xAdd(
      'race:france', '*', {
        'rider': 'Castilla',
        'speed': '29.9',
        'position': '1',
        'location_id': '2'
      }
    );
    console.log(res6); // >>> 1692629676124-0
    
    const res7 = await client.xLen('race:france');
    console.log(res7); // >>> 4
    
    
    const res8 = await client.xAdd('race:usa', '0-1', {
      'racer': 'Castilla'
    });
    console.log(res8); // >>> 0-1
    
    const res9 = await client.xAdd('race:usa', '0-2', {
      'racer': 'Norem'
    });
    console.log(res9); // >>> 0-2
    
    try {
      const res10 = await client.xAdd('race:usa', '0-1', {
        'racer': 'Prickett'
      });
      console.log(res10); // >>> 0-1
    } catch (error) {
      console.error(error); // >>> [SimpleError: ERR The ID specified in XADD is equal or smaller than the target stream top item]
    }
    
    const res11a = await client.xAdd('race:usa', '0-*', { racer: 'Norem' });
    console.log(res11a); // >>> 0-3
    
    const res11 = await client.xRange('race:france', '-', '+');
    console.log(res11); // >>> [{ id: '1692629576966-0', message: { rider: 'Castilla', speed: '30.2', position: '1', location_id: '1' } }, { id: '1692629594113-0', message: { rider: 'Norem', speed: '28.8', position: '3', location_id: '1' } }, { id: '1692629613374-0', message: { rider: 'Prickett', speed: '29.7', position: '2', location_id: '1' } }, { id: '1692629676124-0', message: { rider: 'Castilla', speed: '29.9', position: '1', location_id: '2' } }]
    
    const res12 = await client.xRange('race:france', '1692629576965', '1692629576967');
    console.log(res12); // >>> [{ id: '1692629576966-0', message: { rider: 'Castilla', speed: '30.2', position: '1', location_id: '1' } }]
    
    const res13 = await client.xRange('race:france', '-', '+', {COUNT: 2});
    console.log(res13); // >>> [{ id: '1692629576966-0', message: { rider: 'Castilla', speed: '30.2', position: '1', location_id: '1' } }, { id: '1692629594113-0', message: { rider: 'Norem', speed: '28.8', position: '3', location_id: '1' } }]
    
    const res14 = await client.xRange('race:france', '(1692629594113-0', '+', {COUNT: 2});
    console.log(res14); // >>> [{ id: '1692629613374-0', message: { rider: 'Prickett', speed: '29.7', position: '2', location_id: '1' } }, { id: '1692629676124-0', message: { rider: 'Castilla', speed: '29.9', position: '1', location_id: '2' } }]
    
    const res15 = await client.xRange('race:france', '(1692629676124-0', '+', {COUNT: 2});
    console.log(res15); // >>> []
    
    const res16 = await client.xRevRange('race:france', '+', '-', {COUNT: 1});
    console.log(
      res16
    ); // >>> [{ id: '1692629676124-0', message: { rider: 'Castilla', speed: '29.9', position: '1', location_id: '2' } }]
    
    const res17 = await client.xRead({
      key: 'race:france',
      id: '0-0'
    }, {
      COUNT: 2
    });
    console.log(res17); // >>> [{ name: 'race:france', messages: [{ id: '1692629576966-0', message: { rider: 'Castilla', speed: '30.2', position: '1', location_id: '1' } }, { id: '1692629594113-0', message: { rider: 'Norem', speed: '28.8', position: '3', location_id: '1' } }] }]
    
    const res18 = await client.xGroupCreate('race:france', 'france_riders', '$');
    console.log(res18); // >>> OK
    
    const res19 = await client.xGroupCreate('race:italy', 'italy_riders', '$', {
      MKSTREAM: true
    });
    console.log(res19); // >>> OK
    
    await client.xAdd('race:italy', '*', {
      'rider': 'Castilla'
    });
    await client.xAdd('race:italy', '*', {
      'rider': 'Royce'
    });
    await client.xAdd('race:italy', '*', {
      'rider': 'Sam-Bodden'
    });
    await client.xAdd('race:italy', '*', {
      'rider': 'Prickett'
    });
    await client.xAdd('race:italy', '*', {
      'rider': 'Norem'
    });
    
    const res20 = await client.xReadGroup(
      'italy_riders',
      'Alice', {
        key: 'race:italy',
        id: '>'
      }, {
        COUNT: 1
      }
    );
    console.log(res20); // >>> [{ name: 'race:italy', messages: [{ id: '1692629925771-0', message: { rider: 'Castilla' } }] }]
    
    const res21 = await client.xReadGroup(
      'italy_riders',
      'Alice', {
        key: 'race:italy',
        id: '0'
      }, {
        COUNT: 1
      }
    );
    console.log(res21); // >>> [{ name: 'race:italy', messages: [{ id: '1692629925771-0', message: { rider: 'Castilla' } }] }]
    
    const res22 = await client.xAck('race:italy', 'italy_riders', '1692629925771-0')
    console.log(res22); // >>> 1
    
    const res23 = await client.xReadGroup(
      'italy_riders',
      'Alice', {
        key: 'race:italy',
        id: '0'
      }, {
        COUNT: 1
      }
    );
    console.log(res23); // >>> [{ name: 'race:italy', messages: [] }]
    
    const res24 = await client.xReadGroup(
      'italy_riders',
      'Bob', {
        key: 'race:italy',
        id: '>'
      }, {
        COUNT: 2
      }
    );
    console.log(res24); // >>> [{ name: 'race:italy', messages: [{ id: '1692629925789-0', message: { rider: 'Royce' } }, { id: '1692629925790-0', message: { rider: 'Sam-Bodden' } }] }]
    
    const res25 = await client.xPending('race:italy', 'italy_riders');
    console.log(res25); // >>> {'pending': 2, 'firstId': '1692629925789-0', 'lastId': '1692629925790-0', 'consumers': [{'name': 'Bob', 'deliveriesCounter': 2}]}
    
    const res26 = await client.xPendingRange('race:italy', 'italy_riders', '-', '+', 10);
    console.log(res26); // >>> [{'id': '1692629925789-0', 'consumer': 'Bob', 'millisecondsSinceLastDelivery': 31084, 'deliveriesCounter:': 1}, {'id': '1692629925790-0', 'consumer': 'Bob', 'millisecondsSinceLastDelivery': 31084, 'deliveriesCounter': 1}]
    
    const res27 = await client.xRange('race:italy', '1692629925789-0', '1692629925789-0');
    console.log(res27); // >>> [{ id: '1692629925789-0', message: { rider: 'Royce' } }]
    
    const res28 = await client.xClaim(
      'race:italy', 'italy_riders', 'Alice', 60000, ['1692629925789-0']
    );
    console.log(res28); // >>> [{ id: '1692629925789-0', message: { rider: 'Royce' } }]
    
    const res29 = await client.xAutoClaim('race:italy', 'italy_riders', 'Alice', 1, '0-0', {
      COUNT: 1
    });
    console.log(res29); // >>> { nextId: '1692629925790-0', messages: [{ id: '1692629925789-0', message: { rider: 'Royce' } }], deletedMessages: [] }
    
    const res30 = await client.xAutoClaim(
      'race:italy', 'italy_riders', 'Alice', 1, '(1692629925789-0',
      {
        COUNT: 1
      }
    );
    console.log(res30); // >>> { nextId: '0-0', messages: [{ id: '1692629925790-0', message: { rider: 'Sam-Bodden' } }], deletedMessages: [] }
    
    const res31 = await client.xInfoStream('race:italy');
    console.log(res31); // >>> { length: 5, 'radix-tree-keys': 1, 'radix-tree-nodes': 2, 'last-generated-id': '1692629926436-0', 'max-deleted-entry-id': '0-0', 'entries-added': 5, 'recorded-first-entry-id': '1692629925771-0', groups: 1, 'first-entry': { id: '1692629925771-0', message: { rider: 'Castilla' } }, 'last-entry': { id: '1692629926436-0', message: { rider: 'Norem' } } }
    
    const res32 = await client.xInfoGroups('race:italy');
    console.log(res32); // >>> [{ name: 'italy_riders', consumers: 2, pending: 3, 'last-delivered-id': '1692629925790-0', 'entries-read': 3, lag: 2 }]
    
    const res33 = await client.xInfoConsumers('race:italy', 'italy_riders');
    console.log(res33); // >>> [{ name: 'Alice', pending: 3, idle: 170582, inactive: 170582 }, { name: 'Bob', pending: 0, idle: 489404, inactive: 489404 }]
    
    await client.xAdd('race:italy', '*', {
      'rider': 'Jones'
    }, {
      TRIM: {
        strategy: 'MAXLEN',
        strategyModifier: '~',
        threshold: 2
      }
    });
    await client.xAdd('race:italy', '*', {
      'rider': 'Wood'
    }, {
      TRIM: {
        strategy: 'MAXLEN',
        strategyModifier: '~',
        threshold: 2
      }
    });
    await client.xAdd('race:italy', '*', {
      'rider': 'Henshaw'
    }, {
      TRIM: {
        strategy: 'MAXLEN',
        strategyModifier: '~',
        threshold: 2
      }
    });
    
    const res34 = await client.xLen('race:italy');
    console.log(res34); // >>> 8
    
    const res35 = await client.xRange('race:italy', '-', '+');
    console.log(res35); // >>> [{ id: '1692629925771-0', message: { rider: 'Castilla' } }, { id: '1692629925789-0', message: { rider: 'Royce' } }, { id: '1692629925790-0', message: { rider: 'Sam-Bodden' } }, { id: '1692629925791-0', message: { rider: 'Prickett' } }, { id: '1692629926436-0', message: { rider: 'Norem' } }, { id: '1692630612602-0', message: { rider: 'Jones' } }, { id: '1692630641947-0', message: { rider: 'Wood' } }, { id: '1692630648281-0', message: { rider: 'Henshaw' } }]
    
    await client.xAdd('race:italy', '*', {
      'rider': 'Smith'
    }, {
      TRIM: {
        strategy: 'MAXLEN',
        strategyModifier: '=',
        threshold: 2
      }
    });
    
    const res36 = await client.xRange('race:italy', '-', '+');
    console.log(res36); // >>> [{ id: '1692630648281-0', message: { rider: 'Henshaw' } }, { id: '1692631018238-0', message: { rider: 'Smith' } }]
    
    const res37 = await client.xTrim('race:italy', 'MAXLEN', 10, {
      strategyModifier: '=',
    });
    console.log(res37); // >>> 0
    
    const res38 = await client.xTrim('race:italy', "MAXLEN", 10);
    console.log(res38); // >>> 0
    
    const res39 = await client.xRange('race:italy', '-', '+');
    console.log(res39); // >>> [{ id: '1692630648281-0', message: { rider: 'Henshaw' } }, { id: '1692631018238-0', message: { rider: 'Smith' } }]
    
    const res40 = await client.xDel('race:italy', '1692631018238-0');
    console.log(res40); // >>> 1
    
    const res41 = await client.xRange('race:italy', '-', '+');
    console.log(res41); // >>> [{ id: '1692630648281-0', message: { rider: 'Henshaw' } }]
    
    
    package io.redis.examples;
    
    import redis.clients.jedis.StreamEntryID;
    import redis.clients.jedis.RedisClient;
    
    
    public class StreamsExample {
    
      public void run() {
        RedisClient jedis = RedisClient.create("redis://localhost:6379");
    
    
        StreamEntryID res1 = jedis.xadd("race:france",new HashMap<String,String>(){{put("rider","Castilla");put("speed","30.2");put("position","1");put("location_id","1");}} , XAddParams.xAddParams());
    
        System.out.println(res1); // >>> 1701760582225-0
    
        StreamEntryID res2 = jedis.xadd("race:france",new HashMap<String,String>(){{put("rider","Norem");put("speed","28.8");put("position","3");put("location_id","1");}} , XAddParams.xAddParams());
    
        System.out.println(res2); // >>> 1701760582225-1
    
        StreamEntryID res3 = jedis.xadd("race:france",new HashMap<String,String>(){{put("rider","Prickett");put("speed","29.7");put("position","2");put("location_id","1");}} , XAddParams.xAddParams());
    
        System.out.println(res3); // >>> 1701760582226-0
    
    
        List<StreamEntry> res4 = jedis.xrange("race:france","1701760582225-0","+",2);
    
        System.out.println(res4); // >>> [1701760841292-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701760841292-1 {rider=Norem, speed=28.8, location_id=1, position=3}]
    
        List<Map.Entry<String, List<StreamEntry>>> res5= jedis.xread(XReadParams.xReadParams().block(300).count(100),new HashMap<String,StreamEntryID>(){{put("race:france",new StreamEntryID());}});
        System.out.println(
          res5
        ); // >>> [race:france=[1701761996660-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701761996661-0 {rider=Norem, speed=28.8, location_id=1, position=3}, 1701761996661-1 {rider=Prickett, speed=29.7, location_id=1, position=2}]]
    
        StreamEntryID res6 = jedis.xadd("race:france",new HashMap<String,String>(){{put("rider","Castilla");put("speed","29.9");put("position","2");put("location_id","1");}} , XAddParams.xAddParams());
        System.out.println(res6); // >>> 1701762285679-0
    
        long res7 = jedis.xlen("race:france");
        System.out.println(res7); // >>> 4
    
        StreamEntryID res8 = jedis.xadd("race:usa", new HashMap<String,String>(){{put("racer","Castilla");}},XAddParams.xAddParams().id("0-1"));
        System.out.println(res8); // >>> 0-1
    
        StreamEntryID res9 = jedis.xadd("race:usa", new HashMap<String,String>(){{put("racer","Norem");}},XAddParams.xAddParams().id("0-2"));
        System.out.println(res9); // >>> 0-2
    
        try {
          StreamEntryID res10 = jedis.xadd("race:usa", new HashMap<String,String>(){{put("racer","Prickett");}},XAddParams.xAddParams().id("0-1"));
          System.out.println(res10); // >>> 0-1
        }
        catch (JedisDataException e){
          System.out.println(e); // >>> ERR The ID specified in XADD is equal or smaller than the target stream top item
        }
    
        StreamEntryID res11 = jedis.xadd("race:usa", new HashMap<String,String>(){{put("racer","Norem");}},XAddParams.xAddParams().id("0-*"));
        System.out.println(res11);
    
        List<StreamEntry> res12 = jedis.xrange("race:france","-","+");
        System.out.println(
          res12
        ); // >>> [1701764734160-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701764734160-1 {rider=Norem, speed=28.8, location_id=1, position=3}, 1701764734161-0 {rider=Prickett, speed=29.7, location_id=1, position=2}, 1701764734162-0 {rider=Castilla, speed=29.9, location_id=1, position=2}]
    
        List<StreamEntry> res13 = jedis.xrange("race:france",String.valueOf(System.currentTimeMillis()-1000),String.valueOf(System.currentTimeMillis()+1000));
        System.out.println(
          res13
        ); // >>> [1701764734160-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701764734160-1 {rider=Norem, speed=28.8, location_id=1, position=3}, 1701764734161-0 {rider=Prickett, speed=29.7, location_id=1, position=2}, 1701764734162-0 {rider=Castilla, speed=29.9, location_id=1, position=2}]
    
        List<StreamEntry> res14 = jedis.xrange("race:france","-","+",2);
        System.out.println(res14); // >>> [1701764887638-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701764887638-1 {rider=Norem, speed=28.8, location_id=1, position=3}]
    
        List<StreamEntry> res15 = jedis.xrange("race:france",String.valueOf(System.currentTimeMillis()-1000)+"-0","+",2);
        System.out.println(res15); // >>> [1701764887638-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701764887638-1 {rider=Norem, speed=28.8, location_id=1, position=3}]
    
        List<StreamEntry> res16 = jedis.xrange("race:france",String.valueOf(System.currentTimeMillis()+1000)+"-0","+",2);
        System.out.println(res16); // >>> []
    
        List<StreamEntry> res17 = jedis.xrevrange("race:france","+","-",1);
        System.out.println(res17); // >>> [1701765218592-0 {rider=Castilla, speed=29.9, location_id=1, position=2}]
    
        List<Map.Entry<String, List<StreamEntry>>> res18= jedis.xread(XReadParams.xReadParams().count(2),new HashMap<String,StreamEntryID>(){{put("race:france",new StreamEntryID());}});
        System.out.println(
          res18
        ); // >>> [race:france=[1701765384638-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701765384638-1 {rider=Norem, speed=28.8, location_id=1, position=3}]]
    
        String res19 = jedis.xgroupCreate("race:france","france_riders",StreamEntryID.LAST_ENTRY,false);
        System.out.println(res19); // >>> OK
    
        String res20 = jedis.xgroupCreate("race:italy","italy_riders",StreamEntryID.LAST_ENTRY,true);
        System.out.println(res20); // >>> OK
    
        StreamEntryID id1 = jedis.xadd("race:italy", new HashMap<String,String>(){{put("rider","Castilaa");}},XAddParams.xAddParams());
        StreamEntryID id2 = jedis.xadd("race:italy", new HashMap<String,String>(){{put("rider","Royce");}},XAddParams.xAddParams());
        StreamEntryID id3 = jedis.xadd("race:italy", new HashMap<String,String>(){{put("rider","Sam-Bodden");}},XAddParams.xAddParams());
        StreamEntryID id4 = jedis.xadd("race:italy", new HashMap<String,String>(){{put("rider","Prickett");}},XAddParams.xAddParams());
        StreamEntryID id5 = jedis.xadd("race:italy", new HashMap<String,String>(){{put("rider","Norem");}},XAddParams.xAddParams());
    
        List<Map.Entry<String, List<StreamEntry>>> res21 = jedis.xreadGroup("italy_riders","Alice", XReadGroupParams.xReadGroupParams().count(1),new HashMap<String,StreamEntryID>(){{put("race:italy",StreamEntryID.UNRECEIVED_ENTRY);}});
        System.out.println(res21); // >>> [race:italy=[1701766299006-0 {rider=Castilaa}]]
    
        List<Map.Entry<String, List<StreamEntry>>> res22 = jedis.xreadGroup("italy_riders","Alice", XReadGroupParams.xReadGroupParams().count(1),new HashMap<String,StreamEntryID>(){{put("race:italy",new StreamEntryID());}});
        System.out.println(res22); // >>> [race:italy=[1701766299006-0 {rider=Castilaa}]]
    
        long res23 = jedis.xack("race:italy","italy_riders",id1);
        System.out.println(res23); // >>> 1
    
        List<Map.Entry<String, List<StreamEntry>>> res24 = jedis.xreadGroup("italy_riders","Alice", XReadGroupParams.xReadGroupParams().count(1),new HashMap<String,StreamEntryID>(){{put("race:italy",new StreamEntryID());}});
        System.out.println(res24); // >>> [race:italy=[]]
    
        List<Map.Entry<String, List<StreamEntry>>> res25 = jedis.xreadGroup("italy_riders","Bob", XReadGroupParams.xReadGroupParams().count(2),new HashMap<String,StreamEntryID>(){{put("race:italy",StreamEntryID.UNRECEIVED_ENTRY);}});
        System.out.println(res25); // >>> [race:italy=[1701767632261-1 {rider=Royce}, 1701767632262-0 {rider=Sam-Bodden}]]
    
        StreamPendingSummary res26 = jedis.xpending("race:italy","italy_riders");
        System.out.println(res26.getConsumerMessageCount()); // >>> {Bob=2}
    
        List<StreamPendingEntry> res27 = jedis.xpending("race:italy","italy_riders",XPendingParams.xPendingParams().start(StreamEntryID.MINIMUM_ID).end(StreamEntryID.MAXIMUM_ID).count(10));
        System.out.println(res27); // >>> [1701768567412-1 Bob idle:0 times:1, 1701768567412-2 Bob idle:0 times:1]
    
        List<StreamEntry> res28 = jedis.xrange("race:italy",id2.toString(),id2.toString());
        System.out.println(res28); // >>> [1701768744819-1 {rider=Royce}]
    
        List<StreamEntry> res29 = jedis.xclaim("race:italy","italy_riders","Alice", 0L, XClaimParams.xClaimParams().time(60000),id2);
        System.out.println(res29); // >>> [1701769004195-1 {rider=Royce}]
    
        Map.Entry<StreamEntryID, List<StreamEntry>> res30 = jedis.xautoclaim("race:italy","italy_riders","Alice",1L,new StreamEntryID("0-0"),XAutoClaimParams.xAutoClaimParams().count(1));
        System.out.println(res30); // >>> [1701769266831-2=[1701769266831-1 {rider=Royce}]
    
        Map.Entry<StreamEntryID, List<StreamEntry>> res31 = jedis.xautoclaim("race:italy","italy_riders","Alice",1L,new StreamEntryID(id2.toString()),XAutoClaimParams.xAutoClaimParams().count(1));
        System.out.println(res31); // >>> [0-0=[1701769605847-2 {rider=Sam-Bodden}]
    
        StreamInfo res32 = jedis.xinfoStream("race:italy");
        System.out.println(
          res32.getStreamInfo()
        ); // >>> {radix-tree-keys=1, radix-tree-nodes=2, entries-added=5, length=5, groups=1, max-deleted-entry-id=0-0, first-entry=1701769637612-0 {rider=Castilaa}, last-generated-id=1701769637612-4, last-entry=1701769637612-4 {rider=Norem}, recorded-first-entry-id=1701769637612-0}
    
        List<StreamGroupInfo> res33 = jedis.xinfoGroups("race:italy");
        for (StreamGroupInfo a : res33){
          System.out.println(
            a.getGroupInfo()
          ); // >>> {last-delivered-id=1701770253659-0, lag=2, pending=2, name=italy_riders, consumers=2, entries-read=3}
        }
    
        List<StreamConsumersInfo> res34 = jedis.xinfoConsumers("race:italy","italy_riders");
        for (StreamConsumerInfo a : res34){
          System.out.println(
            a.getConsumerInfo()
          ); // {inactive=1, idle=1, pending=1, name=Alice} , {inactive=3, idle=3, pending=1, name=Bob}
        }
    
        jedis.xadd("race:italy", new HashMap<String,String>(){{put("rider","Jones");}},XAddParams.xAddParams().maxLen(10));
        jedis.xadd("race:italy", new HashMap<String,String>(){{put("rider","Wood");}},XAddParams.xAddParams().maxLen(10));
        jedis.xadd("race:italy", new HashMap<String,String>(){{put("rider","Henshaw");}},XAddParams.xAddParams().maxLen(10));
        long res35 = jedis.xlen("race:italy");
        System.out.println(res35); // >>> 8
    
        List<StreamEntry> res36 = jedis.xrange("race:italy","-","+");
        System.out.println(res36); // >>> [1701771219852-0 {rider=Castilaa}, 1701771219852-1 {rider=Royce}, 1701771219853-0 {rider=Sam-Bodden}, 1701771219853-1 {rider=Prickett}, 1701771219853-2 {rider=Norem}, 1701771219858-0 {rider=Jones}, 1701771219858-1 {rider=Wood}, 1701771219859-0 {rider=Henshaw}]
    
        StreamEntryID id6 = jedis.xadd("race:italy", new HashMap<String,String>(){{put("rider","Smith");}},XAddParams.xAddParams().maxLen(2));
    
        List<StreamEntry> res37 = jedis.xrange("race:italy","-","+");
        System.out.println(res37); // >>> [1701771067332-1 {rider=Henshaw}, 1701771067332-2 {rider=Smith}]
    
        long res38 = jedis.xtrim("race:italy",XTrimParams.xTrimParams().maxLen(10).exactTrimming());
        System.out.println(res38); /// >>> 0
    
        long res39 = jedis.xtrim("race:italy",XTrimParams.xTrimParams().maxLen(10));
        System.out.println(res39); /// >>> 0
    
        List<StreamEntry> res40 = jedis.xrange("race:italy","-","+");
        System.out.println(res40); // >>> [1701771356428-2 {rider=Henshaw}, 1701771356429-0 {rider=Smith}]
    
        long res41 = jedis.xdel("race:italy",id6);
        System.out.println(res41); // >>> 1
    
        List<StreamEntry> res42 = jedis.xrange("race:italy","-","+");
        System.out.println(res42); // >>> [1701771517639-1 {rider=Henshaw}]
    
        jedis.close();
      }
    
    }
    
    package example_commands_test
    
    import (
    	"context"
    	"fmt"
    
    	"github.com/redis/go-redis/v9"
    )
    
    
    
    func ExampleClient_xadd() {
    	ctx := context.Background()
    
    	rdb := redis.NewClient(&redis.Options{
    		Addr:     "localhost:6379",
    		Password: "", // no password docs
    		DB:       0,  // use default DB
    	})
    
    
    	res1, err := rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:france",
    		Values: map[string]interface{}{
    			"rider":       "Castilla",
    			"speed":       30.2,
    			"position":    1,
    			"location_id": 1,
    		},
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	// fmt.Println(res1) // >>> 1692632086370-0
    
    	res2, err := rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:france",
    		Values: map[string]interface{}{
    			"rider":       "Norem",
    			"speed":       28.8,
    			"position":    3,
    			"location_id": 1,
    		},
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	// fmt.PrintLn(res2) // >>> 1692632094485-0
    
    	res3, err := rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:france",
    		Values: map[string]interface{}{
    			"rider":       "Prickett",
    			"speed":       29.7,
    			"position":    2,
    			"location_id": 1,
    		},
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	// fmt.Println(res3) // >>> 1692632102976-0
    
    
    	xlen, err := rdb.XLen(ctx, "race:france").Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(xlen) // >>> 3
    
    }
    
    func ExampleClient_racefrance1() {
    	ctx := context.Background()
    
    	rdb := redis.NewClient(&redis.Options{
    		Addr:     "localhost:6379",
    		Password: "", // no password docs
    		DB:       0,  // use default DB
    	})
    
    
    	_, err := rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:france",
    		Values: map[string]interface{}{
    			"rider":       "Castilla",
    			"speed":       30.2,
    			"position":    1,
    			"location_id": 1,
    		},
    		ID: "1692632086370-0",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:france",
    		Values: map[string]interface{}{
    			"rider":       "Norem",
    			"speed":       28.8,
    			"position":    3,
    			"location_id": 1,
    		},
    		ID: "1692632094485-0",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:france",
    		Values: map[string]interface{}{
    			"rider":       "Prickett",
    			"speed":       29.7,
    			"position":    2,
    			"location_id": 1,
    		},
    		ID: "1692632102976-0",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	res4, err := rdb.XRangeN(ctx, "race:france", "1691765278160-0", "+", 2).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res4)
    	// >>> [{1692632086370-0 map[location_id:1 position:1 rider:Castilla...
    
    	res5, err := rdb.XRead(ctx, &redis.XReadArgs{
    		Streams: []string{"race:france", "0"},
    		Count:   100,
    		Block:   300,
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res5)
    	// >>> // [{race:france [{1692632086370-0 map[location_id:1 position:1...
    
    	res6, err := rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:france",
    		Values: map[string]interface{}{
    			"rider":       "Castilla",
    			"speed":       29.9,
    			"position":    1,
    			"location_id": 2,
    		},
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	//fmt.Println(res6) // >>> 1692632147973-0
    
    	res7, err := rdb.XLen(ctx, "race:france").Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res7) // >>> 4
    
    
    }
    
    func ExampleClient_raceusa() {
    	ctx := context.Background()
    
    	rdb := redis.NewClient(&redis.Options{
    		Addr:     "localhost:6379",
    		Password: "", // no password docs
    		DB:       0,  // use default DB
    	})
    
    
    	res8, err := rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:usa",
    		Values: map[string]interface{}{
    			"racer": "Castilla",
    		},
    		ID: "0-1",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res8) // >>> 0-1
    
    	res9, err := rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:usa",
    		Values: map[string]interface{}{
    			"racer": "Norem",
    		},
    		ID: "0-2",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res9) // >>> 0-2
    
    	res10, err := rdb.XAdd(ctx, &redis.XAddArgs{
    		Values: map[string]interface{}{
    			"racer": "Prickett",
    		},
    		ID: "0-1",
    	}).Result()
    
    	if err != nil {
    		// fmt.Println(err)
    		// >>> ERR The ID specified in XADD is equal or smaller than the target stream top item
    	}
    
    	res11, err := rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:usa",
    		Values: map[string]interface{}{
    			"racer": "Prickett",
    		},
    		ID: "0-*",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res11) // >>> 0-3
    
    
    }
    
    func ExampleClient_racefrance2() {
    	ctx := context.Background()
    
    	rdb := redis.NewClient(&redis.Options{
    		Addr:     "localhost:6379",
    		Password: "", // no password docs
    		DB:       0,  // use default DB
    	})
    
    
    	_, err := rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:france",
    		Values: map[string]interface{}{
    			"rider":       "Castilla",
    			"speed":       30.2,
    			"position":    1,
    			"location_id": 1,
    		},
    		ID: "1692632086370-0",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:france",
    		Values: map[string]interface{}{
    			"rider":       "Norem",
    			"speed":       28.8,
    			"position":    3,
    			"location_id": 1,
    		},
    		ID: "1692632094485-0",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:france",
    		Values: map[string]interface{}{
    			"rider":       "Prickett",
    			"speed":       29.7,
    			"position":    2,
    			"location_id": 1,
    		},
    		ID: "1692632102976-0",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:france",
    		Values: map[string]interface{}{
    			"rider":       "Castilla",
    			"speed":       29.9,
    			"position":    1,
    			"location_id": 2,
    		},
    		ID: "1692632147973-0",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    	res12, err := rdb.XRange(ctx, "race:france", "-", "+").Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res12)
    	// >>> [{1692632086370-0 map[location_id:1 position:1 rider:Castilla...
    
    	res13, err := rdb.XRange(ctx, "race:france",
    		"1692632086369", "1692632086371",
    	).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res13)
    	// >>> [{1692632086370-0 map[location_id:1 position:1 rider:Castilla speed:30.2] 0 0}]
    
    	res14, err := rdb.XRangeN(ctx, "race:france", "-", "+", 2).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res14)
    	// >>> [{1692632086370-0 map[location_id:1 position:1 rider:Castilla speed:30.2] 0 0} {1692632094485-0 map[location_id:1 position:3 rider:Norem speed:28.8] 0 0}]
    
    	res15, err := rdb.XRangeN(ctx, "race:france",
    		"(1692632094485-0", "+", 2,
    	).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res15)
    	// >>> [{1692632102976-0 map[location_id:1 position:2 rider:Prickett speed:29.7] 0 0} {1692632147973-0 map[location_id:2 position:1 rider:Castilla speed:29.9] 0 0}]
    
    	res16, err := rdb.XRangeN(ctx, "race:france",
    		"(1692632147973-0", "+", 2,
    	).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res16)
    	// >>> []
    
    	res17, err := rdb.XRevRangeN(ctx, "race:france", "+", "-", 1).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res17)
    	// >>> [{1692632147973-0 map[location_id:2 position:1 rider:Castilla speed:29.9] 0 0}]
    
    	res18, err := rdb.XRead(ctx, &redis.XReadArgs{
    		Streams: []string{"race:france", "0"},
    		Count:   2,
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res18)
    	// >>> [{race:france [{1692632086370-0 map[location_id:1 position:1 rider:Castilla speed:30.2] 0 0} {1692632094485-0 map[location_id:1 position:3 rider:Norem speed:28.8] 0 0}]}]
    
    }
    
    func ExampleClient_xgroupcreate() {
    	ctx := context.Background()
    
    	rdb := redis.NewClient(&redis.Options{
    		Addr:     "localhost:6379",
    		Password: "", // no password docs
    		DB:       0,  // use default DB
    	})
    
    
    	_, err := rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:france",
    		Values: map[string]interface{}{
    			"rider":       "Castilla",
    			"speed":       30.2,
    			"position":    1,
    			"location_id": 1,
    		},
    		ID: "1692632086370-0",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	res19, err := rdb.XGroupCreate(ctx, "race:france", "france_riders", "$").Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res19) // >>> OK
    
    }
    
    func ExampleClient_xgroupcreatemkstream() {
    	ctx := context.Background()
    
    	rdb := redis.NewClient(&redis.Options{
    		Addr:     "localhost:6379",
    		Password: "", // no password docs
    		DB:       0,  // use default DB
    	})
    
    
    	res20, err := rdb.XGroupCreateMkStream(ctx,
    		"race:italy", "italy_riders", "$",
    	).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res20) // >>> OK
    
    }
    
    func ExampleClient_xgroupread() {
    	ctx := context.Background()
    
    	rdb := redis.NewClient(&redis.Options{
    		Addr:     "localhost:6379",
    		Password: "", // no password docs
    		DB:       0,  // use default DB
    	})
    
    
    	_, err := rdb.XGroupCreateMkStream(ctx,
    		"race:italy", "italy_riders", "$",
    	).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:italy",
    		Values: map[string]interface{}{"rider": "Castilla"},
    	}).Result()
    	// >>> 1692632639151-0
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:italy",
    		Values: map[string]interface{}{"rider": "Royce"},
    	}).Result()
    	// >>> 1692632647899-0
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:italy",
    		Values: map[string]interface{}{"rider": "Sam-Bodden"},
    	}).Result()
    	// >>> 1692632662819-0
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:italy",
    		Values: map[string]interface{}{"rider": "Prickett"},
    	}).Result()
    	// >>> 1692632670501-0
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:italy",
    		Values: map[string]interface{}{"rider": "Norem"},
    	}).Result()
    	// >>> 1692632678249-0
    
    	if err != nil {
    		panic(err)
    	}
    
    	// fmt.Println(res25)
    
    	res21, err := rdb.XReadGroup(ctx, &redis.XReadGroupArgs{
    		Streams:  []string{"race:italy", ">"},
    		Group:    "italy_riders",
    		Consumer: "Alice",
    		Count:    1,
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	// fmt.Println(res21)
    	// >>> [{race:italy [{1692632639151-0 map[rider:Castilla] 0 0}]}]
    
    
    	xlen, err := rdb.XLen(ctx, "race:italy").Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(xlen)
    
    }
    
    func ExampleClient_raceitaly() {
    	ctx := context.Background()
    
    	rdb := redis.NewClient(&redis.Options{
    		Addr:     "localhost:6379",
    		Password: "", // no password docs
    		DB:       0,  // use default DB
    	})
    
    
    	_, err := rdb.XGroupCreateMkStream(ctx,
    		"race:italy", "italy_riders", "$",
    	).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:italy",
    		Values: map[string]interface{}{"rider": "Castilla"},
    		ID:     "1692632639151-0",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:italy",
    		Values: map[string]interface{}{"rider": "Royce"},
    		ID:     "1692632647899-0",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:italy",
    		Values: map[string]interface{}{"rider": "Sam-Bodden"},
    		ID:     "1692632662819-0",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:italy",
    		Values: map[string]interface{}{"rider": "Prickett"},
    		ID:     "1692632670501-0",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:italy",
    		Values: map[string]interface{}{"rider": "Norem"},
    		ID:     "1692632678249-0",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XReadGroup(ctx, &redis.XReadGroupArgs{
    		Streams:  []string{"race:italy", ">"},
    		Group:    "italy_riders",
    		Consumer: "Alice",
    		Count:    1,
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    	res22, err := rdb.XReadGroup(ctx, &redis.XReadGroupArgs{
    		Streams:  []string{"race:italy", "0"},
    		Group:    "italy_riders",
    		Consumer: "Alice",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res22)
    	// >>> [{race:italy [{1692632639151-0 map[rider:Castilla] 0 0}]}]
    
    	res23, err := rdb.XAck(ctx,
    		"race:italy", "italy_riders", "1692632639151-0",
    	).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res23) // >>> 1
    
    	res24, err := rdb.XReadGroup(ctx, &redis.XReadGroupArgs{
    		Streams:  []string{"race:italy", "0"},
    		Group:    "italy_riders",
    		Consumer: "Alice",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res24)
    	// >>> [{race:italy []}]
    
    	res25, err := rdb.XReadGroup(ctx, &redis.XReadGroupArgs{
    		Streams:  []string{"race:italy", ">"},
    		Group:    "italy_riders",
    		Consumer: "Bob",
    		Count:    2,
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res25)
    	// >>> [{race:italy [{1692632647899-0 map[rider:Royce] 0 0} {1692632662819-0 map[rider:Sam-Bodden] 0 0}]}]
    
    
    	res26, err := rdb.XPending(ctx, "race:italy", "italy_riders").Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res26)
    	// >>> &{2 1692632647899-0 1692632662819-0 map[Bob:2]}
    
    	res27, err := rdb.XPendingExt(ctx, &redis.XPendingExtArgs{
    		Stream: "race:italy",
    		Group:  "italy_riders",
    		Start:  "-",
    		End:    "+",
    		Count:  10,
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	// fmt.Println(res27)
    	// >>> [{1692632647899-0 Bob 0s 1} {1692632662819-0 Bob 0s 1}]
    
    	res28, err := rdb.XRange(ctx, "race:italy",
    		"1692632647899-0", "1692632647899-0",
    	).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res28) // >>> [{1692632647899-0 map[rider:Royce] 0 0}]
    
    	res29, err := rdb.XClaim(ctx, &redis.XClaimArgs{
    		Stream:   "race:italy",
    		Group:    "italy_riders",
    		Consumer: "Alice",
    		MinIdle:  0,
    		Messages: []string{"1692632647899-0"},
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res29)
    
    	res30, res30a, err := rdb.XAutoClaim(ctx, &redis.XAutoClaimArgs{
    		Stream:   "race:italy",
    		Group:    "italy_riders",
    		Consumer: "Alice",
    		Start:    "0-0",
    		Count:    1,
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res30)  // >>> [{1692632647899-0 map[rider:Royce] 0 0}]
    	fmt.Println(res30a) // >>> 1692632662819-0
    
    	res31, res31a, err := rdb.XAutoClaim(ctx, &redis.XAutoClaimArgs{
    		Stream:   "race:italy",
    		Group:    "italy_riders",
    		Consumer: "Lora",
    		Start:    "(1692632662819-0",
    		Count:    1,
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res31)  // >>> []
    	fmt.Println(res31a) // >>> 0-0
    
    	res32, err := rdb.XInfoStream(ctx, "race:italy").Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res32.Length)
    	// >>> 5
    	fmt.Println(res32.FirstEntry)
    	// >>> {1692632639151-0 map[rider:Castilla] 0 0}
    
    	res33, err := rdb.XInfoGroups(ctx, "race:italy").Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res33)
    	// >>> [{italy_riders 3 2 1692632662819-0 3 2}]
    
    	res34, err := rdb.XInfoConsumers(ctx, "race:italy", "italy_riders").Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	// fmt.Println(res34)
    	// >>> [{Alice 1 1ms 1ms} {Bob 1 2ms 2ms} {Lora 0 1ms -1ms}]
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:italy",
    		MaxLen: 2,
    		Values: map[string]interface{}{"rider": "Jones"},
    	},
    	).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:italy",
    		MaxLen: 2,
    		Values: map[string]interface{}{"rider": "Wood"},
    	},
    	).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:italy",
    		MaxLen: 2,
    		Values: map[string]interface{}{"rider": "Henshaw"},
    	},
    	).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	res35, err := rdb.XLen(ctx, "race:italy").Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res35) // >>> 2
    
    	res36, err := rdb.XRange(ctx, "race:italy", "-", "+").Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	// fmt.Println(res36)
    	// >>> [{1726649529170-1 map[rider:Wood] 0 0} {1726649529171-0 map[rider:Henshaw] 0 0}]
    
    	res37, err := rdb.XTrimMaxLen(ctx, "race:italy", 10).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res37) // >>> 0
    
    	res38, err := rdb.XTrimMaxLenApprox(ctx, "race:italy", 10, 20).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res38) // >>> 0
    
    
    }
    
    func ExampleClient_xdel() {
    	ctx := context.Background()
    
    	rdb := redis.NewClient(&redis.Options{
    		Addr:     "localhost:6379",
    		Password: "", // no password docs
    		DB:       0,  // use default DB
    	})
    
    
    	_, err := rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:italy",
    		MaxLen: 2,
    		Values: map[string]interface{}{"rider": "Wood"},
    		ID:     "1692633198206-0",
    	},
    	).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:italy",
    		MaxLen: 2,
    		Values: map[string]interface{}{"rider": "Henshaw"},
    		ID:     "1692633208557-0",
    	},
    	).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	res39, err := rdb.XRangeN(ctx, "race:italy", "-", "+", 2).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res39)
    	// >>> [{1692633198206-0 map[rider:Wood] 0 0} {1692633208557-0 map[rider:Henshaw] 0 0}]
    
    	res40, err := rdb.XDel(ctx, "race:italy", "1692633208557-0").Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res40) // 1
    
    	res41, err := rdb.XRangeN(ctx, "race:italy", "-", "+", 2).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res41)
    	// >>> [{1692633198206-0 map[rider:Wood] 0 0}]
    
    }
    
    
    using NRedisStack.Tests;
    using StackExchange.Redis;
    
    
    
    public class StreamTutorial
    {
        public void Run()
        {
            var muxer = ConnectionMultiplexer.Connect("localhost:6379");
            var db = muxer.GetDatabase();
    
            RedisValue res1 = db.StreamAdd(
                "race:france",
                [
                    new("rider", "Castilla"),
                    new("speed", 30.2),
                    new("position", 1),
                    new("location_id", 1)
                ]
            );
            Console.WriteLine(res1);    // >>> 1712668482289-0
    
            RedisValue res2 = db.StreamAdd(
                "race:france",
                [
                    new("rider", "Norem"),
                    new("speed", 28.8),
                    new("position", 3),
                    new("location_id", 1)
                ]
            );
            Console.WriteLine(res2);    // >>> 1712668766534-1
    
            RedisValue res3 = db.StreamAdd(
                "race:france",
                [
                    new("rider", "Prickett"),
                    new("speed", 29.7),
                    new("position", 2),
                    new("location_id", 1)
                ]
            );
            Console.WriteLine(res3);    // >>> 1712669055705-0
    
    
            // Tests for 'xadd' step.
    
    
            StreamEntry[] res4 = db.StreamRange("race:france", "1712668482289-0", "+", 2);
    
            foreach (StreamEntry entry in res4)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
    
            // >>> 1712668482289-0: [rider: Castilla, speed: 30.199999999999999, position: 1, location_id: 1]
            // >>> 1712668766534-1: [rider: Norem, speed: 28.800000000000001, position: 3, location_id: 1]
    
            // Tests for 'xrange' step.
    
    
            StreamEntry[] res5 = db.StreamRead("race:france", 0, 100);
    
            foreach (StreamEntry entry in res4)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
    
            // >>> 1712668482289-0: [rider: Castilla, speed: 30.199999999999999, position: 1, location_id: 1]
            // >>> 1712668766534-1: [rider: Norem, speed: 28.800000000000001, position: 3, location_id: 1]
            // >>> 1712669055705-0: [rider: Prickett, speed: 29.699999999999999, position: 2, location_id: 1]
    
            // Tests for 'xread_block' step.
    
    
            RedisValue res6 = db.StreamAdd(
                "race:france",
                [
                    new("rider", "Castilla"),
                    new("speed", 29.9),
                    new("position", 1),
                    new("location_id", 2)
                ]
            );
    
            Console.WriteLine(res6);    // >>> 1712675674750-0
    
            // Tests for 'xadd_2' step.
    
    
            long res7 = db.StreamLength("race:france");
            Console.WriteLine(res7);    // >>> 4
    
            // Tests for 'xlen' step.
    
    
            RedisValue res8 = db.StreamAdd(
                "race:usa",
                [
                    new("racer", "Castilla")
                ],
                "0-1"
            );
            Console.WriteLine(res8);    // >>> 0-1
    
            RedisValue res9 = db.StreamAdd(
                "race:usa",
                [
                    new("racer", "Norem")
                ],
                "0-2"
            );
            Console.WriteLine(res9);    // >>> 0-2
    
            // Tests for 'xadd_id' step.
    
    
            try
            {
                RedisValue res10 = db.StreamAdd(
                    "race:usa",
                    [
                        new("racer", "Prickett")
                    ],
                    "0-1"
                );
            }
            catch (RedisServerException ex)
            {
                Console.WriteLine(ex);  // >>> ERR The ID specified in XADD is equal or smaller than the target stream top item
            }
    
            // Tests for 'xadd_bad_id' step.
    
    
            RedisValue res11 = "";
            Version version = muxer.GetServer("localhost:6379").Version;
            if (version.Major >= 7)
            {
                res11 = db.StreamAdd(
                    "race:usa",
                    [
                        new("rider", "Norem")
                    ],
                    "0-*"
                );
    
                Console.WriteLine(res11);   // >>> "0-3"
            }
    
            // Tests for 'xadd_7' step.
    
    
            StreamEntry[] res12 = db.StreamRange("race:france", "-", "+");
    
            foreach (StreamEntry entry in res12)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
            // >>> 1712668482289-0: [rider: Castilla, speed: 30.199999999999999, position: 1, location_id: 1]
            // >>> 1712668766534-1: [rider: Norem, speed: 28.800000000000001, position: 3, location_id: 1]
            // >>> 1712669055705-0: [rider: Prickett, speed: 29.699999999999999, position: 2, location_id: 1]
            // >>> 1712675674750-0: [rider: Castilla, speed: 29.899999999999999, position: 1, location_id: 2]
    
            // Tests for 'xrange_all' step.
    
    
            StreamEntry[] res13 = db.StreamRange("race:france", 1712668482289, 1712668482291);
    
            foreach (StreamEntry entry in res13)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
            // >>> 1712668482289-0: [rider: Castilla, speed: 30.199999999999999, position: 1, location_id: 1]
    
            // Tests for 'xrange_time' step.
    
    
            StreamEntry[] res14 = db.StreamRange("race:france", "-", "+", 2);
    
            foreach (StreamEntry entry in res14)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
            // >>> 1712668482289-0: [rider: Castilla, speed: 30.199999999999999, position: 1, location_id: 1]
            // >>> 1712668766534-1: [rider: Norem, speed: 28.800000000000001, position: 3, location_id: 1]
    
            // Tests for 'xrange_step_1' step.
    
    
            StreamEntry[] res15 = db.StreamRange("race:france", "(1712668766534-1", "+", 2);
    
            foreach (StreamEntry entry in res15)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
            // >>> 1712669055705-0: [rider: Prickett, speed: 29.699999999999999, position: 2, location_id: 1]
            // >>> 1712675674750-0: [rider: Castilla, speed: 29.899999999999999, position: 1, location_id: 2]
    
            // Tests for 'xrange_step_2' step.
    
    
            StreamEntry[] res16 = db.StreamRange("race:france", "(1712675674750-0", "+", 2);
    
            foreach (StreamEntry entry in res16)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
            // >>> <empty array>
    
            // Tests for 'xrange_empty' step.
    
    
            StreamEntry[] res17 = db.StreamRange("race:france", "+", "-", 1, Order.Descending);
    
            foreach (StreamEntry entry in res17)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
            // >>> 1712675674750-0: [rider: Castilla, speed: 29.899999999999999, position: 1, location_id: 2]
    
            // Tests for 'xrevrange' step.
    
    
            StreamEntry[] res18 = db.StreamRead("race:france", 0, 2);
    
            foreach (StreamEntry entry in res18)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
            // >>> 1712668482289-0: [rider: Castilla, speed: 30.199999999999999, position: 1, location_id: 1]
            // >>> 1712668766534-1: [rider: Norem, speed: 28.800000000000001, position: 3, location_id: 1]
    
            // Tests for 'xread' step.
    
    
            bool res19 = db.StreamCreateConsumerGroup("race:france", "france_riders", "$");
            Console.WriteLine(res19);   // >>> true
    
            // Tests for 'xgroup_create' step.
    
    
            bool res20 = db.StreamCreateConsumerGroup("race:italy", "italy_riders", "$", true);
            Console.WriteLine(res20);   // >>> true
    
            // Tests for 'xgroup_create_mkstream' step.
    
    
            RedisValue groupRes = db.StreamAdd(
                "race:italy",
                [new("rider", "Castilla")]
            ); // 1712744323758-0
    
            groupRes = db.StreamAdd(
                "race:italy",
                [new("rider", "Royce")]
            ); // 1712744358384-0
    
            groupRes = db.StreamAdd(
                "race:italy",
                [new("rider", "Sam-Bodden")]
            ); // 1712744379676-0
    
            groupRes = db.StreamAdd(
                "race:italy",
                [new("rider", "Prickett")]
            ); // 1712744399401-0
    
            groupRes = db.StreamAdd(
                "race:italy",
                [new("rider", "Norem")]
            ); // 1712744413117-0
    
            StreamEntry[] res21 = db.StreamReadGroup("race:italy", "italy_riders", "Alice", ">", 1);
    
            foreach (StreamEntry entry in res21)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
            // >>> 1712744323758-0: [rider: Castilla]
    
            // Tests for 'xgroup_read' step.
    
    
            StreamEntry[] res22 = db.StreamReadGroup("race:italy", "italy_riders", "Alice", "0");
    
            foreach (StreamEntry entry in res22)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
                // >>> 1712744323758-0: [rider: Castilla]
            }
    
            // Tests for 'xgroup_read_id' step.
    
    
            long res23 = db.StreamAcknowledge("race:italy", "italy_riders", "1712744323758-0");
            Console.WriteLine(res23);   // >>> 1
    
            StreamEntry[] res24 = db.StreamReadGroup("race:italy", "italy_riders", "Alice", "0");
    
            foreach (StreamEntry entry in res24)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
            // >>> <empty array>
    
            // Tests for 'xack' step.
    
    
            StreamEntry[] res25 = db.StreamReadGroup("race:italy", "italy_riders", "Bob", ">", 2);
    
            foreach (StreamEntry entry in res25)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
            // >>> 1712744358384-0: [rider: Royce]
            // >>> 1712744379676-0: [rider: Sam-Bodden]
    
            // Tests for 'xgroup_read_bob' step.
    
    
            StreamPendingInfo res26 = db.StreamPending("race:italy", "italy_riders");
            Console.WriteLine($"pending: {res26.PendingMessageCount}, min: {res26.LowestPendingMessageId}, max: {res26.HighestPendingMessageId}, consumers:[{string.Join(", ", res26.Consumers.Select(c => $"{c.Name}: {c.PendingMessageCount}"))}]");
            // >>> pending: 2, min: 1712747506906-0, max: 1712747506907-0, consumers:[name: Bob, pending:2]
    
            // Tests for 'xpending' step.
    
    
            StreamPendingMessageInfo[] res27 = db.StreamPendingMessages(
                "race:italy", "italy_riders", 10, "", "-", "+"
            );
    
            foreach (StreamPendingMessageInfo info in res27)
            {
                Console.WriteLine($"message_id: {info.MessageId}, consumer: {info.ConsumerName}, time_since_delivered: {info.IdleTimeInMilliseconds}, times_delivered: {info.DeliveryCount}");
            }
            // >>> message_id: min: 1712747506906-0, consumer: Bob, time_since_delivered: 31084, times_delivered: 1
            // >>> message_id: min: 1712747506907-0, consumer: Bob, time_since_delivered: 31084, times_delivered: 1
    
            // Tests for 'xpending_plus_minus' step.
    
    
            StreamEntry[] res28 = db.StreamRange("race:italy", "1712744358384-0", "1712744358384-0");
    
            foreach (StreamEntry entry in res28)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
            // >>> 1712744358384-0: [rider: Royce]
    
            // Tests for 'xrange_pending' step.
    
    
            StreamEntry[] res29 = db.StreamClaim(
                "race:italy", "italy_riders", "Alice", 60000, [1712744358384 - 0]
            );
    
            foreach (StreamEntry entry in res29)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
            // >>> 1712744358384-0: [rider: Royce]
    
            // Tests for 'xclaim' step.
    
            StreamAutoClaimResult res30 = db.StreamAutoClaim(
                "race:italy", "italy_riders", "Alice", 1, "0-0", 1
            );
    
            Console.WriteLine($"{res30.NextStartId}, ({string.Join(", ", res30.ClaimedEntries.Select(entry => $"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]"))})");
            // >>> 1712744379676-0, (1712744358384-0: [rider: Royce])
    
            // Tests for 'xautoclaim' step.
    
    
            StreamAutoClaimResult res31 = db.StreamAutoClaim(
                "race:italy", "italy_riders", "Alice", 1, "(1712744358384-0", 1
            );
    
            Console.WriteLine($"{res31.NextStartId}, ({string.Join(", ", res31.ClaimedEntries.Select(entry => $"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]"))})");
            // >>> 0-0, (1712744379676-0: [rider: Sam-Bodden])
    
            // Tests for 'xautoclaim_cursor' step.
    
    
            StreamInfo res32 = db.StreamInfo("race:italy");
            Console.WriteLine($"length: {res32.Length}, radix-tree-keys: {res32.RadixTreeKeys}, radix-tree-nodes: {res32.RadixTreeNodes}, last-generated-id: {res32.LastGeneratedId}, first-entry: {$"{res32.FirstEntry.Id}: [{string.Join(", ", res32.FirstEntry.Values.Select(b => $"{b.Name}: {b.Value}"))}]"}, last-entry: {$"{res32.LastEntry.Id}: [{string.Join(", ", res32.LastEntry.Values.Select(b => $"{b.Name}: {b.Value}"))}]"}");
            // >>> length: 5, radix-tree-keys: 1, radix-tree-nodes: 2, last-generated-id: 1712756762686-1, first-entry: 1712756762685-0: [rider: Castilla], last-entry: 1712756762686-1: [rider: Norem]
    
            // Tests for 'xinfo' step.
    
    
            StreamGroupInfo[] res33 = db.StreamGroupInfo("race:italy");
    
            foreach (StreamGroupInfo info in res33)
            {
                Console.WriteLine($"name: {info.Name}, consumers: {info.ConsumerCount}, pending: {info.PendingMessageCount}, last-delivered-id: {info.LastDeliveredId}");
            }
            // >>> name: italy_riders, consumers: 2, pending: 2, last-delivered-id: 1712757192730-2
    
            // Tests for 'xinfo_groups' step.
    
    
            StreamConsumerInfo[] res34 = db.StreamConsumerInfo("race:italy", "italy_riders");
    
            foreach (StreamConsumerInfo info in res34)
            {
                Console.WriteLine($"name: {info.Name}, pending: {info.PendingMessageCount}, idle: {info.IdleTimeInMilliseconds}");
            }
            // >>> name: Alice, pending: 1, idle: 7717
            // >>> name: Bob, pending: 0, idle: 7722
    
            // Tests for 'xinfo_consumers' step.
    
    
            db.StreamAdd(
                "race:italy", [new("rider", "Jones")], null, 2, true
            );
    
            db.StreamAdd(
                "race:italy", [new("rider", "Wood")], null, 2, true
            );
    
            db.StreamAdd(
                "race:italy", [new("rider", "Henshaw")], null, 2, true
            );
    
            long res35 = db.StreamLength("race:italy");
            Console.WriteLine(res35); // >>> 8
    
            StreamEntry[] res36 = db.StreamRange("race:italy", "-", "+");
    
            foreach (StreamEntry entry in res36)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
            // >>> 1712758336128-0: [rider: Castilla]
            // >>> 1712758336128-1: [rider: Royce]
            // >>> 1712758336128-2: [rider: Sam-Bodden]
            // >>> 1712758336129-0: [rider: Prickett]
            // >>> 1712758336139-0: [rider: Norem]
            // >>> 1712758340854-0: [rider: Jones]
            // >>> 1712758341645-0: [rider: Wood]
            // >>> 1712758342134-0: [rider: Henshaw]
    
            db.StreamAdd(
                "race:italy", [new("rider", "Smith")], null, 2, false
            );
    
            StreamEntry[] res37 = db.StreamRange("race:italy", "-", "+");
    
            foreach (StreamEntry entry in res37)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
            // 1712758746476-1: [rider: Henshaw]
            // 1712758746477-0: [rider: Smith]
    
            // Tests for 'maxlen' step.
    
    
            long res38 = db.StreamTrim("race:italy", 10, false);
            Console.WriteLine(res38);   // >>> 0
    
            // Tests for 'xtrim' step.
    
    
            long res39 = db.StreamTrim("race:italy", 10, true);
            Console.WriteLine(res39);   // >>> 0
    
            // Tests for 'xtrim2' step.
    
    
            StreamEntry[] res40 = db.StreamRange("race:italy", "-", "+");
    
            foreach (StreamEntry entry in res40)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
            // >>> 1712759694003-0: [rider: Henshaw]
            // >>> 1712759694003-1: [rider: Smith]
    
            long res41 = db.StreamDelete("race:italy", ["1712759694003-1"]);
            Console.WriteLine(res41);   // >>> 1
    
            StreamEntry[] res42 = db.StreamRange("race:italy", "-", "+");
    
            foreach (StreamEntry entry in res42)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
    
            }
            // >>> 1712759694003-0: [rider: Henshaw]
    
            // Tests for 'xdel' step.
    
    
        }
    }
    
    
    require 'redis'
    
    r = Redis.new
    
    
    res1 = r.xadd('race:france', {
      'rider' => 'Castilla',
      'speed' => 30.2,
      'position' => 1,
      'location_id' => 1
    })
    puts res1 # 1692632086370-0, for example
    
    res2 = r.xadd('race:france', {
      'rider' => 'Norem',
      'speed' => 28.8,
      'position' => 3,
      'location_id' => 1
    })
    puts res2 # 1692632094485-0, for example
    
    res3 = r.xadd('race:france', {
      'rider' => 'Prickett',
      'speed' => 29.7,
      'position' => 2,
      'location_id' => 1
    })
    puts res3 # 1692632102976-0, for example
    
    
    r.del('race:france')
    r.xadd('race:france', {
      'rider' => 'Castilla',
      'speed' => '30.2',
      'position' => '1',
      'location_id' => '1'
    }, id: '1692632086370-0')
    r.xadd('race:france', {
      'rider' => 'Norem',
      'speed' => '28.8',
      'position' => '3',
      'location_id' => '1'
    }, id: '1692632094485-0')
    r.xadd('race:france', {
      'rider' => 'Prickett',
      'speed' => '29.7',
      'position' => '2',
      'location_id' => '1'
    }, id: '1692632102976-0')
    r.xadd('race:france', {
      'rider' => 'Castilla',
      'speed' => '29.9',
      'position' => '1',
      'location_id' => '2'
    }, id: '1692632147973-0')
    res4 = r.xrange('race:france', '1692632086370-0', '+', count: 2)
    puts res4.inspect
    # [["1692632086370-0", {"rider"=>"Castilla", "speed"=>"30.2", "position"=>"1", "location_id"=>"1"}],
    #  ["1692632094485-0", {"rider"=>"Norem", "speed"=>"28.8", "position"=>"3", "location_id"=>"1"}]]
    
    
    r.del('race:france')
    r.xadd('race:france', {'rider' => 'Castilla'}, id: '1692632086370-0')
    res5 = r.xread(['race:france'], ['$'], count: 100, block: 300)
    puts res5.inspect # {}
    
    
    res6 = r.xadd('race:france', {
      'rider' => 'Castilla',
      'speed' => 29.9,
      'position' => 1,
      'location_id' => 2
    })
    puts res6 # 1692632147973-0, for example
    
    
    r.del('race:france')
    r.xadd('race:france', {'rider' => 'Castilla'}, id: '1692632086370-0')
    r.xadd('race:france', {'rider' => 'Norem'}, id: '1692632094485-0')
    r.xadd('race:france', {'rider' => 'Prickett'}, id: '1692632102976-0')
    r.xadd('race:france', {'rider' => 'Castilla'}, id: '1692632147973-0')
    res7 = r.xlen('race:france')
    puts res7 # 4
    
    
    r.del('race:usa')
    res8 = r.xadd('race:usa', {'racer' => 'Castilla'}, id: '0-1')
    puts res8 # 0-1
    
    res9 = r.xadd('race:usa', {'racer' => 'Norem'}, id: '0-2')
    puts res9 # 0-2
    
    
    begin
      r.xadd('race:usa', {'racer' => 'Prickett'}, id: '0-1')
    rescue Redis::CommandError => e
      puts e.message
      # ERR The ID specified in XADD is equal or smaller than the target stream top item
    end
    
    
    r.del('race:usa')
    r.xadd('race:usa', {'racer' => 'Castilla'}, id: '0-1')
    r.xadd('race:usa', {'racer' => 'Norem'}, id: '0-2')
    res10 = r.xadd('race:usa', {'racer' => 'Prickett'}, id: '0-*')
    puts res10 # 0-3
    
    
    r.del('race:france')
    r.xadd('race:france', {'rider' => 'Castilla', 'speed' => '30.2', 'position' => '1', 'location_id' => '1'}, id: '1692632086370-0')
    r.xadd('race:france', {'rider' => 'Norem', 'speed' => '28.8', 'position' => '3', 'location_id' => '1'}, id: '1692632094485-0')
    r.xadd('race:france', {'rider' => 'Prickett', 'speed' => '29.7', 'position' => '2', 'location_id' => '1'}, id: '1692632102976-0')
    r.xadd('race:france', {'rider' => 'Castilla', 'speed' => '29.9', 'position' => '1', 'location_id' => '2'}, id: '1692632147973-0')
    res11 = r.xrange('race:france', '-', '+')
    puts res11.inspect
    # [["1692632086370-0", {"rider"=>"Castilla", "speed"=>"30.2", "position"=>"1", "location_id"=>"1"}],
    #  ["1692632094485-0", {"rider"=>"Norem", "speed"=>"28.8", "position"=>"3", "location_id"=>"1"}],
    #  ["1692632102976-0", {"rider"=>"Prickett", "speed"=>"29.7", "position"=>"2", "location_id"=>"1"}],
    #  ["1692632147973-0", {"rider"=>"Castilla", "speed"=>"29.9", "position"=>"1", "location_id"=>"2"}]]
    
    
    r.del('race:france')
    r.xadd('race:france', {'rider' => 'Castilla', 'speed' => '30.2', 'position' => '1', 'location_id' => '1'}, id: '1692632086370-0')
    r.xadd('race:france', {'rider' => 'Norem', 'speed' => '28.8', 'position' => '3', 'location_id' => '1'}, id: '1692632094485-0')
    res12 = r.xrange('race:france', '1692632086369', '1692632086371')
    puts res12.inspect
    # [["1692632086370-0", {"rider"=>"Castilla", "speed"=>"30.2", "position"=>"1", "location_id"=>"1"}]]
    
    
    r.del('race:france')
    r.xadd('race:france', {'rider' => 'Castilla', 'speed' => '30.2', 'position' => '1', 'location_id' => '1'}, id: '1692632086370-0')
    r.xadd('race:france', {'rider' => 'Norem', 'speed' => '28.8', 'position' => '3', 'location_id' => '1'}, id: '1692632094485-0')
    r.xadd('race:france', {'rider' => 'Prickett', 'speed' => '29.7', 'position' => '2', 'location_id' => '1'}, id: '1692632102976-0')
    r.xadd('race:france', {'rider' => 'Castilla', 'speed' => '29.9', 'position' => '1', 'location_id' => '2'}, id: '1692632147973-0')
    res13 = r.xrange('race:france', '-', '+', count: 2)
    puts res13.inspect
    # [["1692632086370-0", {"rider"=>"Castilla", "speed"=>"30.2", "position"=>"1", "location_id"=>"1"}],
    #  ["1692632094485-0", {"rider"=>"Norem", "speed"=>"28.8", "position"=>"3", "location_id"=>"1"}]]
    
    
    r.del('race:france')
    r.xadd('race:france', {'rider' => 'Castilla', 'speed' => '30.2', 'position' => '1', 'location_id' => '1'}, id: '1692632086370-0')
    r.xadd('race:france', {'rider' => 'Norem', 'speed' => '28.8', 'position' => '3', 'location_id' => '1'}, id: '1692632094485-0')
    r.xadd('race:france', {'rider' => 'Prickett', 'speed' => '29.7', 'position' => '2', 'location_id' => '1'}, id: '1692632102976-0')
    r.xadd('race:france', {'rider' => 'Castilla', 'speed' => '29.9', 'position' => '1', 'location_id' => '2'}, id: '1692632147973-0')
    res14 = r.xrange('race:france', '(1692632094485-0', '+', count: 2)
    puts res14.inspect
    # [["1692632102976-0", {"rider"=>"Prickett", "speed"=>"29.7", "position"=>"2", "location_id"=>"1"}],
    #  ["1692632147973-0", {"rider"=>"Castilla", "speed"=>"29.9", "position"=>"1", "location_id"=>"2"}]]
    
    
    res15 = r.xrange('race:france', '(1692632147973-0', '+', count: 2)
    puts res15.inspect # []
    
    
    res16 = r.xrevrange('race:france', '+', '-', count: 1)
    puts res16.inspect
    # [["1692632147973-0", {"rider"=>"Castilla", "speed"=>"29.9", "position"=>"1", "location_id"=>"2"}]]
    
    
    res17 = r.xread(['race:france'], ['0'], count: 2)
    puts res17.inspect
    # {"race:france"=>[["1692632086370-0", {"rider"=>"Castilla", "speed"=>"30.2", "position"=>"1", "location_id"=>"1"}],
    #                  ["1692632094485-0", {"rider"=>"Norem", "speed"=>"28.8", "position"=>"3", "location_id"=>"1"}]]}
    
    
    r.del('race:france')
    r.xadd('race:france', {'rider' => 'Castilla'}, id: '1692632086370-0')
    res18 = r.xgroup(:create, 'race:france', 'france_riders', '$')
    puts res18 # OK
    
    
    r.del('race:italy')
    res19 = r.xgroup(:create, 'race:italy', 'italy_riders', '$', mkstream: true)
    puts res19 # OK
    
    
    r.del('race:italy')
    r.xgroup(:create, 'race:italy', 'italy_riders', '$', mkstream: true)
    r.xadd('race:italy', {'rider' => 'Castilla'}, id: '1692632639151-0')
    r.xadd('race:italy', {'rider' => 'Royce'}, id: '1692632647899-0')
    r.xadd('race:italy', {'rider' => 'Sam-Bodden'}, id: '1692632662819-0')
    r.xadd('race:italy', {'rider' => 'Prickett'}, id: '1692632670501-0')
    r.xadd('race:italy', {'rider' => 'Norem'}, id: '1692632678249-0')
    
    res20 = r.xreadgroup('italy_riders', 'Alice', ['race:italy'], ['>'], count: 1)
    puts res20.inspect
    # {"race:italy"=>[["1692632639151-0", {"rider"=>"Castilla"}]]}
    
    
    res21 = r.xreadgroup('italy_riders', 'Alice', ['race:italy'], ['0'], count: 1)
    puts res21.inspect
    # {"race:italy"=>[["1692632639151-0", {"rider"=>"Castilla"}]]}
    
    
    res22 = r.xack('race:italy', 'italy_riders', '1692632639151-0')
    puts res22 # 1
    
    res23 = r.xreadgroup('italy_riders', 'Alice', ['race:italy'], ['0'])
    puts res23.inspect
    # {"race:italy"=>[]}
    
    
    res24 = r.xreadgroup('italy_riders', 'Bob', ['race:italy'], ['>'], count: 2)
    puts res24.inspect
    # {"race:italy"=>[["1692632647899-0", {"rider"=>"Royce"}],
    #                 ["1692632662819-0", {"rider"=>"Sam-Bodden"}]]}
    
    
    res25 = r.xpending('race:italy', 'italy_riders')
    puts res25.inspect
    # {"size"=>2, "min_entry_id"=>"1692632647899-0", "max_entry_id"=>"1692632662819-0", "consumers"=>{"Bob"=>"2"}}
    
    
    res26 = r.xpending('race:italy', 'italy_riders', '-', '+', 10)
    puts res26.inspect
    
    
    res27 = r.xrange('race:italy', '1692632647899-0', '1692632647899-0')
    puts res27.inspect
    # [["1692632647899-0", {"rider"=>"Royce"}]]
    
    
    res28 = r.xclaim('race:italy', 'italy_riders', 'Alice', 0, '1692632647899-0')
    puts res28.inspect
    # [["1692632647899-0", {"rider"=>"Royce"}]]
    
    
    res29 = r.xautoclaim('race:italy', 'italy_riders', 'Alice', 0, '0-0', count: 1)
    puts res29.inspect
    # {"next"=>"1692632662819-0", "entries"=>[["1692632647899-0", {"rider"=>"Royce"}]]}
    
    
    res30 = r.xautoclaim('race:italy', 'italy_riders', 'Lora', 0, res29['next'], count: 1)
    puts res30.inspect
    # {"next"=>"0-0", "entries"=>[["1692632662819-0", {"rider"=>"Sam-Bodden"}]]}
    
    
    res31 = r.xinfo(:stream, 'race:italy')
    puts res31.inspect
    
    
    res32 = r.xinfo(:groups, 'race:italy')
    puts res32.inspect
    
    
    res33 = r.xinfo(:consumers, 'race:italy', 'italy_riders')
    puts res33.inspect
    
    
    r.del('race:italy')
    r.xadd('race:italy', {'rider' => 'Castilla'}, id: '1692632639151-0')
    r.xadd('race:italy', {'rider' => 'Royce'}, id: '1692632647899-0')
    r.xadd('race:italy', {'rider' => 'Sam-Bodden'}, id: '1692632662819-0')
    r.xadd('race:italy', {'rider' => 'Prickett'}, id: '1692632670501-0')
    r.xadd('race:italy', {'rider' => 'Norem'}, id: '1692632678249-0')
    r.xadd('race:italy', {'rider' => 'Jones'}, id: '1692633189161-0', maxlen: 2)
    r.xadd('race:italy', {'rider' => 'Wood'}, id: '1692633198206-0', maxlen: 2)
    r.xadd('race:italy', {'rider' => 'Henshaw'}, id: '1692633208557-0', maxlen: 2)
    
    res34 = r.xlen('race:italy')
    puts res34 # 2
    
    res35 = r.xrange('race:italy', '-', '+')
    puts res35.inspect
    # [["1692633198206-0", {"rider"=>"Wood"}], ["1692633208557-0", {"rider"=>"Henshaw"}]]
    
    
    res36 = r.xtrim('race:italy', 10, approximate: false)
    puts res36 # 0
    
    
    r.del('mystream')
    1.upto(10) do |n|
      r.xadd('mystream', {'field' => 'value'}, id: "#{n}-0")
    end
    res37 = r.xtrim('mystream', 10, approximate: true)
    puts res37 # 0
    
    
    r.del('race:italy')
    r.xadd('race:italy', {'rider' => 'Wood'}, id: '1692633198206-0')
    r.xadd('race:italy', {'rider' => 'Henshaw'}, id: '1692633208557-0')
    res38 = r.xrange('race:italy', '-', '+', count: 2)
    puts res38.inspect
    # [["1692633198206-0", {"rider"=>"Wood"}], ["1692633208557-0", {"rider"=>"Henshaw"}]]
    
    res39 = r.xdel('race:italy', '1692633208557-0')
    puts res39 # 1
    
    res40 = r.xrange('race:italy', '-', '+', count: 2)
    puts res40.inspect
    # [["1692633198206-0", {"rider"=>"Wood"}]]
    
    
    mod stream_tests {
        use redis::{
            streams::{
                StreamAutoClaimOptions, StreamInfoConsumersReply, StreamInfoGroupsReply,
                StreamInfoStreamReply, StreamMaxlen, StreamPendingCountReply, StreamPendingReply,
                StreamRangeReply, StreamReadOptions, StreamReadReply, StreamTrimmingMode,
                StreamTrimOptions,
            },
            Commands,
        };
        use std::{thread::sleep, time::Duration};
    
        fn delete_keys(r: &mut redis::Connection, keys: &[&str]) {
            let _: usize = r.del(keys).unwrap_or(0);
        }
    
        fn add_france_fixed(r: &mut redis::Connection) {
            delete_keys(r, &["race:france"]);
            let _: Option<String> = r
                .xadd(
                    "race:france",
                    "1692632086370-0",
                    &[
                        ("rider", "Castilla"),
                        ("speed", "30.2"),
                        ("position", "1"),
                        ("location_id", "1"),
                    ],
                )
                .expect("add france 1");
            let _: Option<String> = r
                .xadd(
                    "race:france",
                    "1692632094485-0",
                    &[
                        ("rider", "Norem"),
                        ("speed", "28.8"),
                        ("position", "3"),
                        ("location_id", "1"),
                    ],
                )
                .expect("add france 2");
            let _: Option<String> = r
                .xadd(
                    "race:france",
                    "1692632102976-0",
                    &[
                        ("rider", "Prickett"),
                        ("speed", "29.7"),
                        ("position", "2"),
                        ("location_id", "1"),
                    ],
                )
                .expect("add france 3");
            let _: Option<String> = r
                .xadd(
                    "race:france",
                    "1692632147973-0",
                    &[
                        ("rider", "Castilla"),
                        ("speed", "29.9"),
                        ("position", "1"),
                        ("location_id", "2"),
                    ],
                )
                .expect("add france 4");
        }
    
        fn seed_usa_fixed(r: &mut redis::Connection) {
            delete_keys(r, &["race:usa"]);
            let _: Option<String> = r
                .xadd("race:usa", "0-1", &[("racer", "Castilla")])
                .expect("add usa 1");
            let _: Option<String> = r
                .xadd("race:usa", "0-2", &[("racer", "Norem")])
                .expect("add usa 2");
        }
    
        fn seed_italy_group_base(r: &mut redis::Connection) {
            delete_keys(r, &["race:italy"]);
            let _: () = r
                .xgroup_create_mkstream("race:italy", "italy_riders", "$")
                .expect("create italy group");
            let _: Option<String> = r
                .xadd("race:italy", "1692632639151-0", &[("rider", "Castilla")])
                .expect("add italy 1");
            let _: Option<String> = r
                .xadd("race:italy", "1692632647899-0", &[("rider", "Royce")])
                .expect("add italy 2");
            let _: Option<String> = r
                .xadd("race:italy", "1692632662819-0", &[("rider", "Sam-Bodden")])
                .expect("add italy 3");
            let _: Option<String> = r
                .xadd("race:italy", "1692632670501-0", &[("rider", "Prickett")])
                .expect("add italy 4");
            let _: Option<String> = r
                .xadd("race:italy", "1692632678249-0", &[("rider", "Norem")])
                .expect("add italy 5");
        }
    
        fn seed_italy_alice_pending(r: &mut redis::Connection) {
            seed_italy_group_base(r);
            let opts = StreamReadOptions::default().group("italy_riders", "Alice").count(1);
            let _: Option<StreamReadReply> = r
                .xread_options(&["race:italy"], &[">"], &opts)
                .expect("alice read pending");
        }
    
        fn seed_italy_after_ack(r: &mut redis::Connection) {
            seed_italy_alice_pending(r);
            let _: usize = r
                .xack("race:italy", "italy_riders", &["1692632639151-0"])
                .expect("ack first italy message");
        }
    
        fn seed_italy_bob_pending(r: &mut redis::Connection) {
            seed_italy_after_ack(r);
            let opts = StreamReadOptions::default().group("italy_riders", "Bob").count(2);
            let _: Option<StreamReadReply> = r
                .xread_options(&["race:italy"], &[">"], &opts)
                .expect("bob read pending");
        }
    
        fn seed_italy_info_state(r: &mut redis::Connection) {
            seed_italy_bob_pending(r);
            sleep(Duration::from_millis(5));
            let _: redis::streams::StreamClaimReply = r
                .xclaim("race:italy", "italy_riders", "Alice", 1, &["1692632647899-0"])
                .expect("alice claim");
            sleep(Duration::from_millis(5));
            let _: redis::streams::StreamClaimReply = r
                .xclaim("race:italy", "italy_riders", "Lora", 1, &["1692632662819-0"])
                .expect("lora claim");
        }
    
        fn seed_trim_stream(r: &mut redis::Connection) {
            delete_keys(r, &["mystream"]);
            for id in ["1-0", "2-0", "3-0", "4-0", "5-0", "6-0", "7-0", "8-0", "9-0", "10-0"] {
                let _: Option<String> = r
                    .xadd("mystream", id, &[("field", "value")])
                    .expect("seed mystream");
            }
        }
    
        fn run() {
            let mut r = match redis::Client::open("redis://127.0.0.1") {
                Ok(client) => match client.get_connection() {
                    Ok(conn) => conn,
                    Err(e) => {
                        println!("Failed to connect to Redis: {e}");
                        return;
                    }
                },
                Err(e) => {
                    println!("Failed to create Redis client: {e}");
                    return;
                }
            };
    
            let res1 = {
                let res: Option<String> = r
                    .xadd(
                        "race:france",
                        "*",
                        &[
                            ("rider", "Castilla"),
                            ("speed", "30.2"),
                            ("position", "1"),
                            ("location_id", "1"),
                        ],
                    )
                    .expect("xadd 1");
                res.expect("missing stream id")
            };
            println!("{res1}"); // >>> 1692632086370-0
    
            let res2 = {
                let res: Option<String> = r
                    .xadd(
                        "race:france",
                        "*",
                        &[
                            ("rider", "Norem"),
                            ("speed", "28.8"),
                            ("position", "3"),
                            ("location_id", "1"),
                        ],
                    )
                    .expect("xadd 2");
                res.expect("missing stream id")
            };
            println!("{res2}"); // >>> 1692632094485-0
    
            let res3 = {
                let res: Option<String> = r
                    .xadd(
                        "race:france",
                        "*",
                        &[
                            ("rider", "Prickett"),
                            ("speed", "29.7"),
                            ("position", "2"),
                            ("location_id", "1"),
                        ],
                    )
                    .expect("xadd 3");
                res.expect("missing stream id")
            };
            println!("{res3}"); // >>> 1692632102976-0
    
            add_france_fixed(&mut r);
            if let Ok(res) = r.xrange_count("race:france", "1692632086370-0", "+", 2) {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![
                                ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                (
                                    "location_id".to_string(),
                                    entry.get::<String>("location_id").expect("missing location_id"),
                                ),
                            ],
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("1692632086370-0", [("rider", "Castilla"), ("speed", "30.2"), ("position", "1"), ("location_id", "1")]), ("1692632094485-0", [("rider", "Norem"), ("speed", "28.8"), ("position", "3"), ("location_id", "1")])]
            }
    
            add_france_fixed(&mut r);
            let opts = StreamReadOptions::default().count(100).block(300);
            if let Ok(res) = r.xread_options(&["race:france"], &["$"], &opts) {
                let res: Option<StreamReadReply> = res;
                println!("{res:?}"); // >>> None
            }
    
            if let Ok(res) = r.xadd(
                "race:france",
                "*",
                &[
                    ("rider", "Castilla"),
                    ("speed", "29.9"),
                    ("position", "1"),
                    ("location_id", "2"),
                ],
            ) {
                let res: Option<String> = res;
                let res = res.expect("missing stream id");
                println!("{res}"); // >>> 1692632147973-0
            }
    
            add_france_fixed(&mut r);
            if let Ok(res) = r.xlen("race:france") {
                let res: usize = res;
                println!("{res}"); // >>> 4
            }
    
            delete_keys(&mut r, &["race:usa"]);
            if let Ok(res) = r.xadd("race:usa", "0-1", &[("racer", "Castilla")]) {
                let res: Option<String> = res;
                let res = res.expect("missing stream id");
                println!("{res}"); // >>> 0-1
            }
    
            if let Ok(res) = r.xadd("race:usa", "0-2", &[("racer", "Norem")]) {
                let res: Option<String> = res;
                let res = res.expect("missing stream id");
                println!("{res}"); // >>> 0-2
            }
    
            let res: redis::RedisResult<Option<String>> =
                r.xadd("race:usa", "0-1", &[("racer", "Prickett")]);
            match res {
                Ok(_) => {}
                Err(e) => {
                    let msg = e.to_string();
                    println!("{msg}");
                    // >>> An error was signalled by the server - ResponseError: The ID specified in XADD is equal or smaller than the target stream top item
                }
            }
    
            seed_usa_fixed(&mut r);
            if let Ok(res) = r.xadd("race:usa", "0-*", &[("racer", "Prickett")]) {
                let res: Option<String> = res;
                let res = res.expect("missing stream id");
                println!("{res}"); // >>> 0-3
            }
    
            add_france_fixed(&mut r);
            if let Ok(res) = r.xrange_all("race:france") {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![
                                ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                (
                                    "location_id".to_string(),
                                    entry.get::<String>("location_id").expect("missing location_id"),
                                ),
                            ],
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("1692632086370-0", [("rider", "Castilla"), ("speed", "30.2"), ("position", "1"), ("location_id", "1")]), ("1692632094485-0", [("rider", "Norem"), ("speed", "28.8"), ("position", "3"), ("location_id", "1")]), ("1692632102976-0", [("rider", "Prickett"), ("speed", "29.7"), ("position", "2"), ("location_id", "1")]), ("1692632147973-0", [("rider", "Castilla"), ("speed", "29.9"), ("position", "1"), ("location_id", "2")])]
            }
    
            add_france_fixed(&mut r);
            if let Ok(res) = r.xrange("race:france", "1692632086369", "1692632086371") {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![
                                ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                (
                                    "location_id".to_string(),
                                    entry.get::<String>("location_id").expect("missing location_id"),
                                ),
                            ],
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("1692632086370-0", [("rider", "Castilla"), ("speed", "30.2"), ("position", "1"), ("location_id", "1")])]
            }
    
            add_france_fixed(&mut r);
            if let Ok(res) = r.xrange_count("race:france", "-", "+", 2) {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![
                                ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                (
                                    "location_id".to_string(),
                                    entry.get::<String>("location_id").expect("missing location_id"),
                                ),
                            ],
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("1692632086370-0", [("rider", "Castilla"), ("speed", "30.2"), ("position", "1"), ("location_id", "1")]), ("1692632094485-0", [("rider", "Norem"), ("speed", "28.8"), ("position", "3"), ("location_id", "1")])]
            }
    
            add_france_fixed(&mut r);
            if let Ok(res) = r.xrange_count("race:france", "(1692632094485-0", "+", 2) {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![
                                ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                (
                                    "location_id".to_string(),
                                    entry.get::<String>("location_id").expect("missing location_id"),
                                ),
                            ],
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("1692632102976-0", [("rider", "Prickett"), ("speed", "29.7"), ("position", "2"), ("location_id", "1")]), ("1692632147973-0", [("rider", "Castilla"), ("speed", "29.9"), ("position", "1"), ("location_id", "2")])]
            }
    
            add_france_fixed(&mut r);
            if let Ok(res) = r.xrange_count("race:france", "(1692632147973-0", "+", 2) {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![
                                ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                (
                                    "location_id".to_string(),
                                    entry.get::<String>("location_id").expect("missing location_id"),
                                ),
                            ],
                        )
                    })
                    .collect();
                println!("{view:?}"); // >>> []
            }
    
            add_france_fixed(&mut r);
            if let Ok(res) = r.xrevrange_count("race:france", "+", "-", 1) {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![
                                ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                (
                                    "location_id".to_string(),
                                    entry.get::<String>("location_id").expect("missing location_id"),
                                ),
                            ],
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("1692632147973-0", [("rider", "Castilla"), ("speed", "29.9"), ("position", "1"), ("location_id", "2")])]
            }
    
            add_france_fixed(&mut r);
            let opts = StreamReadOptions::default().count(2);
            if let Ok(res) = r.xread_options(&["race:france"], &["0"], &opts) {
                let res: Option<StreamReadReply> = res;
                let reply = res.expect("xread should return data");
                let view: Vec<_> = reply
                    .keys
                    .iter()
                    .map(|stream| {
                        (
                            stream.key.clone(),
                            stream
                                .ids
                                .iter()
                                .map(|entry| {
                                    (
                                        entry.id.clone(),
                                        vec![
                                            ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                            ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                            ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                            (
                                                "location_id".to_string(),
                                                entry.get::<String>("location_id").expect("missing location_id"),
                                            ),
                                        ],
                                    )
                                })
                                .collect::<Vec<_>>(),
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("race:france", [("1692632086370-0", [("rider", "Castilla"), ("speed", "30.2"), ("position", "1"), ("location_id", "1")]), ("1692632094485-0", [("rider", "Norem"), ("speed", "28.8"), ("position", "3"), ("location_id", "1")])])]
            }
    
            add_france_fixed(&mut r);
            if let Ok(res) = r.xgroup_create("race:france", "france_riders", "$") {
                let res: () = res;
                let _ = res;
                println!("OK"); // >>> OK
            }
    
            delete_keys(&mut r, &["race:italy"]);
            if let Ok(res) = r.xgroup_create_mkstream("race:italy", "italy_riders", "$") {
                let res: () = res;
                let _ = res;
                println!("OK"); // >>> OK
            }
    
            delete_keys(&mut r, &["race:italy"]);
            let _: () = r
                .xgroup_create_mkstream("race:italy", "italy_riders", "$")
                .expect("create italy group");
            let italy_1: Option<String> = r
                .xadd("race:italy", "1692632639151-0", &[("rider", "Castilla")])
                .expect("italy1");
            let italy_1 = italy_1.expect("missing stream id");
            println!("{italy_1}"); // >>> 1692632639151-0
            let italy_2: Option<String> = r
                .xadd("race:italy", "1692632647899-0", &[("rider", "Royce")])
                .expect("italy2");
            let italy_2 = italy_2.expect("missing stream id");
            println!("{italy_2}"); // >>> 1692632647899-0
            let italy_3: Option<String> = r
                .xadd("race:italy", "1692632662819-0", &[("rider", "Sam-Bodden")])
                .expect("italy3");
            let italy_3 = italy_3.expect("missing stream id");
            println!("{italy_3}"); // >>> 1692632662819-0
            let italy_4: Option<String> = r
                .xadd("race:italy", "1692632670501-0", &[("rider", "Prickett")])
                .expect("italy4");
            let italy_4 = italy_4.expect("missing stream id");
            println!("{italy_4}"); // >>> 1692632670501-0
            let italy_5: Option<String> = r
                .xadd("race:italy", "1692632678249-0", &[("rider", "Norem")])
                .expect("italy5");
            let italy_5 = italy_5.expect("missing stream id");
            println!("{italy_5}"); // >>> 1692632678249-0
    
            let opts = StreamReadOptions::default().group("italy_riders", "Alice").count(1);
            if let Ok(res) = r.xread_options(&["race:italy"], &[">"], &opts) {
                let res: Option<StreamReadReply> = res;
                let view: Vec<_> = res
                    .expect("xgroup read should return data")
                    .keys
                    .iter()
                    .map(|stream| {
                        (
                            stream.key.clone(),
                            stream
                                .ids
                                .iter()
                                .map(|entry| {
                                    (
                                        entry.id.clone(),
                                        vec![(
                                            "rider".to_string(),
                                            entry.get::<String>("rider").expect("missing rider"),
                                        )],
                                    )
                                })
                                .collect::<Vec<_>>(),
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("race:italy", [("1692632639151-0", [("rider", "Castilla")])])]
            }
    
            seed_italy_alice_pending(&mut r);
            let opts = StreamReadOptions::default().group("italy_riders", "Alice");
            if let Ok(res) = r.xread_options(&["race:italy"], &["0"], &opts) {
                let res: Option<StreamReadReply> = res;
                let view: Vec<_> = res
                    .expect("xgroup history")
                    .keys
                    .iter()
                    .map(|stream| {
                        (
                            stream.key.clone(),
                            stream
                                .ids
                                .iter()
                                .map(|entry| {
                                    (
                                        entry.id.clone(),
                                        vec![(
                                            "rider".to_string(),
                                            entry.get::<String>("rider").expect("missing rider"),
                                        )],
                                    )
                                })
                                .collect::<Vec<_>>(),
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("race:italy", [("1692632639151-0", [("rider", "Castilla")])])]
            }
    
            seed_italy_alice_pending(&mut r);
            if let Ok(res) = r.xack("race:italy", "italy_riders", &["1692632639151-0"]) {
                let res: usize = res;
                println!("{res}"); // >>> 1
            }
    
            let opts = StreamReadOptions::default().group("italy_riders", "Alice");
            if let Ok(res) = r.xread_options(&["race:italy"], &["0"], &opts) {
                let res: Option<StreamReadReply> = res;
                let view: Vec<_> = res
                    .expect("xgroup history")
                    .keys
                    .iter()
                    .map(|stream| {
                        (
                            stream.key.clone(),
                            stream
                                .ids
                                .iter()
                                .map(|entry| {
                                    (
                                        entry.id.clone(),
                                        vec![(
                                            "rider".to_string(),
                                            entry.get::<String>("rider").expect("missing rider"),
                                        )],
                                    )
                                })
                                .collect::<Vec<_>>(),
                        )
                    })
                    .collect();
                println!("{view:?}"); // >>> [("race:italy", [])]
            }
    
            seed_italy_after_ack(&mut r);
            let opts = StreamReadOptions::default().group("italy_riders", "Bob").count(2);
            if let Ok(res) = r.xread_options(&["race:italy"], &[">"], &opts) {
                let res: Option<StreamReadReply> = res;
                let view: Vec<_> = res
                    .expect("bob should receive data")
                    .keys
                    .iter()
                    .map(|stream| {
                        (
                            stream.key.clone(),
                            stream
                                .ids
                                .iter()
                                .map(|entry| {
                                    (
                                        entry.id.clone(),
                                        vec![(
                                            "rider".to_string(),
                                            entry.get::<String>("rider").expect("missing rider"),
                                        )],
                                    )
                                })
                                .collect::<Vec<_>>(),
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("race:italy", [("1692632647899-0", [("rider", "Royce")]), ("1692632662819-0", [("rider", "Sam-Bodden")])])]
            }
    
            seed_italy_bob_pending(&mut r);
            if let Ok(res) = r.xpending("race:italy", "italy_riders") {
                let res: StreamPendingReply = res;
                let view = match res {
                    StreamPendingReply::Empty => None,
                    StreamPendingReply::Data(data) => Some((
                        data.count,
                        data.start_id.clone(),
                        data.end_id.clone(),
                        data.consumers
                            .iter()
                            .map(|consumer| (consumer.name.clone(), consumer.pending))
                            .collect::<Vec<_>>(),
                    )),
                }
                .expect("pending summary");
                println!("{view:?}");
                // >>> (2, "1692632647899-0", "1692632662819-0", [("Bob", 2)])
            }
    
            seed_italy_bob_pending(&mut r);
            sleep(Duration::from_millis(5));
            if let Ok(res) = r.xpending_count("race:italy", "italy_riders", "-", "+", 10) {
                let res: StreamPendingCountReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            entry.consumer.clone(),
                            entry.last_delivered_ms,
                            entry.times_delivered,
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("1692632647899-0", "Bob", 5, 1), ("1692632662819-0", "Bob", 5, 1)]
            }
    
            seed_italy_bob_pending(&mut r);
            if let Ok(res) = r.xrange("race:italy", "1692632647899-0", "1692632647899-0") {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![(
                                "rider".to_string(),
                                entry.get::<String>("rider").expect("missing rider"),
                            )],
                        )
                    })
                    .collect();
                println!("{view:?}"); // >>> [("1692632647899-0", [("rider", "Royce")])]
            }
    
            seed_italy_bob_pending(&mut r);
            sleep(Duration::from_millis(5));
            if let Ok(res) = r.xclaim("race:italy", "italy_riders", "Alice", 1, &["1692632647899-0"]) {
                let res: redis::streams::StreamClaimReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![(
                                "rider".to_string(),
                                entry.get::<String>("rider").expect("missing rider"),
                            )],
                        )
                    })
                    .collect();
                println!("{view:?}"); // >>> [("1692632647899-0", [("rider", "Royce")])]
            }
    
            seed_italy_bob_pending(&mut r);
            sleep(Duration::from_millis(5));
            let opts = StreamAutoClaimOptions::default().count(1);
            if let Ok(res) = r.xautoclaim_options("race:italy", "italy_riders", "Alice", 1, "0-0", opts) {
                let res: redis::streams::StreamAutoClaimReply = res;
                let claimed: Vec<_> = res
                    .claimed
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![(
                                "rider".to_string(),
                                entry.get::<String>("rider").expect("missing rider"),
                            )],
                        )
                    })
                    .collect();
                println!("{:?}", (res.next_stream_id.clone(), &claimed));
                // >>> ("1692632662819-0", [("1692632647899-0", [("rider", "Royce")])])
            }
    
            seed_italy_bob_pending(&mut r);
            sleep(Duration::from_millis(5));
            let first_opts = StreamAutoClaimOptions::default().count(1);
            let _: redis::streams::StreamAutoClaimReply = r
                .xautoclaim_options("race:italy", "italy_riders", "Alice", 1, "0-0", first_opts)
                .expect("first autoclaim");
            let next_opts = StreamAutoClaimOptions::default().count(1);
            if let Ok(res) = r.xautoclaim_options(
                "race:italy",
                "italy_riders",
                "Lora",
                1,
                "(1692632647899-0",
                next_opts,
            ) {
                let res: redis::streams::StreamAutoClaimReply = res;
                let claimed: Vec<_> = res
                    .claimed
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![(
                                "rider".to_string(),
                                entry.get::<String>("rider").expect("missing rider"),
                            )],
                        )
                    })
                    .collect();
                println!("{:?}", (res.next_stream_id.clone(), &claimed));
                // >>> ("0-0", [("1692632662819-0", [("rider", "Sam-Bodden")])])
            }
    
            seed_italy_info_state(&mut r);
            if let Ok(res) = r.xinfo_stream("race:italy") {
                let res: StreamInfoStreamReply = res;
                let view = (
                    res.length,
                    res.radix_tree_keys,
                    res.groups,
                    res.last_generated_id.clone(),
                    res.first_entry.id.clone(),
                    res.last_entry.id.clone(),
                );
                println!("{view:?}");
                // >>> (5, 1, 1, "1692632678249-0", "1692632639151-0", "1692632678249-0")
            }
    
            seed_italy_info_state(&mut r);
            if let Ok(res) = r.xinfo_groups("race:italy") {
                let res: StreamInfoGroupsReply = res;
                let view: Vec<_> = res
                    .groups
                    .iter()
                    .map(|group| {
                        (
                            group.name.clone(),
                            group.consumers,
                            group.pending,
                            group.last_delivered_id.clone(),
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("italy_riders", 3, 2, "1692632662819-0")]
            }
    
            seed_italy_info_state(&mut r);
            if let Ok(res) = r.xinfo_consumers("race:italy", "italy_riders") {
                let res: StreamInfoConsumersReply = res;
                let mut view: Vec<_> = res
                    .consumers
                    .iter()
                    .map(|consumer| (consumer.name.clone(), consumer.pending, consumer.idle))
                    .collect();
                view.sort_by(|a, b| a.0.cmp(&b.0));
                println!("{view:?}");
                // >>> [("Alice", 1, 5), ("Bob", 0, 5), ("Lora", 1, 5)]
            }
    
            delete_keys(&mut r, &["race:italy"]);
            let max1: Option<String> = r
                .xadd_maxlen("race:italy", StreamMaxlen::Equals(2), "1-0", &[("rider", "Jones")])
                .expect("maxlen add 1");
            let max1 = max1.expect("missing stream id");
            println!("{max1}"); // >>> 1-0
            let max2: Option<String> = r
                .xadd_maxlen("race:italy", StreamMaxlen::Equals(2), "2-0", &[("rider", "Wood")])
                .expect("maxlen add 2");
            let max2 = max2.expect("missing stream id");
            println!("{max2}"); // >>> 2-0
            let max3: Option<String> = r
                .xadd_maxlen("race:italy", StreamMaxlen::Equals(2), "3-0", &[("rider", "Henshaw")])
                .expect("maxlen add 3");
            let max3 = max3.expect("missing stream id");
            println!("{max3}"); // >>> 3-0
    
            if let Ok(res) = r.xlen("race:italy") {
                let res: usize = res;
                println!("{res}"); // >>> 2
            }
    
            if let Ok(res) = r.xrange_all("race:italy") {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![(
                                "rider".to_string(),
                                entry.get::<String>("rider").expect("missing rider"),
                            )],
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("2-0", [("rider", "Wood")]), ("3-0", [("rider", "Henshaw")])]
            }
    
            delete_keys(&mut r, &["race:italy"]);
            let _: Option<String> = r.xadd("race:italy", "1-0", &[("rider", "Wood")]).expect("trim seed 1");
            let _: Option<String> = r.xadd("race:italy", "2-0", &[("rider", "Henshaw")]).expect("trim seed 2");
            if let Ok(res) = r.xtrim("race:italy", StreamMaxlen::Equals(10)) {
                let res: usize = res;
                println!("{res}"); // >>> 0
            }
    
            seed_trim_stream(&mut r);
            if let Ok(res) = r.xtrim_options(
                "mystream",
                &StreamTrimOptions::maxlen(StreamTrimmingMode::Approx, 10),
            ) {
                let res: usize = res;
                println!("{res}"); // >>> 0
            }
    
            delete_keys(&mut r, &["race:italy"]);
            let _: Option<String> = r.xadd("race:italy", "2-0", &[("rider", "Wood")]).expect("xdel seed 1");
            let _: Option<String> = r.xadd("race:italy", "3-0", &[("rider", "Henshaw")]).expect("xdel seed 2");
            if let Ok(res) = r.xrange_count("race:italy", "-", "+", 2) {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![(
                                "rider".to_string(),
                                entry.get::<String>("rider").expect("missing rider"),
                            )],
                        )
                    })
                    .collect();
                println!("{view:?}"); // >>> [("2-0", [("rider", "Wood")]), ("3-0", [("rider", "Henshaw")])]
            }
    
            if let Ok(res) = r.xdel("race:italy", &["3-0"]) {
                let res: usize = res;
                println!("{res}"); // >>> 1
            }
    
            if let Ok(res) = r.xrange_count("race:italy", "-", "+", 2) {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![(
                                "rider".to_string(),
                                entry.get::<String>("rider").expect("missing rider"),
                            )],
                        )
                    })
                    .collect();
                println!("{view:?}"); // >>> [("2-0", [("rider", "Wood")])]
            }
        }
    }
    
    mod tests {
        use redis::{
            streams::{
                StreamAutoClaimOptions, StreamInfoConsumersReply, StreamInfoGroupsReply,
                StreamInfoStreamReply, StreamMaxlen, StreamPendingCountReply, StreamPendingReply,
                StreamRangeReply, StreamReadOptions, StreamReadReply, StreamTrimmingMode, StreamTrimOptions,
            },
            AsyncCommands,
        };
        use tokio::time::{sleep, Duration};
    
        async fn delete_keys(r: &mut redis::aio::MultiplexedConnection, keys: &[&str]) {
            let _: usize = r.del(keys).await.unwrap_or(0);
        }
    
        async fn add_france_fixed(r: &mut redis::aio::MultiplexedConnection) {
            delete_keys(r, &["race:france"]).await;
            let _: Option<String> = r
                .xadd(
                    "race:france",
                    "1692632086370-0",
                    &[
                        ("rider", "Castilla"),
                        ("speed", "30.2"),
                        ("position", "1"),
                        ("location_id", "1"),
                    ],
                )
                .await
                .expect("add france 1");
            let _: Option<String> = r
                .xadd(
                    "race:france",
                    "1692632094485-0",
                    &[
                        ("rider", "Norem"),
                        ("speed", "28.8"),
                        ("position", "3"),
                        ("location_id", "1"),
                    ],
                )
                .await
                .expect("add france 2");
            let _: Option<String> = r
                .xadd(
                    "race:france",
                    "1692632102976-0",
                    &[
                        ("rider", "Prickett"),
                        ("speed", "29.7"),
                        ("position", "2"),
                        ("location_id", "1"),
                    ],
                )
                .await
                .expect("add france 3");
            let _: Option<String> = r
                .xadd(
                    "race:france",
                    "1692632147973-0",
                    &[
                        ("rider", "Castilla"),
                        ("speed", "29.9"),
                        ("position", "1"),
                        ("location_id", "2"),
                    ],
                )
                .await
                .expect("add france 4");
        }
    
        async fn seed_usa_fixed(r: &mut redis::aio::MultiplexedConnection) {
            delete_keys(r, &["race:usa"]).await;
            let _: Option<String> = r
                .xadd("race:usa", "0-1", &[("racer", "Castilla")])
                .await
                .expect("add usa 1");
            let _: Option<String> = r
                .xadd("race:usa", "0-2", &[("racer", "Norem")])
                .await
                .expect("add usa 2");
        }
    
        async fn seed_italy_group_base(r: &mut redis::aio::MultiplexedConnection) {
            delete_keys(r, &["race:italy"]).await;
            let _: () = r
                .xgroup_create_mkstream("race:italy", "italy_riders", "$")
                .await
                .expect("create italy group");
            let _: Option<String> = r
                .xadd("race:italy", "1692632639151-0", &[("rider", "Castilla")])
                .await
                .expect("add italy 1");
            let _: Option<String> = r
                .xadd("race:italy", "1692632647899-0", &[("rider", "Royce")])
                .await
                .expect("add italy 2");
            let _: Option<String> = r
                .xadd("race:italy", "1692632662819-0", &[("rider", "Sam-Bodden")])
                .await
                .expect("add italy 3");
            let _: Option<String> = r
                .xadd("race:italy", "1692632670501-0", &[("rider", "Prickett")])
                .await
                .expect("add italy 4");
            let _: Option<String> = r
                .xadd("race:italy", "1692632678249-0", &[("rider", "Norem")])
                .await
                .expect("add italy 5");
        }
    
        async fn seed_italy_alice_pending(r: &mut redis::aio::MultiplexedConnection) {
            seed_italy_group_base(r).await;
            let opts = StreamReadOptions::default().group("italy_riders", "Alice").count(1);
            let _: Option<StreamReadReply> = r
                .xread_options(&["race:italy"], &[">"], &opts)
                .await
                .expect("alice read pending");
        }
    
        async fn seed_italy_after_ack(r: &mut redis::aio::MultiplexedConnection) {
            seed_italy_alice_pending(r).await;
            let _: usize = r
                .xack("race:italy", "italy_riders", &["1692632639151-0"])
                .await
                .expect("ack first italy message");
        }
    
        async fn seed_italy_bob_pending(r: &mut redis::aio::MultiplexedConnection) {
            seed_italy_after_ack(r).await;
            let opts = StreamReadOptions::default().group("italy_riders", "Bob").count(2);
            let _: Option<StreamReadReply> = r
                .xread_options(&["race:italy"], &[">"], &opts)
                .await
                .expect("bob read pending");
        }
    
        async fn seed_italy_info_state(r: &mut redis::aio::MultiplexedConnection) {
            seed_italy_bob_pending(r).await;
            sleep(Duration::from_millis(5)).await;
            let _: redis::streams::StreamClaimReply = r
                .xclaim("race:italy", "italy_riders", "Alice", 1, &["1692632647899-0"])
                .await
                .expect("alice claim");
            sleep(Duration::from_millis(5)).await;
            let _: redis::streams::StreamClaimReply = r
                .xclaim("race:italy", "italy_riders", "Lora", 1, &["1692632662819-0"])
                .await
                .expect("lora claim");
        }
    
        async fn seed_trim_stream(r: &mut redis::aio::MultiplexedConnection) {
            delete_keys(r, &["mystream"]).await;
            for id in ["1-0", "2-0", "3-0", "4-0", "5-0", "6-0", "7-0", "8-0", "9-0", "10-0"] {
                let _: Option<String> = r
                    .xadd("mystream", id, &[("field", "value")])
                    .await
                    .expect("seed mystream");
            }
        }
    
        async fn run() {
            let mut r = match redis::Client::open("redis://127.0.0.1") {
                Ok(client) => match client.get_multiplexed_async_connection().await {
                    Ok(conn) => conn,
                    Err(e) => {
                        println!("Failed to connect to Redis: {e}");
                        return;
                    }
                },
                Err(e) => {
                    println!("Failed to create Redis client: {e}");
                    return;
                }
            };
    
            let res1: Option<String> = r
                .xadd(
                    "race:france",
                    "*",
                    &[
                        ("rider", "Castilla"),
                        ("speed", "30.2"),
                        ("position", "1"),
                        ("location_id", "1"),
                    ],
                )
                .await
                .expect("xadd 1");
            let res1 = res1.expect("missing stream id");
            println!("{res1}"); // >>> 1692632086370-0
    
            let res2: Option<String> = r
                .xadd(
                    "race:france",
                    "*",
                    &[
                        ("rider", "Norem"),
                        ("speed", "28.8"),
                        ("position", "3"),
                        ("location_id", "1"),
                    ],
                )
                .await
                .expect("xadd 2");
            let res2 = res2.expect("missing stream id");
            println!("{res2}"); // >>> 1692632094485-0
    
            let res3: Option<String> = r
                .xadd(
                    "race:france",
                    "*",
                    &[
                        ("rider", "Prickett"),
                        ("speed", "29.7"),
                        ("position", "2"),
                        ("location_id", "1"),
                    ],
                )
                .await
                .expect("xadd 3");
            let res3 = res3.expect("missing stream id");
            println!("{res3}"); // >>> 1692632102976-0
    
            add_france_fixed(&mut r).await;
            if let Ok(res) = r.xrange_count("race:france", "1692632086370-0", "+", 2).await {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![
                                ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                (
                                    "location_id".to_string(),
                                    entry.get::<String>("location_id").expect("missing location_id"),
                                ),
                            ],
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("1692632086370-0", [("rider", "Castilla"), ("speed", "30.2"), ("position", "1"), ("location_id", "1")]), ("1692632094485-0", [("rider", "Norem"), ("speed", "28.8"), ("position", "3"), ("location_id", "1")])]
            }
    
            add_france_fixed(&mut r).await;
            let opts = StreamReadOptions::default().count(100).block(300);
            if let Ok(res) = r.xread_options(&["race:france"], &["$"], &opts).await {
                let res: Option<StreamReadReply> = res;
                println!("{res:?}"); // >>> None
            }
    
            if let Ok(res) = r
                .xadd(
                    "race:france",
                    "*",
                    &[
                        ("rider", "Castilla"),
                        ("speed", "29.9"),
                        ("position", "1"),
                        ("location_id", "2"),
                    ],
                )
                .await
            {
                let res: Option<String> = res;
                let res = res.expect("missing stream id");
                println!("{res}"); // >>> 1692632147973-0
            }
    
            add_france_fixed(&mut r).await;
            if let Ok(res) = r.xlen("race:france").await {
                let res: usize = res;
                println!("{res}"); // >>> 4
            }
    
            delete_keys(&mut r, &["race:usa"]).await;
            if let Ok(res) = r.xadd("race:usa", "0-1", &[("racer", "Castilla")]).await {
                let res: Option<String> = res;
                let res = res.expect("missing stream id");
                println!("{res}"); // >>> 0-1
            }
            if let Ok(res) = r.xadd("race:usa", "0-2", &[("racer", "Norem")]).await {
                let res: Option<String> = res;
                let res = res.expect("missing stream id");
                println!("{res}"); // >>> 0-2
            }
    
            let res: redis::RedisResult<Option<String>> =
                r.xadd("race:usa", "0-1", &[("racer", "Prickett")]).await;
            match res {
                Ok(_) => {}
                Err(e) => {
                    let msg = e.to_string();
                    println!("{msg}");
                    // >>> An error was signalled by the server - ResponseError: The ID specified in XADD is equal or smaller than the target stream top item
                }
            }
    
            seed_usa_fixed(&mut r).await;
            if let Ok(res) = r.xadd("race:usa", "0-*", &[("racer", "Prickett")]).await {
                let res: Option<String> = res;
                let res = res.expect("missing stream id");
                println!("{res}"); // >>> 0-3
            }
    
            add_france_fixed(&mut r).await;
            if let Ok(res) = r.xrange_all("race:france").await {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![
                                ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                (
                                    "location_id".to_string(),
                                    entry.get::<String>("location_id").expect("missing location_id"),
                                ),
                            ],
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("1692632086370-0", [("rider", "Castilla"), ("speed", "30.2"), ("position", "1"), ("location_id", "1")]), ("1692632094485-0", [("rider", "Norem"), ("speed", "28.8"), ("position", "3"), ("location_id", "1")]), ("1692632102976-0", [("rider", "Prickett"), ("speed", "29.7"), ("position", "2"), ("location_id", "1")]), ("1692632147973-0", [("rider", "Castilla"), ("speed", "29.9"), ("position", "1"), ("location_id", "2")])]
            }
    
            add_france_fixed(&mut r).await;
            if let Ok(res) = r.xrange("race:france", "1692632086369", "1692632086371").await {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![
                                ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                (
                                    "location_id".to_string(),
                                    entry.get::<String>("location_id").expect("missing location_id"),
                                ),
                            ],
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("1692632086370-0", [("rider", "Castilla"), ("speed", "30.2"), ("position", "1"), ("location_id", "1")])]
            }
    
            add_france_fixed(&mut r).await;
            if let Ok(res) = r.xrange_count("race:france", "-", "+", 2).await {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![
                                ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                (
                                    "location_id".to_string(),
                                    entry.get::<String>("location_id").expect("missing location_id"),
                                ),
                            ],
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("1692632086370-0", [("rider", "Castilla"), ("speed", "30.2"), ("position", "1"), ("location_id", "1")]), ("1692632094485-0", [("rider", "Norem"), ("speed", "28.8"), ("position", "3"), ("location_id", "1")])]
            }
    
            add_france_fixed(&mut r).await;
            if let Ok(res) = r.xrange_count("race:france", "(1692632094485-0", "+", 2).await {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![
                                ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                (
                                    "location_id".to_string(),
                                    entry.get::<String>("location_id").expect("missing location_id"),
                                ),
                            ],
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("1692632102976-0", [("rider", "Prickett"), ("speed", "29.7"), ("position", "2"), ("location_id", "1")]), ("1692632147973-0", [("rider", "Castilla"), ("speed", "29.9"), ("position", "1"), ("location_id", "2")])]
            }
    
            add_france_fixed(&mut r).await;
            if let Ok(res) = r.xrange_count("race:france", "(1692632147973-0", "+", 2).await {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![
                                ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                (
                                    "location_id".to_string(),
                                    entry.get::<String>("location_id").expect("missing location_id"),
                                ),
                            ],
                        )
                    })
                    .collect();
                println!("{view:?}"); // >>> []
            }
    
            add_france_fixed(&mut r).await;
            if let Ok(res) = r.xrevrange_count("race:france", "+", "-", 1).await {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![
                                ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                (
                                    "location_id".to_string(),
                                    entry.get::<String>("location_id").expect("missing location_id"),
                                ),
                            ],
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("1692632147973-0", [("rider", "Castilla"), ("speed", "29.9"), ("position", "1"), ("location_id", "2")])]
            }
    
            add_france_fixed(&mut r).await;
            let opts = StreamReadOptions::default().count(2);
            if let Ok(res) = r.xread_options(&["race:france"], &["0"], &opts).await {
                let res: Option<StreamReadReply> = res;
                let view: Vec<_> = res
                    .expect("xread should return data")
                    .keys
                    .iter()
                    .map(|stream| {
                        (
                            stream.key.clone(),
                            stream
                                .ids
                                .iter()
                                .map(|entry| {
                                    (
                                        entry.id.clone(),
                                        vec![
                                            ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                            ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                            ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                            (
                                                "location_id".to_string(),
                                                entry.get::<String>("location_id").expect("missing location_id"),
                                            ),
                                        ],
                                    )
                                })
                                .collect::<Vec<_>>(),
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("race:france", [("1692632086370-0", [("rider", "Castilla"), ("speed", "30.2"), ("position", "1"), ("location_id", "1")]), ("1692632094485-0", [("rider", "Norem"), ("speed", "28.8"), ("position", "3"), ("location_id", "1")])])]
            }
    
            add_france_fixed(&mut r).await;
            if let Ok(res) = r.xgroup_create("race:france", "france_riders", "$").await {
                let res: () = res;
                let _ = res;
                println!("OK"); // >>> OK
            }
    
            delete_keys(&mut r, &["race:italy"]).await;
            if let Ok(res) = r.xgroup_create_mkstream("race:italy", "italy_riders", "$").await {
                let res: () = res;
                let _ = res;
                println!("OK"); // >>> OK
            }
    
            delete_keys(&mut r, &["race:italy"]).await;
            let _: () = r
                .xgroup_create_mkstream("race:italy", "italy_riders", "$")
                .await
                .expect("create italy group");
            let italy_1: Option<String> = r
                .xadd("race:italy", "1692632639151-0", &[("rider", "Castilla")])
                .await
                .expect("italy1");
            let italy_1 = italy_1.expect("missing stream id");
            println!("{italy_1}"); // >>> 1692632639151-0
            let italy_2: Option<String> = r
                .xadd("race:italy", "1692632647899-0", &[("rider", "Royce")])
                .await
                .expect("italy2");
            let italy_2 = italy_2.expect("missing stream id");
            println!("{italy_2}"); // >>> 1692632647899-0
            let italy_3: Option<String> = r
                .xadd("race:italy", "1692632662819-0", &[("rider", "Sam-Bodden")])
                .await
                .expect("italy3");
            let italy_3 = italy_3.expect("missing stream id");
            println!("{italy_3}"); // >>> 1692632662819-0
            let italy_4: Option<String> = r
                .xadd("race:italy", "1692632670501-0", &[("rider", "Prickett")])
                .await
                .expect("italy4");
            let italy_4 = italy_4.expect("missing stream id");
            println!("{italy_4}"); // >>> 1692632670501-0
            let italy_5: Option<String> = r
                .xadd("race:italy", "1692632678249-0", &[("rider", "Norem")])
                .await
                .expect("italy5");
            let italy_5 = italy_5.expect("missing stream id");
            println!("{italy_5}"); // >>> 1692632678249-0
            let opts = StreamReadOptions::default().group("italy_riders", "Alice").count(1);
            if let Ok(res) = r.xread_options(&["race:italy"], &[">"], &opts).await {
                let res: Option<StreamReadReply> = res;
                let view: Vec<_> = res
                    .expect("xgroup read should return data")
                    .keys
                    .iter()
                    .map(|stream| {
                        (
                            stream.key.clone(),
                            stream
                                .ids
                                .iter()
                                .map(|entry| {
                                    (
                                        entry.id.clone(),
                                        vec![(
                                            "rider".to_string(),
                                            entry.get::<String>("rider").expect("missing rider"),
                                        )],
                                    )
                                })
                                .collect::<Vec<_>>(),
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("race:italy", [("1692632639151-0", [("rider", "Castilla")])])]
            }
    
            seed_italy_alice_pending(&mut r).await;
            let opts = StreamReadOptions::default().group("italy_riders", "Alice");
            if let Ok(res) = r.xread_options(&["race:italy"], &["0"], &opts).await {
                let res: Option<StreamReadReply> = res;
                let view: Vec<_> = res
                    .expect("xgroup history")
                    .keys
                    .iter()
                    .map(|stream| {
                        (
                            stream.key.clone(),
                            stream
                                .ids
                                .iter()
                                .map(|entry| {
                                    (
                                        entry.id.clone(),
                                        vec![(
                                            "rider".to_string(),
                                            entry.get::<String>("rider").expect("missing rider"),
                                        )],
                                    )
                                })
                                .collect::<Vec<_>>(),
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("race:italy", [("1692632639151-0", [("rider", "Castilla")])])]
            }
    
            seed_italy_alice_pending(&mut r).await;
            if let Ok(res) = r.xack("race:italy", "italy_riders", &["1692632639151-0"]).await {
                let res: usize = res;
                println!("{res}"); // >>> 1
            }
            let opts = StreamReadOptions::default().group("italy_riders", "Alice");
            if let Ok(res) = r.xread_options(&["race:italy"], &["0"], &opts).await {
                let res: Option<StreamReadReply> = res;
                let view: Vec<_> = res
                    .expect("xgroup history")
                    .keys
                    .iter()
                    .map(|stream| {
                        (
                            stream.key.clone(),
                            stream
                                .ids
                                .iter()
                                .map(|entry| {
                                    (
                                        entry.id.clone(),
                                        vec![(
                                            "rider".to_string(),
                                            entry.get::<String>("rider").expect("missing rider"),
                                        )],
                                    )
                                })
                                .collect::<Vec<_>>(),
                        )
                    })
                    .collect();
                println!("{view:?}"); // >>> [("race:italy", [])]
            }
    
            seed_italy_after_ack(&mut r).await;
            let opts = StreamReadOptions::default().group("italy_riders", "Bob").count(2);
            if let Ok(res) = r.xread_options(&["race:italy"], &[">"], &opts).await {
                let res: Option<StreamReadReply> = res;
                let view: Vec<_> = res
                    .expect("bob should receive data")
                    .keys
                    .iter()
                    .map(|stream| {
                        (
                            stream.key.clone(),
                            stream
                                .ids
                                .iter()
                                .map(|entry| {
                                    (
                                        entry.id.clone(),
                                        vec![(
                                            "rider".to_string(),
                                            entry.get::<String>("rider").expect("missing rider"),
                                        )],
                                    )
                                })
                                .collect::<Vec<_>>(),
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("race:italy", [("1692632647899-0", [("rider", "Royce")]), ("1692632662819-0", [("rider", "Sam-Bodden")])])]
            }
    
            seed_italy_bob_pending(&mut r).await;
            if let Ok(res) = r.xpending("race:italy", "italy_riders").await {
                let res: StreamPendingReply = res;
                let view = match res {
                    StreamPendingReply::Empty => None,
                    StreamPendingReply::Data(data) => Some((
                        data.count,
                        data.start_id.clone(),
                        data.end_id.clone(),
                        data.consumers
                            .iter()
                            .map(|consumer| (consumer.name.clone(), consumer.pending))
                            .collect::<Vec<_>>(),
                    )),
                }
                .expect("pending summary");
                println!("{view:?}");
                // >>> (2, "1692632647899-0", "1692632662819-0", [("Bob", 2)])
            }
    
            seed_italy_bob_pending(&mut r).await;
            sleep(Duration::from_millis(5)).await;
            if let Ok(res) = r.xpending_count("race:italy", "italy_riders", "-", "+", 10).await {
                let res: StreamPendingCountReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            entry.consumer.clone(),
                            entry.last_delivered_ms,
                            entry.times_delivered,
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("1692632647899-0", "Bob", 5, 1), ("1692632662819-0", "Bob", 5, 1)]
            }
    
            seed_italy_bob_pending(&mut r).await;
            if let Ok(res) = r.xrange("race:italy", "1692632647899-0", "1692632647899-0").await {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![(
                                "rider".to_string(),
                                entry.get::<String>("rider").expect("missing rider"),
                            )],
                        )
                    })
                    .collect();
                println!("{view:?}"); // >>> [("1692632647899-0", [("rider", "Royce")])]
            }
    
            seed_italy_bob_pending(&mut r).await;
            sleep(Duration::from_millis(5)).await;
            if let Ok(res) = r
                .xclaim("race:italy", "italy_riders", "Alice", 1, &["1692632647899-0"])
                .await
            {
                let res: redis::streams::StreamClaimReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![(
                                "rider".to_string(),
                                entry.get::<String>("rider").expect("missing rider"),
                            )],
                        )
                    })
                    .collect();
                println!("{view:?}"); // >>> [("1692632647899-0", [("rider", "Royce")])]
            }
    
            seed_italy_bob_pending(&mut r).await;
            sleep(Duration::from_millis(5)).await;
            let opts = StreamAutoClaimOptions::default().count(1);
            if let Ok(res) = r
                .xautoclaim_options("race:italy", "italy_riders", "Alice", 1, "0-0", opts)
                .await
            {
                let res: redis::streams::StreamAutoClaimReply = res;
                let claimed: Vec<_> = res
                    .claimed
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![(
                                "rider".to_string(),
                                entry.get::<String>("rider").expect("missing rider"),
                            )],
                        )
                    })
                    .collect();
                println!("{:?}", (res.next_stream_id.clone(), &claimed));
                // >>> ("1692632662819-0", [("1692632647899-0", [("rider", "Royce")])])
            }
    
            seed_italy_bob_pending(&mut r).await;
            sleep(Duration::from_millis(5)).await;
            let first_opts = StreamAutoClaimOptions::default().count(1);
            let _: redis::streams::StreamAutoClaimReply = r
                .xautoclaim_options("race:italy", "italy_riders", "Alice", 1, "0-0", first_opts)
                .await
                .expect("first autoclaim");
            let next_opts = StreamAutoClaimOptions::default().count(1);
            if let Ok(res) = r
                .xautoclaim_options(
                    "race:italy",
                    "italy_riders",
                    "Lora",
                    1,
                    "(1692632647899-0",
                    next_opts,
                )
                .await
            {
                let res: redis::streams::StreamAutoClaimReply = res;
                let claimed: Vec<_> = res
                    .claimed
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![(
                                "rider".to_string(),
                                entry.get::<String>("rider").expect("missing rider"),
                            )],
                        )
                    })
                    .collect();
                println!("{:?}", (res.next_stream_id.clone(), &claimed));
                // >>> ("0-0", [("1692632662819-0", [("rider", "Sam-Bodden")])])
            }
    
            seed_italy_info_state(&mut r).await;
            if let Ok(res) = r.xinfo_stream("race:italy").await {
                let res: StreamInfoStreamReply = res;
                let view = (
                    res.length,
                    res.radix_tree_keys,
                    res.groups,
                    res.last_generated_id.clone(),
                    res.first_entry.id.clone(),
                    res.last_entry.id.clone(),
                );
                println!("{view:?}");
                // >>> (5, 1, 1, "1692632678249-0", "1692632639151-0", "1692632678249-0")
            }
    
            seed_italy_info_state(&mut r).await;
            if let Ok(res) = r.xinfo_groups("race:italy").await {
                let res: StreamInfoGroupsReply = res;
                let view: Vec<_> = res
                    .groups
                    .iter()
                    .map(|group| {
                        (
                            group.name.clone(),
                            group.consumers,
                            group.pending,
                            group.last_delivered_id.clone(),
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("italy_riders", 3, 2, "1692632662819-0")]
            }
    
            seed_italy_info_state(&mut r).await;
            if let Ok(res) = r.xinfo_consumers("race:italy", "italy_riders").await {
                let res: StreamInfoConsumersReply = res;
                let mut view: Vec<_> = res
                    .consumers
                    .iter()
                    .map(|consumer| (consumer.name.clone(), consumer.pending, consumer.idle))
                    .collect();
                view.sort_by(|a, b| a.0.cmp(&b.0));
                println!("{view:?}");
                // >>> [("Alice", 1, 5), ("Bob", 0, 5), ("Lora", 1, 5)]
            }
    
            delete_keys(&mut r, &["race:italy"]).await;
            let max1: Option<String> = r
                .xadd_maxlen("race:italy", StreamMaxlen::Equals(2), "1-0", &[("rider", "Jones")])
                .await
                .expect("maxlen add 1");
            let max1 = max1.expect("missing stream id");
            println!("{max1}"); // >>> 1-0
            let max2: Option<String> = r
                .xadd_maxlen("race:italy", StreamMaxlen::Equals(2), "2-0", &[("rider", "Wood")])
                .await
                .expect("maxlen add 2");
            let max2 = max2.expect("missing stream id");
            println!("{max2}"); // >>> 2-0
            let max3: Option<String> = r
                .xadd_maxlen("race:italy", StreamMaxlen::Equals(2), "3-0", &[("rider", "Henshaw")])
                .await
                .expect("maxlen add 3");
            let max3 = max3.expect("missing stream id");
            println!("{max3}"); // >>> 3-0
            if let Ok(res) = r.xlen("race:italy").await {
                let res: usize = res;
                println!("{res}"); // >>> 2
            }
            if let Ok(res) = r.xrange_all("race:italy").await {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![(
                                "rider".to_string(),
                                entry.get::<String>("rider").expect("missing rider"),
                            )],
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("2-0", [("rider", "Wood")]), ("3-0", [("rider", "Henshaw")])]
            }
    
            delete_keys(&mut r, &["race:italy"]).await;
            let _: Option<String> = r.xadd("race:italy", "1-0", &[("rider", "Wood")]).await.expect("trim seed 1");
            let _: Option<String> = r.xadd("race:italy", "2-0", &[("rider", "Henshaw")]).await.expect("trim seed 2");
            if let Ok(res) = r.xtrim("race:italy", StreamMaxlen::Equals(10)).await {
                let res: usize = res;
                println!("{res}"); // >>> 0
            }
    
            seed_trim_stream(&mut r).await;
            if let Ok(res) = r
                .xtrim_options(
                    "mystream",
                    &StreamTrimOptions::maxlen(StreamTrimmingMode::Approx, 10),
                )
                .await
            {
                let res: usize = res;
                println!("{res}"); // >>> 0
            }
    
            delete_keys(&mut r, &["race:italy"]).await;
            let _: Option<String> = r.xadd("race:italy", "2-0", &[("rider", "Wood")]).await.expect("xdel seed 1");
            let _: Option<String> = r.xadd("race:italy", "3-0", &[("rider", "Henshaw")]).await.expect("xdel seed 2");
            if let Ok(res) = r.xrange_count("race:italy", "-", "+", 2).await {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![(
                                "rider".to_string(),
                                entry.get::<String>("rider").expect("missing rider"),
                            )],
                        )
                    })
                    .collect();
                println!("{view:?}"); // >>> [("2-0", [("rider", "Wood")]), ("3-0", [("rider", "Henshaw")])]
            }
            if let Ok(res) = r.xdel("race:italy", &["3-0"]).await {
                let res: usize = res;
                println!("{res}"); // >>> 1
            }
            if let Ok(res) = r.xrange_count("race:italy", "-", "+", 2).await {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![(
                                "rider".to_string(),
                                entry.get::<String>("rider").expect("missing rider"),
                            )],
                        )
                    })
                    .collect();
                println!("{view:?}"); // >>> [("2-0", [("rider", "Wood")])]
            }
        }
    }
    

  • Read up to 100 new stream entries, starting at the end of the stream, and block for up to 300 ms if no entries are being written:

    Use XREAD with BLOCK to wait for new entries when you need to consume messages as they arrive
    > XREAD COUNT 100 BLOCK 300 STREAMS race:france $
    (nil)
    """
    Code samples for Stream doc pages:
        https://redis.io/docs/latest/develop/data-types/streams/
    """
    
    import redis
    
    r = redis.Redis(decode_responses=True)
    
    res1 = r.xadd(
        "race:france",
        {"rider": "Castilla", "speed": 30.2, "position": 1, "location_id": 1},
    )
    print(res1)  # >>> 1692629576966-0
    
    res2 = r.xadd(
        "race:france",
        {"rider": "Norem", "speed": 28.8, "position": 3, "location_id": 1},
    )
    print(res2)  # >>> 1692629594113-0
    
    res3 = r.xadd(
        "race:france",
        {"rider": "Prickett", "speed": 29.7, "position": 2, "location_id": 1},
    )
    print(res3)  # >>> 1692629613374-0
    
    
    res4 = r.xrange("race:france", "1691765278160-0", "+", 2)
    print(
        res4
    )  # >>> [
    #   ('1692629576966-0',
    #       {'rider': 'Castilla', 'speed': '30.2', 'position': '1', 'location_id': '1'}
    #   ),
    #   ('1692629594113-0',
    #       {'rider': 'Norem', 'speed': '28.8', 'position': '3', 'location_id': '1'}
    #   )
    # ]
    
    res5 = r.xread(streams={"race:france": 0}, count=100, block=300)
    print(
        res5
    )
    # >>> [
    #   ['race:france',
    #       [('1692629576966-0',
    #           {'rider': 'Castilla', 'speed': '30.2', 'position': '1', 'location_id': '1'}
    #       ),
    #       ('1692629594113-0',
    #           {'rider': 'Norem', 'speed': '28.8', 'position': '3', 'location_id': '1'}
    #       ),
    #       ('1692629613374-0',
    #           {'rider': 'Prickett', 'speed': '29.7', 'position': '2', 'location_id': '1'}
    #       )]
    # ]
    # ]
    
    res6 = r.xadd(
        "race:france",
        {"rider": "Castilla", "speed": 29.9, "position": 1, "location_id": 2},
    )
    print(res6)  # >>> 1692629676124-0
    
    res7 = r.xlen("race:france")
    print(res7)  # >>> 4
    
    
    res8 = r.xadd("race:usa", {"racer": "Castilla"}, id="0-1")
    print(res8)  # >>> 0-1
    
    res9 = r.xadd("race:usa", {"racer": "Norem"}, id="0-2")
    print(res9)  # >>> 0-2
    
    try:
        res10 = r.xadd("race:usa", {"racer": "Prickett"}, id="0-1")
        print(res10)  # >>> 0-1
    except redis.exceptions.ResponseError as e:
        print(e)  # >>> WRONGID
    
    # Not yet implemented
    
    res11 = r.xrange("race:france", "-", "+")
    print(
        res11
    )
    # >>> [
    #   ('1692629576966-0',
    #       {'rider': 'Castilla', 'speed': '30.2', 'position': '1', 'location_id': '1'}
    #   ),
    #   ('1692629594113-0',
    #       {'rider': 'Norem', 'speed': '28.8', 'position': '3', 'location_id': '1'}
    #   ),
    #   ('1692629613374-0',
    #       {'rider': 'Prickett', 'speed': '29.7', 'position': '2', 'location_id': '1'}
    #   ),
    #   ('1692629676124-0',
    #       {'rider': 'Castilla', 'speed': '29.9', 'position': '1', 'location_id': '2'}
    #   )
    # ]
    
    res12 = r.xrange("race:france", 1692629576965, 1692629576967)
    print(
        res12
    )
    # >>> [
    #       ('1692629576966-0',
    #           {'rider': 'Castilla', 'speed': '30.2', 'position': '1', 'location_id': '1'}
    #       )
    # ]
    
    res13 = r.xrange("race:france", "-", "+", 2)
    print(
        res13
    )
    # >>> [
    #   ('1692629576966-0',
    #       {'rider': 'Castilla', 'speed': '30.2', 'position': '1', 'location_id': '1'}
    #   ),
    #   ('1692629594113-0',
    #       {'rider': 'Norem', 'speed': '28.8', 'position': '3', 'location_id': '1'}
    #   )
    # ]
    
    res14 = r.xrange("race:france", "(1692629594113-0", "+", 2)
    print(
        res14
    )
    # >>> [
    #   ('1692629613374-0',
    #       {'rider': 'Prickett', 'speed': '29.7', 'position': '2', 'location_id': '1'}
    #   ),
    #   ('1692629676124-0',
    #       {'rider': 'Castilla', 'speed': '29.9', 'position': '1', 'location_id': '2'}
    #   )
    # ]
    
    res15 = r.xrange("race:france", "(1692629676124-0", "+", 2)
    print(res15)  # >>> []
    
    res16 = r.xrevrange("race:france", "+", "-", 1)
    print(
        res16
    )
    # >>> [
    #       ('1692629676124-0',
    #           {'rider': 'Castilla', 'speed': '29.9', 'position': '1', 'location_id': '2'}
    #       )
    # ]
    
    res17 = r.xread(streams={"race:france": 0}, count=2)
    print(
        res17
    )
    # >>> [
    #       ['race:france', [
    #       ('1692629576966-0',
    #           {'rider': 'Castilla', 'speed': '30.2', 'position': '1', 'location_id': '1'}
    #       ),
    #       ('1692629594113-0',
    #           {'rider': 'Norem', 'speed': '28.8', 'position': '3', 'location_id': '1'}
    #       )
    #       ]
    #       ]
    #   ]
    
    res18 = r.xgroup_create("race:france", "france_riders", "$")
    print(res18)  # >>> True
    
    res19 = r.xgroup_create("race:italy", "italy_riders", "$", mkstream=True)
    print(res19)  # >>> True
    
    r.xadd("race:italy", {"rider": "Castilla"})
    r.xadd("race:italy", {"rider": "Royce"})
    r.xadd("race:italy", {"rider": "Sam-Bodden"})
    r.xadd("race:italy", {"rider": "Prickett"})
    r.xadd("race:italy", {"rider": "Norem"})
    
    res20 = r.xreadgroup(
        streams={"race:italy": ">"},
        consumername="Alice",
        groupname="italy_riders",
        count=1,
    )
    print(res20)  # >>> [['race:italy', [('1692629925771-0', {'rider': 'Castilla'})]]]
    
    res21 = r.xreadgroup(
        streams={"race:italy": 0},
        consumername="Alice",
        groupname="italy_riders",
        count=1,
    )
    print(res21)  # >>> [['race:italy', [('1692629925771-0', {'rider': 'Castilla'})]]]
    
    res22 = r.xack("race:italy", "italy_riders", "1692629925771-0")
    print(res22)  # >>> 1
    
    res23 = r.xreadgroup(
        streams={"race:italy": 0},
        consumername="Alice",
        groupname="italy_riders",
        count=1,
    )
    print(res23)  # >>> [['race:italy', []]]
    
    res24 = r.xreadgroup(
        streams={"race:italy": ">"},
        consumername="Bob",
        groupname="italy_riders",
        count=2,
    )
    print(
        res24
    )
    # >>> [
    #       ['race:italy', [
    #           ('1692629925789-0',
    #               {'rider': 'Royce'}
    #           ),
    #           ('1692629925790-0',
    #               {'rider': 'Sam-Bodden'}
    #           )
    #       ]
    #       ]
    # ]
    
    res25 = r.xpending("race:italy", "italy_riders")
    print(
        res25
    )
    # >>> {
    #       'pending': 2, 'min': '1692629925789-0', 'max': '1692629925790-0',
    #       'consumers': [{'name': 'Bob', 'pending': 2}]
    # }
    
    res26 = r.xpending_range("race:italy", "italy_riders", "-", "+", 10)
    print(
        res26
    )
    # >>> [
    #       {
    #           'message_id': '1692629925789-0', 'consumer': 'Bob',
    #           'time_since_delivered': 31084, 'times_delivered': 1
    #       },
    #       {
    #           'message_id': '1692629925790-0', 'consumer': 'Bob',
    #           'time_since_delivered': 31084, 'times_delivered': 1
    #       }
    # ]
    
    res27 = r.xrange("race:italy", "1692629925789-0", "1692629925789-0")
    print(res27)  # >>> [('1692629925789-0', {'rider': 'Royce'})]
    
    res28 = r.xclaim("race:italy", "italy_riders", "Alice", 60000, ["1692629925789-0"])
    print(res28)  # >>> [('1692629925789-0', {'rider': 'Royce'})]
    
    res29 = r.xautoclaim("race:italy", "italy_riders", "Alice", 1, "0-0", 1)
    print(res29)  # >>> ['1692629925790-0', [('1692629925789-0', {'rider': 'Royce'})]]
    
    res30 = r.xautoclaim("race:italy", "italy_riders", "Alice", 1, "(1692629925789-0", 1)
    print(res30)  # >>> ['0-0', [('1692629925790-0', {'rider': 'Sam-Bodden'})]]
    
    res31 = r.xinfo_stream("race:italy")
    print(
        res31
    )
    # >>> {
    #       'length': 5, 'radix-tree-keys': 1, 'radix-tree-nodes': 2,
    #       'last-generated-id': '1692629926436-0', 'groups': 1,
    #       'first-entry': ('1692629925771-0', {'rider': 'Castilla'}),
    #       'last-entry': ('1692629926436-0', {'rider': 'Norem'})
    # }
    
    res32 = r.xinfo_groups("race:italy")
    print(
        res32
    )
    # >>> [
    #       {
    #           'name': 'italy_riders', 'consumers': 2, 'pending': 2,
    #           'last-delivered-id': '1692629925790-0'
    #       }
    # ]
    
    res33 = r.xinfo_consumers("race:italy", "italy_riders")
    print(
        res33
    )
    # >>> [
    #       {'name': 'Alice', 'pending': 2, 'idle': 199332},
    #       {'name': 'Bob', 'pending': 0, 'idle': 489170}
    # ]
    
    r.xadd("race:italy", {"rider": "Jones"}, maxlen=2)
    r.xadd("race:italy", {"rider": "Wood"}, maxlen=2)
    r.xadd("race:italy", {"rider": "Henshaw"}, maxlen=2)
    
    res34 = r.xlen("race:italy")
    print(res34)  # >>> 8
    
    res35 = r.xrange("race:italy", "-", "+")
    print(
        res35
    )
    # >>> [
    #       ('1692629925771-0', {'rider': 'Castilla'}),
    #       ('1692629925789-0', {'rider': 'Royce'}),
    #       ('1692629925790-0', {'rider': 'Sam-Bodden'}),
    #       ('1692629925791-0', {'rider': 'Prickett'}),
    #       ('1692629926436-0', {'rider': 'Norem'}),
    #       ('1692630612602-0', {'rider': 'Jones'}),
    #       ('1692630641947-0', {'rider': 'Wood'}),
    #       ('1692630648281-0', {'rider': 'Henshaw'})
    # ]
    
    r.xadd("race:italy", {"rider": "Smith"}, maxlen=2, approximate=False)
    
    res36 = r.xrange("race:italy", "-", "+")
    print(
        res36
    )
    # >>> [
    #       ('1692630648281-0', {'rider': 'Henshaw'}),
    #       ('1692631018238-0', {'rider': 'Smith'})
    # ]
    
    res37 = r.xtrim("race:italy", maxlen=10, approximate=False)
    print(res37)  # >>> 0
    
    res38 = r.xtrim("race:italy", maxlen=10)
    print(res38)  # >>> 0
    
    res39 = r.xrange("race:italy", "-", "+")
    print(
        res39
    )
    # >>> [
    #       ('1692630648281-0', {'rider': 'Henshaw'}),
    #       ('1692631018238-0', {'rider': 'Smith'})
    # ]
    
    res40 = r.xdel("race:italy", "1692631018238-0")
    print(res40)  # >>> 1
    
    res41 = r.xrange("race:italy", "-", "+")
    print(res41)  # >>> [('1692630648281-0', {'rider': 'Henshaw'})]
    
    import assert from 'assert';
    import {
      createClient
    } from 'redis';
    
    const client = await createClient();
    await client.connect();
    
    const res1 = await client.xAdd(
      'race:france', '*', {
        'rider': 'Castilla',
        'speed': '30.2',
        'position': '1',
        'location_id': '1'
      }
    );
    console.log(res1); // >>> 1700073067968-0 N.B. actual values will differ from these examples
    
    const res2 = await client.xAdd(
      'race:france', '*', {
        'rider': 'Norem',
        'speed': '28.8',
        'position': '3',
        'location_id': '1'
      },
    );
    console.log(res2); // >>> 1692629594113-0
    
    const res3 = await client.xAdd(
      'race:france', '*', {
        'rider': 'Prickett',
        'speed': '29.7',
        'position': '2',
        'location_id': '1'
      },
    );
    console.log(res3); // >>> 1692629613374-0
    
    
    const res4 = await client.xRange('race:france', '1691765278160-0', '+', {COUNT: 2});
    console.log(res4); // >>> [{ id: '1692629576966-0', message: { rider: 'Castilla', speed: '30.2', position: '1', location_id: '1' } }, { id: '1692629594113-0', message: { rider: 'Norem', speed: '28.8', position: '3', location_id: '1' } }]
    
    const res5 = await client.xRead({
      key: 'race:france',
      id: '0-0'
    }, {
      COUNT: 100,
      BLOCK: 300
    });
    console.log(res5); // >>> [{ name: 'race:france', messages: [{ id: '1692629576966-0', message: { rider: 'Castilla', speed: '30.2', position: '1', location_id: '1' } }, { id: '1692629594113-0', message: { rider: 'Norem', speed: '28.8', position: '3', location_id: '1' } }, { id: '1692629613374-0', message: { rider: 'Prickett', speed: '29.7', position: '2', location_id: '1' } }] }]
    
    const res6 = await client.xAdd(
      'race:france', '*', {
        'rider': 'Castilla',
        'speed': '29.9',
        'position': '1',
        'location_id': '2'
      }
    );
    console.log(res6); // >>> 1692629676124-0
    
    const res7 = await client.xLen('race:france');
    console.log(res7); // >>> 4
    
    
    const res8 = await client.xAdd('race:usa', '0-1', {
      'racer': 'Castilla'
    });
    console.log(res8); // >>> 0-1
    
    const res9 = await client.xAdd('race:usa', '0-2', {
      'racer': 'Norem'
    });
    console.log(res9); // >>> 0-2
    
    try {
      const res10 = await client.xAdd('race:usa', '0-1', {
        'racer': 'Prickett'
      });
      console.log(res10); // >>> 0-1
    } catch (error) {
      console.error(error); // >>> [SimpleError: ERR The ID specified in XADD is equal or smaller than the target stream top item]
    }
    
    const res11a = await client.xAdd('race:usa', '0-*', { racer: 'Norem' });
    console.log(res11a); // >>> 0-3
    
    const res11 = await client.xRange('race:france', '-', '+');
    console.log(res11); // >>> [{ id: '1692629576966-0', message: { rider: 'Castilla', speed: '30.2', position: '1', location_id: '1' } }, { id: '1692629594113-0', message: { rider: 'Norem', speed: '28.8', position: '3', location_id: '1' } }, { id: '1692629613374-0', message: { rider: 'Prickett', speed: '29.7', position: '2', location_id: '1' } }, { id: '1692629676124-0', message: { rider: 'Castilla', speed: '29.9', position: '1', location_id: '2' } }]
    
    const res12 = await client.xRange('race:france', '1692629576965', '1692629576967');
    console.log(res12); // >>> [{ id: '1692629576966-0', message: { rider: 'Castilla', speed: '30.2', position: '1', location_id: '1' } }]
    
    const res13 = await client.xRange('race:france', '-', '+', {COUNT: 2});
    console.log(res13); // >>> [{ id: '1692629576966-0', message: { rider: 'Castilla', speed: '30.2', position: '1', location_id: '1' } }, { id: '1692629594113-0', message: { rider: 'Norem', speed: '28.8', position: '3', location_id: '1' } }]
    
    const res14 = await client.xRange('race:france', '(1692629594113-0', '+', {COUNT: 2});
    console.log(res14); // >>> [{ id: '1692629613374-0', message: { rider: 'Prickett', speed: '29.7', position: '2', location_id: '1' } }, { id: '1692629676124-0', message: { rider: 'Castilla', speed: '29.9', position: '1', location_id: '2' } }]
    
    const res15 = await client.xRange('race:france', '(1692629676124-0', '+', {COUNT: 2});
    console.log(res15); // >>> []
    
    const res16 = await client.xRevRange('race:france', '+', '-', {COUNT: 1});
    console.log(
      res16
    ); // >>> [{ id: '1692629676124-0', message: { rider: 'Castilla', speed: '29.9', position: '1', location_id: '2' } }]
    
    const res17 = await client.xRead({
      key: 'race:france',
      id: '0-0'
    }, {
      COUNT: 2
    });
    console.log(res17); // >>> [{ name: 'race:france', messages: [{ id: '1692629576966-0', message: { rider: 'Castilla', speed: '30.2', position: '1', location_id: '1' } }, { id: '1692629594113-0', message: { rider: 'Norem', speed: '28.8', position: '3', location_id: '1' } }] }]
    
    const res18 = await client.xGroupCreate('race:france', 'france_riders', '$');
    console.log(res18); // >>> OK
    
    const res19 = await client.xGroupCreate('race:italy', 'italy_riders', '$', {
      MKSTREAM: true
    });
    console.log(res19); // >>> OK
    
    await client.xAdd('race:italy', '*', {
      'rider': 'Castilla'
    });
    await client.xAdd('race:italy', '*', {
      'rider': 'Royce'
    });
    await client.xAdd('race:italy', '*', {
      'rider': 'Sam-Bodden'
    });
    await client.xAdd('race:italy', '*', {
      'rider': 'Prickett'
    });
    await client.xAdd('race:italy', '*', {
      'rider': 'Norem'
    });
    
    const res20 = await client.xReadGroup(
      'italy_riders',
      'Alice', {
        key: 'race:italy',
        id: '>'
      }, {
        COUNT: 1
      }
    );
    console.log(res20); // >>> [{ name: 'race:italy', messages: [{ id: '1692629925771-0', message: { rider: 'Castilla' } }] }]
    
    const res21 = await client.xReadGroup(
      'italy_riders',
      'Alice', {
        key: 'race:italy',
        id: '0'
      }, {
        COUNT: 1
      }
    );
    console.log(res21); // >>> [{ name: 'race:italy', messages: [{ id: '1692629925771-0', message: { rider: 'Castilla' } }] }]
    
    const res22 = await client.xAck('race:italy', 'italy_riders', '1692629925771-0')
    console.log(res22); // >>> 1
    
    const res23 = await client.xReadGroup(
      'italy_riders',
      'Alice', {
        key: 'race:italy',
        id: '0'
      }, {
        COUNT: 1
      }
    );
    console.log(res23); // >>> [{ name: 'race:italy', messages: [] }]
    
    const res24 = await client.xReadGroup(
      'italy_riders',
      'Bob', {
        key: 'race:italy',
        id: '>'
      }, {
        COUNT: 2
      }
    );
    console.log(res24); // >>> [{ name: 'race:italy', messages: [{ id: '1692629925789-0', message: { rider: 'Royce' } }, { id: '1692629925790-0', message: { rider: 'Sam-Bodden' } }] }]
    
    const res25 = await client.xPending('race:italy', 'italy_riders');
    console.log(res25); // >>> {'pending': 2, 'firstId': '1692629925789-0', 'lastId': '1692629925790-0', 'consumers': [{'name': 'Bob', 'deliveriesCounter': 2}]}
    
    const res26 = await client.xPendingRange('race:italy', 'italy_riders', '-', '+', 10);
    console.log(res26); // >>> [{'id': '1692629925789-0', 'consumer': 'Bob', 'millisecondsSinceLastDelivery': 31084, 'deliveriesCounter:': 1}, {'id': '1692629925790-0', 'consumer': 'Bob', 'millisecondsSinceLastDelivery': 31084, 'deliveriesCounter': 1}]
    
    const res27 = await client.xRange('race:italy', '1692629925789-0', '1692629925789-0');
    console.log(res27); // >>> [{ id: '1692629925789-0', message: { rider: 'Royce' } }]
    
    const res28 = await client.xClaim(
      'race:italy', 'italy_riders', 'Alice', 60000, ['1692629925789-0']
    );
    console.log(res28); // >>> [{ id: '1692629925789-0', message: { rider: 'Royce' } }]
    
    const res29 = await client.xAutoClaim('race:italy', 'italy_riders', 'Alice', 1, '0-0', {
      COUNT: 1
    });
    console.log(res29); // >>> { nextId: '1692629925790-0', messages: [{ id: '1692629925789-0', message: { rider: 'Royce' } }], deletedMessages: [] }
    
    const res30 = await client.xAutoClaim(
      'race:italy', 'italy_riders', 'Alice', 1, '(1692629925789-0',
      {
        COUNT: 1
      }
    );
    console.log(res30); // >>> { nextId: '0-0', messages: [{ id: '1692629925790-0', message: { rider: 'Sam-Bodden' } }], deletedMessages: [] }
    
    const res31 = await client.xInfoStream('race:italy');
    console.log(res31); // >>> { length: 5, 'radix-tree-keys': 1, 'radix-tree-nodes': 2, 'last-generated-id': '1692629926436-0', 'max-deleted-entry-id': '0-0', 'entries-added': 5, 'recorded-first-entry-id': '1692629925771-0', groups: 1, 'first-entry': { id: '1692629925771-0', message: { rider: 'Castilla' } }, 'last-entry': { id: '1692629926436-0', message: { rider: 'Norem' } } }
    
    const res32 = await client.xInfoGroups('race:italy');
    console.log(res32); // >>> [{ name: 'italy_riders', consumers: 2, pending: 3, 'last-delivered-id': '1692629925790-0', 'entries-read': 3, lag: 2 }]
    
    const res33 = await client.xInfoConsumers('race:italy', 'italy_riders');
    console.log(res33); // >>> [{ name: 'Alice', pending: 3, idle: 170582, inactive: 170582 }, { name: 'Bob', pending: 0, idle: 489404, inactive: 489404 }]
    
    await client.xAdd('race:italy', '*', {
      'rider': 'Jones'
    }, {
      TRIM: {
        strategy: 'MAXLEN',
        strategyModifier: '~',
        threshold: 2
      }
    });
    await client.xAdd('race:italy', '*', {
      'rider': 'Wood'
    }, {
      TRIM: {
        strategy: 'MAXLEN',
        strategyModifier: '~',
        threshold: 2
      }
    });
    await client.xAdd('race:italy', '*', {
      'rider': 'Henshaw'
    }, {
      TRIM: {
        strategy: 'MAXLEN',
        strategyModifier: '~',
        threshold: 2
      }
    });
    
    const res34 = await client.xLen('race:italy');
    console.log(res34); // >>> 8
    
    const res35 = await client.xRange('race:italy', '-', '+');
    console.log(res35); // >>> [{ id: '1692629925771-0', message: { rider: 'Castilla' } }, { id: '1692629925789-0', message: { rider: 'Royce' } }, { id: '1692629925790-0', message: { rider: 'Sam-Bodden' } }, { id: '1692629925791-0', message: { rider: 'Prickett' } }, { id: '1692629926436-0', message: { rider: 'Norem' } }, { id: '1692630612602-0', message: { rider: 'Jones' } }, { id: '1692630641947-0', message: { rider: 'Wood' } }, { id: '1692630648281-0', message: { rider: 'Henshaw' } }]
    
    await client.xAdd('race:italy', '*', {
      'rider': 'Smith'
    }, {
      TRIM: {
        strategy: 'MAXLEN',
        strategyModifier: '=',
        threshold: 2
      }
    });
    
    const res36 = await client.xRange('race:italy', '-', '+');
    console.log(res36); // >>> [{ id: '1692630648281-0', message: { rider: 'Henshaw' } }, { id: '1692631018238-0', message: { rider: 'Smith' } }]
    
    const res37 = await client.xTrim('race:italy', 'MAXLEN', 10, {
      strategyModifier: '=',
    });
    console.log(res37); // >>> 0
    
    const res38 = await client.xTrim('race:italy', "MAXLEN", 10);
    console.log(res38); // >>> 0
    
    const res39 = await client.xRange('race:italy', '-', '+');
    console.log(res39); // >>> [{ id: '1692630648281-0', message: { rider: 'Henshaw' } }, { id: '1692631018238-0', message: { rider: 'Smith' } }]
    
    const res40 = await client.xDel('race:italy', '1692631018238-0');
    console.log(res40); // >>> 1
    
    const res41 = await client.xRange('race:italy', '-', '+');
    console.log(res41); // >>> [{ id: '1692630648281-0', message: { rider: 'Henshaw' } }]
    
    
    package io.redis.examples;
    
    import redis.clients.jedis.StreamEntryID;
    import redis.clients.jedis.RedisClient;
    
    
    public class StreamsExample {
    
      public void run() {
        RedisClient jedis = RedisClient.create("redis://localhost:6379");
    
    
        StreamEntryID res1 = jedis.xadd("race:france",new HashMap<String,String>(){{put("rider","Castilla");put("speed","30.2");put("position","1");put("location_id","1");}} , XAddParams.xAddParams());
    
        System.out.println(res1); // >>> 1701760582225-0
    
        StreamEntryID res2 = jedis.xadd("race:france",new HashMap<String,String>(){{put("rider","Norem");put("speed","28.8");put("position","3");put("location_id","1");}} , XAddParams.xAddParams());
    
        System.out.println(res2); // >>> 1701760582225-1
    
        StreamEntryID res3 = jedis.xadd("race:france",new HashMap<String,String>(){{put("rider","Prickett");put("speed","29.7");put("position","2");put("location_id","1");}} , XAddParams.xAddParams());
    
        System.out.println(res3); // >>> 1701760582226-0
    
    
        List<StreamEntry> res4 = jedis.xrange("race:france","1701760582225-0","+",2);
    
        System.out.println(res4); // >>> [1701760841292-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701760841292-1 {rider=Norem, speed=28.8, location_id=1, position=3}]
    
        List<Map.Entry<String, List<StreamEntry>>> res5= jedis.xread(XReadParams.xReadParams().block(300).count(100),new HashMap<String,StreamEntryID>(){{put("race:france",new StreamEntryID());}});
        System.out.println(
          res5
        ); // >>> [race:france=[1701761996660-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701761996661-0 {rider=Norem, speed=28.8, location_id=1, position=3}, 1701761996661-1 {rider=Prickett, speed=29.7, location_id=1, position=2}]]
    
        StreamEntryID res6 = jedis.xadd("race:france",new HashMap<String,String>(){{put("rider","Castilla");put("speed","29.9");put("position","2");put("location_id","1");}} , XAddParams.xAddParams());
        System.out.println(res6); // >>> 1701762285679-0
    
        long res7 = jedis.xlen("race:france");
        System.out.println(res7); // >>> 4
    
        StreamEntryID res8 = jedis.xadd("race:usa", new HashMap<String,String>(){{put("racer","Castilla");}},XAddParams.xAddParams().id("0-1"));
        System.out.println(res8); // >>> 0-1
    
        StreamEntryID res9 = jedis.xadd("race:usa", new HashMap<String,String>(){{put("racer","Norem");}},XAddParams.xAddParams().id("0-2"));
        System.out.println(res9); // >>> 0-2
    
        try {
          StreamEntryID res10 = jedis.xadd("race:usa", new HashMap<String,String>(){{put("racer","Prickett");}},XAddParams.xAddParams().id("0-1"));
          System.out.println(res10); // >>> 0-1
        }
        catch (JedisDataException e){
          System.out.println(e); // >>> ERR The ID specified in XADD is equal or smaller than the target stream top item
        }
    
        StreamEntryID res11 = jedis.xadd("race:usa", new HashMap<String,String>(){{put("racer","Norem");}},XAddParams.xAddParams().id("0-*"));
        System.out.println(res11);
    
        List<StreamEntry> res12 = jedis.xrange("race:france","-","+");
        System.out.println(
          res12
        ); // >>> [1701764734160-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701764734160-1 {rider=Norem, speed=28.8, location_id=1, position=3}, 1701764734161-0 {rider=Prickett, speed=29.7, location_id=1, position=2}, 1701764734162-0 {rider=Castilla, speed=29.9, location_id=1, position=2}]
    
        List<StreamEntry> res13 = jedis.xrange("race:france",String.valueOf(System.currentTimeMillis()-1000),String.valueOf(System.currentTimeMillis()+1000));
        System.out.println(
          res13
        ); // >>> [1701764734160-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701764734160-1 {rider=Norem, speed=28.8, location_id=1, position=3}, 1701764734161-0 {rider=Prickett, speed=29.7, location_id=1, position=2}, 1701764734162-0 {rider=Castilla, speed=29.9, location_id=1, position=2}]
    
        List<StreamEntry> res14 = jedis.xrange("race:france","-","+",2);
        System.out.println(res14); // >>> [1701764887638-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701764887638-1 {rider=Norem, speed=28.8, location_id=1, position=3}]
    
        List<StreamEntry> res15 = jedis.xrange("race:france",String.valueOf(System.currentTimeMillis()-1000)+"-0","+",2);
        System.out.println(res15); // >>> [1701764887638-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701764887638-1 {rider=Norem, speed=28.8, location_id=1, position=3}]
    
        List<StreamEntry> res16 = jedis.xrange("race:france",String.valueOf(System.currentTimeMillis()+1000)+"-0","+",2);
        System.out.println(res16); // >>> []
    
        List<StreamEntry> res17 = jedis.xrevrange("race:france","+","-",1);
        System.out.println(res17); // >>> [1701765218592-0 {rider=Castilla, speed=29.9, location_id=1, position=2}]
    
        List<Map.Entry<String, List<StreamEntry>>> res18= jedis.xread(XReadParams.xReadParams().count(2),new HashMap<String,StreamEntryID>(){{put("race:france",new StreamEntryID());}});
        System.out.println(
          res18
        ); // >>> [race:france=[1701765384638-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701765384638-1 {rider=Norem, speed=28.8, location_id=1, position=3}]]
    
        String res19 = jedis.xgroupCreate("race:france","france_riders",StreamEntryID.LAST_ENTRY,false);
        System.out.println(res19); // >>> OK
    
        String res20 = jedis.xgroupCreate("race:italy","italy_riders",StreamEntryID.LAST_ENTRY,true);
        System.out.println(res20); // >>> OK
    
        StreamEntryID id1 = jedis.xadd("race:italy", new HashMap<String,String>(){{put("rider","Castilaa");}},XAddParams.xAddParams());
        StreamEntryID id2 = jedis.xadd("race:italy", new HashMap<String,String>(){{put("rider","Royce");}},XAddParams.xAddParams());
        StreamEntryID id3 = jedis.xadd("race:italy", new HashMap<String,String>(){{put("rider","Sam-Bodden");}},XAddParams.xAddParams());
        StreamEntryID id4 = jedis.xadd("race:italy", new HashMap<String,String>(){{put("rider","Prickett");}},XAddParams.xAddParams());
        StreamEntryID id5 = jedis.xadd("race:italy", new HashMap<String,String>(){{put("rider","Norem");}},XAddParams.xAddParams());
    
        List<Map.Entry<String, List<StreamEntry>>> res21 = jedis.xreadGroup("italy_riders","Alice", XReadGroupParams.xReadGroupParams().count(1),new HashMap<String,StreamEntryID>(){{put("race:italy",StreamEntryID.UNRECEIVED_ENTRY);}});
        System.out.println(res21); // >>> [race:italy=[1701766299006-0 {rider=Castilaa}]]
    
        List<Map.Entry<String, List<StreamEntry>>> res22 = jedis.xreadGroup("italy_riders","Alice", XReadGroupParams.xReadGroupParams().count(1),new HashMap<String,StreamEntryID>(){{put("race:italy",new StreamEntryID());}});
        System.out.println(res22); // >>> [race:italy=[1701766299006-0 {rider=Castilaa}]]
    
        long res23 = jedis.xack("race:italy","italy_riders",id1);
        System.out.println(res23); // >>> 1
    
        List<Map.Entry<String, List<StreamEntry>>> res24 = jedis.xreadGroup("italy_riders","Alice", XReadGroupParams.xReadGroupParams().count(1),new HashMap<String,StreamEntryID>(){{put("race:italy",new StreamEntryID());}});
        System.out.println(res24); // >>> [race:italy=[]]
    
        List<Map.Entry<String, List<StreamEntry>>> res25 = jedis.xreadGroup("italy_riders","Bob", XReadGroupParams.xReadGroupParams().count(2),new HashMap<String,StreamEntryID>(){{put("race:italy",StreamEntryID.UNRECEIVED_ENTRY);}});
        System.out.println(res25); // >>> [race:italy=[1701767632261-1 {rider=Royce}, 1701767632262-0 {rider=Sam-Bodden}]]
    
        StreamPendingSummary res26 = jedis.xpending("race:italy","italy_riders");
        System.out.println(res26.getConsumerMessageCount()); // >>> {Bob=2}
    
        List<StreamPendingEntry> res27 = jedis.xpending("race:italy","italy_riders",XPendingParams.xPendingParams().start(StreamEntryID.MINIMUM_ID).end(StreamEntryID.MAXIMUM_ID).count(10));
        System.out.println(res27); // >>> [1701768567412-1 Bob idle:0 times:1, 1701768567412-2 Bob idle:0 times:1]
    
        List<StreamEntry> res28 = jedis.xrange("race:italy",id2.toString(),id2.toString());
        System.out.println(res28); // >>> [1701768744819-1 {rider=Royce}]
    
        List<StreamEntry> res29 = jedis.xclaim("race:italy","italy_riders","Alice", 0L, XClaimParams.xClaimParams().time(60000),id2);
        System.out.println(res29); // >>> [1701769004195-1 {rider=Royce}]
    
        Map.Entry<StreamEntryID, List<StreamEntry>> res30 = jedis.xautoclaim("race:italy","italy_riders","Alice",1L,new StreamEntryID("0-0"),XAutoClaimParams.xAutoClaimParams().count(1));
        System.out.println(res30); // >>> [1701769266831-2=[1701769266831-1 {rider=Royce}]
    
        Map.Entry<StreamEntryID, List<StreamEntry>> res31 = jedis.xautoclaim("race:italy","italy_riders","Alice",1L,new StreamEntryID(id2.toString()),XAutoClaimParams.xAutoClaimParams().count(1));
        System.out.println(res31); // >>> [0-0=[1701769605847-2 {rider=Sam-Bodden}]
    
        StreamInfo res32 = jedis.xinfoStream("race:italy");
        System.out.println(
          res32.getStreamInfo()
        ); // >>> {radix-tree-keys=1, radix-tree-nodes=2, entries-added=5, length=5, groups=1, max-deleted-entry-id=0-0, first-entry=1701769637612-0 {rider=Castilaa}, last-generated-id=1701769637612-4, last-entry=1701769637612-4 {rider=Norem}, recorded-first-entry-id=1701769637612-0}
    
        List<StreamGroupInfo> res33 = jedis.xinfoGroups("race:italy");
        for (StreamGroupInfo a : res33){
          System.out.println(
            a.getGroupInfo()
          ); // >>> {last-delivered-id=1701770253659-0, lag=2, pending=2, name=italy_riders, consumers=2, entries-read=3}
        }
    
        List<StreamConsumersInfo> res34 = jedis.xinfoConsumers("race:italy","italy_riders");
        for (StreamConsumerInfo a : res34){
          System.out.println(
            a.getConsumerInfo()
          ); // {inactive=1, idle=1, pending=1, name=Alice} , {inactive=3, idle=3, pending=1, name=Bob}
        }
    
        jedis.xadd("race:italy", new HashMap<String,String>(){{put("rider","Jones");}},XAddParams.xAddParams().maxLen(10));
        jedis.xadd("race:italy", new HashMap<String,String>(){{put("rider","Wood");}},XAddParams.xAddParams().maxLen(10));
        jedis.xadd("race:italy", new HashMap<String,String>(){{put("rider","Henshaw");}},XAddParams.xAddParams().maxLen(10));
        long res35 = jedis.xlen("race:italy");
        System.out.println(res35); // >>> 8
    
        List<StreamEntry> res36 = jedis.xrange("race:italy","-","+");
        System.out.println(res36); // >>> [1701771219852-0 {rider=Castilaa}, 1701771219852-1 {rider=Royce}, 1701771219853-0 {rider=Sam-Bodden}, 1701771219853-1 {rider=Prickett}, 1701771219853-2 {rider=Norem}, 1701771219858-0 {rider=Jones}, 1701771219858-1 {rider=Wood}, 1701771219859-0 {rider=Henshaw}]
    
        StreamEntryID id6 = jedis.xadd("race:italy", new HashMap<String,String>(){{put("rider","Smith");}},XAddParams.xAddParams().maxLen(2));
    
        List<StreamEntry> res37 = jedis.xrange("race:italy","-","+");
        System.out.println(res37); // >>> [1701771067332-1 {rider=Henshaw}, 1701771067332-2 {rider=Smith}]
    
        long res38 = jedis.xtrim("race:italy",XTrimParams.xTrimParams().maxLen(10).exactTrimming());
        System.out.println(res38); /// >>> 0
    
        long res39 = jedis.xtrim("race:italy",XTrimParams.xTrimParams().maxLen(10));
        System.out.println(res39); /// >>> 0
    
        List<StreamEntry> res40 = jedis.xrange("race:italy","-","+");
        System.out.println(res40); // >>> [1701771356428-2 {rider=Henshaw}, 1701771356429-0 {rider=Smith}]
    
        long res41 = jedis.xdel("race:italy",id6);
        System.out.println(res41); // >>> 1
    
        List<StreamEntry> res42 = jedis.xrange("race:italy","-","+");
        System.out.println(res42); // >>> [1701771517639-1 {rider=Henshaw}]
    
        jedis.close();
      }
    
    }
    
    package example_commands_test
    
    import (
    	"context"
    	"fmt"
    
    	"github.com/redis/go-redis/v9"
    )
    
    
    
    func ExampleClient_xadd() {
    	ctx := context.Background()
    
    	rdb := redis.NewClient(&redis.Options{
    		Addr:     "localhost:6379",
    		Password: "", // no password docs
    		DB:       0,  // use default DB
    	})
    
    
    	res1, err := rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:france",
    		Values: map[string]interface{}{
    			"rider":       "Castilla",
    			"speed":       30.2,
    			"position":    1,
    			"location_id": 1,
    		},
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	// fmt.Println(res1) // >>> 1692632086370-0
    
    	res2, err := rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:france",
    		Values: map[string]interface{}{
    			"rider":       "Norem",
    			"speed":       28.8,
    			"position":    3,
    			"location_id": 1,
    		},
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	// fmt.PrintLn(res2) // >>> 1692632094485-0
    
    	res3, err := rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:france",
    		Values: map[string]interface{}{
    			"rider":       "Prickett",
    			"speed":       29.7,
    			"position":    2,
    			"location_id": 1,
    		},
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	// fmt.Println(res3) // >>> 1692632102976-0
    
    
    	xlen, err := rdb.XLen(ctx, "race:france").Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(xlen) // >>> 3
    
    }
    
    func ExampleClient_racefrance1() {
    	ctx := context.Background()
    
    	rdb := redis.NewClient(&redis.Options{
    		Addr:     "localhost:6379",
    		Password: "", // no password docs
    		DB:       0,  // use default DB
    	})
    
    
    	_, err := rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:france",
    		Values: map[string]interface{}{
    			"rider":       "Castilla",
    			"speed":       30.2,
    			"position":    1,
    			"location_id": 1,
    		},
    		ID: "1692632086370-0",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:france",
    		Values: map[string]interface{}{
    			"rider":       "Norem",
    			"speed":       28.8,
    			"position":    3,
    			"location_id": 1,
    		},
    		ID: "1692632094485-0",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:france",
    		Values: map[string]interface{}{
    			"rider":       "Prickett",
    			"speed":       29.7,
    			"position":    2,
    			"location_id": 1,
    		},
    		ID: "1692632102976-0",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	res4, err := rdb.XRangeN(ctx, "race:france", "1691765278160-0", "+", 2).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res4)
    	// >>> [{1692632086370-0 map[location_id:1 position:1 rider:Castilla...
    
    	res5, err := rdb.XRead(ctx, &redis.XReadArgs{
    		Streams: []string{"race:france", "0"},
    		Count:   100,
    		Block:   300,
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res5)
    	// >>> // [{race:france [{1692632086370-0 map[location_id:1 position:1...
    
    	res6, err := rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:france",
    		Values: map[string]interface{}{
    			"rider":       "Castilla",
    			"speed":       29.9,
    			"position":    1,
    			"location_id": 2,
    		},
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	//fmt.Println(res6) // >>> 1692632147973-0
    
    	res7, err := rdb.XLen(ctx, "race:france").Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res7) // >>> 4
    
    
    }
    
    func ExampleClient_raceusa() {
    	ctx := context.Background()
    
    	rdb := redis.NewClient(&redis.Options{
    		Addr:     "localhost:6379",
    		Password: "", // no password docs
    		DB:       0,  // use default DB
    	})
    
    
    	res8, err := rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:usa",
    		Values: map[string]interface{}{
    			"racer": "Castilla",
    		},
    		ID: "0-1",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res8) // >>> 0-1
    
    	res9, err := rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:usa",
    		Values: map[string]interface{}{
    			"racer": "Norem",
    		},
    		ID: "0-2",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res9) // >>> 0-2
    
    	res10, err := rdb.XAdd(ctx, &redis.XAddArgs{
    		Values: map[string]interface{}{
    			"racer": "Prickett",
    		},
    		ID: "0-1",
    	}).Result()
    
    	if err != nil {
    		// fmt.Println(err)
    		// >>> ERR The ID specified in XADD is equal or smaller than the target stream top item
    	}
    
    	res11, err := rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:usa",
    		Values: map[string]interface{}{
    			"racer": "Prickett",
    		},
    		ID: "0-*",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res11) // >>> 0-3
    
    
    }
    
    func ExampleClient_racefrance2() {
    	ctx := context.Background()
    
    	rdb := redis.NewClient(&redis.Options{
    		Addr:     "localhost:6379",
    		Password: "", // no password docs
    		DB:       0,  // use default DB
    	})
    
    
    	_, err := rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:france",
    		Values: map[string]interface{}{
    			"rider":       "Castilla",
    			"speed":       30.2,
    			"position":    1,
    			"location_id": 1,
    		},
    		ID: "1692632086370-0",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:france",
    		Values: map[string]interface{}{
    			"rider":       "Norem",
    			"speed":       28.8,
    			"position":    3,
    			"location_id": 1,
    		},
    		ID: "1692632094485-0",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:france",
    		Values: map[string]interface{}{
    			"rider":       "Prickett",
    			"speed":       29.7,
    			"position":    2,
    			"location_id": 1,
    		},
    		ID: "1692632102976-0",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:france",
    		Values: map[string]interface{}{
    			"rider":       "Castilla",
    			"speed":       29.9,
    			"position":    1,
    			"location_id": 2,
    		},
    		ID: "1692632147973-0",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    	res12, err := rdb.XRange(ctx, "race:france", "-", "+").Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res12)
    	// >>> [{1692632086370-0 map[location_id:1 position:1 rider:Castilla...
    
    	res13, err := rdb.XRange(ctx, "race:france",
    		"1692632086369", "1692632086371",
    	).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res13)
    	// >>> [{1692632086370-0 map[location_id:1 position:1 rider:Castilla speed:30.2] 0 0}]
    
    	res14, err := rdb.XRangeN(ctx, "race:france", "-", "+", 2).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res14)
    	// >>> [{1692632086370-0 map[location_id:1 position:1 rider:Castilla speed:30.2] 0 0} {1692632094485-0 map[location_id:1 position:3 rider:Norem speed:28.8] 0 0}]
    
    	res15, err := rdb.XRangeN(ctx, "race:france",
    		"(1692632094485-0", "+", 2,
    	).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res15)
    	// >>> [{1692632102976-0 map[location_id:1 position:2 rider:Prickett speed:29.7] 0 0} {1692632147973-0 map[location_id:2 position:1 rider:Castilla speed:29.9] 0 0}]
    
    	res16, err := rdb.XRangeN(ctx, "race:france",
    		"(1692632147973-0", "+", 2,
    	).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res16)
    	// >>> []
    
    	res17, err := rdb.XRevRangeN(ctx, "race:france", "+", "-", 1).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res17)
    	// >>> [{1692632147973-0 map[location_id:2 position:1 rider:Castilla speed:29.9] 0 0}]
    
    	res18, err := rdb.XRead(ctx, &redis.XReadArgs{
    		Streams: []string{"race:france", "0"},
    		Count:   2,
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res18)
    	// >>> [{race:france [{1692632086370-0 map[location_id:1 position:1 rider:Castilla speed:30.2] 0 0} {1692632094485-0 map[location_id:1 position:3 rider:Norem speed:28.8] 0 0}]}]
    
    }
    
    func ExampleClient_xgroupcreate() {
    	ctx := context.Background()
    
    	rdb := redis.NewClient(&redis.Options{
    		Addr:     "localhost:6379",
    		Password: "", // no password docs
    		DB:       0,  // use default DB
    	})
    
    
    	_, err := rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:france",
    		Values: map[string]interface{}{
    			"rider":       "Castilla",
    			"speed":       30.2,
    			"position":    1,
    			"location_id": 1,
    		},
    		ID: "1692632086370-0",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	res19, err := rdb.XGroupCreate(ctx, "race:france", "france_riders", "$").Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res19) // >>> OK
    
    }
    
    func ExampleClient_xgroupcreatemkstream() {
    	ctx := context.Background()
    
    	rdb := redis.NewClient(&redis.Options{
    		Addr:     "localhost:6379",
    		Password: "", // no password docs
    		DB:       0,  // use default DB
    	})
    
    
    	res20, err := rdb.XGroupCreateMkStream(ctx,
    		"race:italy", "italy_riders", "$",
    	).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res20) // >>> OK
    
    }
    
    func ExampleClient_xgroupread() {
    	ctx := context.Background()
    
    	rdb := redis.NewClient(&redis.Options{
    		Addr:     "localhost:6379",
    		Password: "", // no password docs
    		DB:       0,  // use default DB
    	})
    
    
    	_, err := rdb.XGroupCreateMkStream(ctx,
    		"race:italy", "italy_riders", "$",
    	).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:italy",
    		Values: map[string]interface{}{"rider": "Castilla"},
    	}).Result()
    	// >>> 1692632639151-0
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:italy",
    		Values: map[string]interface{}{"rider": "Royce"},
    	}).Result()
    	// >>> 1692632647899-0
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:italy",
    		Values: map[string]interface{}{"rider": "Sam-Bodden"},
    	}).Result()
    	// >>> 1692632662819-0
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:italy",
    		Values: map[string]interface{}{"rider": "Prickett"},
    	}).Result()
    	// >>> 1692632670501-0
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:italy",
    		Values: map[string]interface{}{"rider": "Norem"},
    	}).Result()
    	// >>> 1692632678249-0
    
    	if err != nil {
    		panic(err)
    	}
    
    	// fmt.Println(res25)
    
    	res21, err := rdb.XReadGroup(ctx, &redis.XReadGroupArgs{
    		Streams:  []string{"race:italy", ">"},
    		Group:    "italy_riders",
    		Consumer: "Alice",
    		Count:    1,
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	// fmt.Println(res21)
    	// >>> [{race:italy [{1692632639151-0 map[rider:Castilla] 0 0}]}]
    
    
    	xlen, err := rdb.XLen(ctx, "race:italy").Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(xlen)
    
    }
    
    func ExampleClient_raceitaly() {
    	ctx := context.Background()
    
    	rdb := redis.NewClient(&redis.Options{
    		Addr:     "localhost:6379",
    		Password: "", // no password docs
    		DB:       0,  // use default DB
    	})
    
    
    	_, err := rdb.XGroupCreateMkStream(ctx,
    		"race:italy", "italy_riders", "$",
    	).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:italy",
    		Values: map[string]interface{}{"rider": "Castilla"},
    		ID:     "1692632639151-0",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:italy",
    		Values: map[string]interface{}{"rider": "Royce"},
    		ID:     "1692632647899-0",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:italy",
    		Values: map[string]interface{}{"rider": "Sam-Bodden"},
    		ID:     "1692632662819-0",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:italy",
    		Values: map[string]interface{}{"rider": "Prickett"},
    		ID:     "1692632670501-0",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:italy",
    		Values: map[string]interface{}{"rider": "Norem"},
    		ID:     "1692632678249-0",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XReadGroup(ctx, &redis.XReadGroupArgs{
    		Streams:  []string{"race:italy", ">"},
    		Group:    "italy_riders",
    		Consumer: "Alice",
    		Count:    1,
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    	res22, err := rdb.XReadGroup(ctx, &redis.XReadGroupArgs{
    		Streams:  []string{"race:italy", "0"},
    		Group:    "italy_riders",
    		Consumer: "Alice",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res22)
    	// >>> [{race:italy [{1692632639151-0 map[rider:Castilla] 0 0}]}]
    
    	res23, err := rdb.XAck(ctx,
    		"race:italy", "italy_riders", "1692632639151-0",
    	).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res23) // >>> 1
    
    	res24, err := rdb.XReadGroup(ctx, &redis.XReadGroupArgs{
    		Streams:  []string{"race:italy", "0"},
    		Group:    "italy_riders",
    		Consumer: "Alice",
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res24)
    	// >>> [{race:italy []}]
    
    	res25, err := rdb.XReadGroup(ctx, &redis.XReadGroupArgs{
    		Streams:  []string{"race:italy", ">"},
    		Group:    "italy_riders",
    		Consumer: "Bob",
    		Count:    2,
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res25)
    	// >>> [{race:italy [{1692632647899-0 map[rider:Royce] 0 0} {1692632662819-0 map[rider:Sam-Bodden] 0 0}]}]
    
    
    	res26, err := rdb.XPending(ctx, "race:italy", "italy_riders").Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res26)
    	// >>> &{2 1692632647899-0 1692632662819-0 map[Bob:2]}
    
    	res27, err := rdb.XPendingExt(ctx, &redis.XPendingExtArgs{
    		Stream: "race:italy",
    		Group:  "italy_riders",
    		Start:  "-",
    		End:    "+",
    		Count:  10,
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	// fmt.Println(res27)
    	// >>> [{1692632647899-0 Bob 0s 1} {1692632662819-0 Bob 0s 1}]
    
    	res28, err := rdb.XRange(ctx, "race:italy",
    		"1692632647899-0", "1692632647899-0",
    	).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res28) // >>> [{1692632647899-0 map[rider:Royce] 0 0}]
    
    	res29, err := rdb.XClaim(ctx, &redis.XClaimArgs{
    		Stream:   "race:italy",
    		Group:    "italy_riders",
    		Consumer: "Alice",
    		MinIdle:  0,
    		Messages: []string{"1692632647899-0"},
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res29)
    
    	res30, res30a, err := rdb.XAutoClaim(ctx, &redis.XAutoClaimArgs{
    		Stream:   "race:italy",
    		Group:    "italy_riders",
    		Consumer: "Alice",
    		Start:    "0-0",
    		Count:    1,
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res30)  // >>> [{1692632647899-0 map[rider:Royce] 0 0}]
    	fmt.Println(res30a) // >>> 1692632662819-0
    
    	res31, res31a, err := rdb.XAutoClaim(ctx, &redis.XAutoClaimArgs{
    		Stream:   "race:italy",
    		Group:    "italy_riders",
    		Consumer: "Lora",
    		Start:    "(1692632662819-0",
    		Count:    1,
    	}).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res31)  // >>> []
    	fmt.Println(res31a) // >>> 0-0
    
    	res32, err := rdb.XInfoStream(ctx, "race:italy").Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res32.Length)
    	// >>> 5
    	fmt.Println(res32.FirstEntry)
    	// >>> {1692632639151-0 map[rider:Castilla] 0 0}
    
    	res33, err := rdb.XInfoGroups(ctx, "race:italy").Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res33)
    	// >>> [{italy_riders 3 2 1692632662819-0 3 2}]
    
    	res34, err := rdb.XInfoConsumers(ctx, "race:italy", "italy_riders").Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	// fmt.Println(res34)
    	// >>> [{Alice 1 1ms 1ms} {Bob 1 2ms 2ms} {Lora 0 1ms -1ms}]
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:italy",
    		MaxLen: 2,
    		Values: map[string]interface{}{"rider": "Jones"},
    	},
    	).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:italy",
    		MaxLen: 2,
    		Values: map[string]interface{}{"rider": "Wood"},
    	},
    	).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:italy",
    		MaxLen: 2,
    		Values: map[string]interface{}{"rider": "Henshaw"},
    	},
    	).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	res35, err := rdb.XLen(ctx, "race:italy").Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res35) // >>> 2
    
    	res36, err := rdb.XRange(ctx, "race:italy", "-", "+").Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	// fmt.Println(res36)
    	// >>> [{1726649529170-1 map[rider:Wood] 0 0} {1726649529171-0 map[rider:Henshaw] 0 0}]
    
    	res37, err := rdb.XTrimMaxLen(ctx, "race:italy", 10).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res37) // >>> 0
    
    	res38, err := rdb.XTrimMaxLenApprox(ctx, "race:italy", 10, 20).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res38) // >>> 0
    
    
    }
    
    func ExampleClient_xdel() {
    	ctx := context.Background()
    
    	rdb := redis.NewClient(&redis.Options{
    		Addr:     "localhost:6379",
    		Password: "", // no password docs
    		DB:       0,  // use default DB
    	})
    
    
    	_, err := rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:italy",
    		MaxLen: 2,
    		Values: map[string]interface{}{"rider": "Wood"},
    		ID:     "1692633198206-0",
    	},
    	).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
    		Stream: "race:italy",
    		MaxLen: 2,
    		Values: map[string]interface{}{"rider": "Henshaw"},
    		ID:     "1692633208557-0",
    	},
    	).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	res39, err := rdb.XRangeN(ctx, "race:italy", "-", "+", 2).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res39)
    	// >>> [{1692633198206-0 map[rider:Wood] 0 0} {1692633208557-0 map[rider:Henshaw] 0 0}]
    
    	res40, err := rdb.XDel(ctx, "race:italy", "1692633208557-0").Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res40) // 1
    
    	res41, err := rdb.XRangeN(ctx, "race:italy", "-", "+", 2).Result()
    
    	if err != nil {
    		panic(err)
    	}
    
    	fmt.Println(res41)
    	// >>> [{1692633198206-0 map[rider:Wood] 0 0}]
    
    }
    
    
    using NRedisStack.Tests;
    using StackExchange.Redis;
    
    
    
    public class StreamTutorial
    {
        public void Run()
        {
            var muxer = ConnectionMultiplexer.Connect("localhost:6379");
            var db = muxer.GetDatabase();
    
            RedisValue res1 = db.StreamAdd(
                "race:france",
                [
                    new("rider", "Castilla"),
                    new("speed", 30.2),
                    new("position", 1),
                    new("location_id", 1)
                ]
            );
            Console.WriteLine(res1);    // >>> 1712668482289-0
    
            RedisValue res2 = db.StreamAdd(
                "race:france",
                [
                    new("rider", "Norem"),
                    new("speed", 28.8),
                    new("position", 3),
                    new("location_id", 1)
                ]
            );
            Console.WriteLine(res2);    // >>> 1712668766534-1
    
            RedisValue res3 = db.StreamAdd(
                "race:france",
                [
                    new("rider", "Prickett"),
                    new("speed", 29.7),
                    new("position", 2),
                    new("location_id", 1)
                ]
            );
            Console.WriteLine(res3);    // >>> 1712669055705-0
    
    
            // Tests for 'xadd' step.
    
    
            StreamEntry[] res4 = db.StreamRange("race:france", "1712668482289-0", "+", 2);
    
            foreach (StreamEntry entry in res4)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
    
            // >>> 1712668482289-0: [rider: Castilla, speed: 30.199999999999999, position: 1, location_id: 1]
            // >>> 1712668766534-1: [rider: Norem, speed: 28.800000000000001, position: 3, location_id: 1]
    
            // Tests for 'xrange' step.
    
    
            StreamEntry[] res5 = db.StreamRead("race:france", 0, 100);
    
            foreach (StreamEntry entry in res4)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
    
            // >>> 1712668482289-0: [rider: Castilla, speed: 30.199999999999999, position: 1, location_id: 1]
            // >>> 1712668766534-1: [rider: Norem, speed: 28.800000000000001, position: 3, location_id: 1]
            // >>> 1712669055705-0: [rider: Prickett, speed: 29.699999999999999, position: 2, location_id: 1]
    
            // Tests for 'xread_block' step.
    
    
            RedisValue res6 = db.StreamAdd(
                "race:france",
                [
                    new("rider", "Castilla"),
                    new("speed", 29.9),
                    new("position", 1),
                    new("location_id", 2)
                ]
            );
    
            Console.WriteLine(res6);    // >>> 1712675674750-0
    
            // Tests for 'xadd_2' step.
    
    
            long res7 = db.StreamLength("race:france");
            Console.WriteLine(res7);    // >>> 4
    
            // Tests for 'xlen' step.
    
    
            RedisValue res8 = db.StreamAdd(
                "race:usa",
                [
                    new("racer", "Castilla")
                ],
                "0-1"
            );
            Console.WriteLine(res8);    // >>> 0-1
    
            RedisValue res9 = db.StreamAdd(
                "race:usa",
                [
                    new("racer", "Norem")
                ],
                "0-2"
            );
            Console.WriteLine(res9);    // >>> 0-2
    
            // Tests for 'xadd_id' step.
    
    
            try
            {
                RedisValue res10 = db.StreamAdd(
                    "race:usa",
                    [
                        new("racer", "Prickett")
                    ],
                    "0-1"
                );
            }
            catch (RedisServerException ex)
            {
                Console.WriteLine(ex);  // >>> ERR The ID specified in XADD is equal or smaller than the target stream top item
            }
    
            // Tests for 'xadd_bad_id' step.
    
    
            RedisValue res11 = "";
            Version version = muxer.GetServer("localhost:6379").Version;
            if (version.Major >= 7)
            {
                res11 = db.StreamAdd(
                    "race:usa",
                    [
                        new("rider", "Norem")
                    ],
                    "0-*"
                );
    
                Console.WriteLine(res11);   // >>> "0-3"
            }
    
            // Tests for 'xadd_7' step.
    
    
            StreamEntry[] res12 = db.StreamRange("race:france", "-", "+");
    
            foreach (StreamEntry entry in res12)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
            // >>> 1712668482289-0: [rider: Castilla, speed: 30.199999999999999, position: 1, location_id: 1]
            // >>> 1712668766534-1: [rider: Norem, speed: 28.800000000000001, position: 3, location_id: 1]
            // >>> 1712669055705-0: [rider: Prickett, speed: 29.699999999999999, position: 2, location_id: 1]
            // >>> 1712675674750-0: [rider: Castilla, speed: 29.899999999999999, position: 1, location_id: 2]
    
            // Tests for 'xrange_all' step.
    
    
            StreamEntry[] res13 = db.StreamRange("race:france", 1712668482289, 1712668482291);
    
            foreach (StreamEntry entry in res13)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
            // >>> 1712668482289-0: [rider: Castilla, speed: 30.199999999999999, position: 1, location_id: 1]
    
            // Tests for 'xrange_time' step.
    
    
            StreamEntry[] res14 = db.StreamRange("race:france", "-", "+", 2);
    
            foreach (StreamEntry entry in res14)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
            // >>> 1712668482289-0: [rider: Castilla, speed: 30.199999999999999, position: 1, location_id: 1]
            // >>> 1712668766534-1: [rider: Norem, speed: 28.800000000000001, position: 3, location_id: 1]
    
            // Tests for 'xrange_step_1' step.
    
    
            StreamEntry[] res15 = db.StreamRange("race:france", "(1712668766534-1", "+", 2);
    
            foreach (StreamEntry entry in res15)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
            // >>> 1712669055705-0: [rider: Prickett, speed: 29.699999999999999, position: 2, location_id: 1]
            // >>> 1712675674750-0: [rider: Castilla, speed: 29.899999999999999, position: 1, location_id: 2]
    
            // Tests for 'xrange_step_2' step.
    
    
            StreamEntry[] res16 = db.StreamRange("race:france", "(1712675674750-0", "+", 2);
    
            foreach (StreamEntry entry in res16)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
            // >>> <empty array>
    
            // Tests for 'xrange_empty' step.
    
    
            StreamEntry[] res17 = db.StreamRange("race:france", "+", "-", 1, Order.Descending);
    
            foreach (StreamEntry entry in res17)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
            // >>> 1712675674750-0: [rider: Castilla, speed: 29.899999999999999, position: 1, location_id: 2]
    
            // Tests for 'xrevrange' step.
    
    
            StreamEntry[] res18 = db.StreamRead("race:france", 0, 2);
    
            foreach (StreamEntry entry in res18)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
            // >>> 1712668482289-0: [rider: Castilla, speed: 30.199999999999999, position: 1, location_id: 1]
            // >>> 1712668766534-1: [rider: Norem, speed: 28.800000000000001, position: 3, location_id: 1]
    
            // Tests for 'xread' step.
    
    
            bool res19 = db.StreamCreateConsumerGroup("race:france", "france_riders", "$");
            Console.WriteLine(res19);   // >>> true
    
            // Tests for 'xgroup_create' step.
    
    
            bool res20 = db.StreamCreateConsumerGroup("race:italy", "italy_riders", "$", true);
            Console.WriteLine(res20);   // >>> true
    
            // Tests for 'xgroup_create_mkstream' step.
    
    
            RedisValue groupRes = db.StreamAdd(
                "race:italy",
                [new("rider", "Castilla")]
            ); // 1712744323758-0
    
            groupRes = db.StreamAdd(
                "race:italy",
                [new("rider", "Royce")]
            ); // 1712744358384-0
    
            groupRes = db.StreamAdd(
                "race:italy",
                [new("rider", "Sam-Bodden")]
            ); // 1712744379676-0
    
            groupRes = db.StreamAdd(
                "race:italy",
                [new("rider", "Prickett")]
            ); // 1712744399401-0
    
            groupRes = db.StreamAdd(
                "race:italy",
                [new("rider", "Norem")]
            ); // 1712744413117-0
    
            StreamEntry[] res21 = db.StreamReadGroup("race:italy", "italy_riders", "Alice", ">", 1);
    
            foreach (StreamEntry entry in res21)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
            // >>> 1712744323758-0: [rider: Castilla]
    
            // Tests for 'xgroup_read' step.
    
    
            StreamEntry[] res22 = db.StreamReadGroup("race:italy", "italy_riders", "Alice", "0");
    
            foreach (StreamEntry entry in res22)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
                // >>> 1712744323758-0: [rider: Castilla]
            }
    
            // Tests for 'xgroup_read_id' step.
    
    
            long res23 = db.StreamAcknowledge("race:italy", "italy_riders", "1712744323758-0");
            Console.WriteLine(res23);   // >>> 1
    
            StreamEntry[] res24 = db.StreamReadGroup("race:italy", "italy_riders", "Alice", "0");
    
            foreach (StreamEntry entry in res24)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
            // >>> <empty array>
    
            // Tests for 'xack' step.
    
    
            StreamEntry[] res25 = db.StreamReadGroup("race:italy", "italy_riders", "Bob", ">", 2);
    
            foreach (StreamEntry entry in res25)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
            // >>> 1712744358384-0: [rider: Royce]
            // >>> 1712744379676-0: [rider: Sam-Bodden]
    
            // Tests for 'xgroup_read_bob' step.
    
    
            StreamPendingInfo res26 = db.StreamPending("race:italy", "italy_riders");
            Console.WriteLine($"pending: {res26.PendingMessageCount}, min: {res26.LowestPendingMessageId}, max: {res26.HighestPendingMessageId}, consumers:[{string.Join(", ", res26.Consumers.Select(c => $"{c.Name}: {c.PendingMessageCount}"))}]");
            // >>> pending: 2, min: 1712747506906-0, max: 1712747506907-0, consumers:[name: Bob, pending:2]
    
            // Tests for 'xpending' step.
    
    
            StreamPendingMessageInfo[] res27 = db.StreamPendingMessages(
                "race:italy", "italy_riders", 10, "", "-", "+"
            );
    
            foreach (StreamPendingMessageInfo info in res27)
            {
                Console.WriteLine($"message_id: {info.MessageId}, consumer: {info.ConsumerName}, time_since_delivered: {info.IdleTimeInMilliseconds}, times_delivered: {info.DeliveryCount}");
            }
            // >>> message_id: min: 1712747506906-0, consumer: Bob, time_since_delivered: 31084, times_delivered: 1
            // >>> message_id: min: 1712747506907-0, consumer: Bob, time_since_delivered: 31084, times_delivered: 1
    
            // Tests for 'xpending_plus_minus' step.
    
    
            StreamEntry[] res28 = db.StreamRange("race:italy", "1712744358384-0", "1712744358384-0");
    
            foreach (StreamEntry entry in res28)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
            // >>> 1712744358384-0: [rider: Royce]
    
            // Tests for 'xrange_pending' step.
    
    
            StreamEntry[] res29 = db.StreamClaim(
                "race:italy", "italy_riders", "Alice", 60000, [1712744358384 - 0]
            );
    
            foreach (StreamEntry entry in res29)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
            // >>> 1712744358384-0: [rider: Royce]
    
            // Tests for 'xclaim' step.
    
            StreamAutoClaimResult res30 = db.StreamAutoClaim(
                "race:italy", "italy_riders", "Alice", 1, "0-0", 1
            );
    
            Console.WriteLine($"{res30.NextStartId}, ({string.Join(", ", res30.ClaimedEntries.Select(entry => $"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]"))})");
            // >>> 1712744379676-0, (1712744358384-0: [rider: Royce])
    
            // Tests for 'xautoclaim' step.
    
    
            StreamAutoClaimResult res31 = db.StreamAutoClaim(
                "race:italy", "italy_riders", "Alice", 1, "(1712744358384-0", 1
            );
    
            Console.WriteLine($"{res31.NextStartId}, ({string.Join(", ", res31.ClaimedEntries.Select(entry => $"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]"))})");
            // >>> 0-0, (1712744379676-0: [rider: Sam-Bodden])
    
            // Tests for 'xautoclaim_cursor' step.
    
    
            StreamInfo res32 = db.StreamInfo("race:italy");
            Console.WriteLine($"length: {res32.Length}, radix-tree-keys: {res32.RadixTreeKeys}, radix-tree-nodes: {res32.RadixTreeNodes}, last-generated-id: {res32.LastGeneratedId}, first-entry: {$"{res32.FirstEntry.Id}: [{string.Join(", ", res32.FirstEntry.Values.Select(b => $"{b.Name}: {b.Value}"))}]"}, last-entry: {$"{res32.LastEntry.Id}: [{string.Join(", ", res32.LastEntry.Values.Select(b => $"{b.Name}: {b.Value}"))}]"}");
            // >>> length: 5, radix-tree-keys: 1, radix-tree-nodes: 2, last-generated-id: 1712756762686-1, first-entry: 1712756762685-0: [rider: Castilla], last-entry: 1712756762686-1: [rider: Norem]
    
            // Tests for 'xinfo' step.
    
    
            StreamGroupInfo[] res33 = db.StreamGroupInfo("race:italy");
    
            foreach (StreamGroupInfo info in res33)
            {
                Console.WriteLine($"name: {info.Name}, consumers: {info.ConsumerCount}, pending: {info.PendingMessageCount}, last-delivered-id: {info.LastDeliveredId}");
            }
            // >>> name: italy_riders, consumers: 2, pending: 2, last-delivered-id: 1712757192730-2
    
            // Tests for 'xinfo_groups' step.
    
    
            StreamConsumerInfo[] res34 = db.StreamConsumerInfo("race:italy", "italy_riders");
    
            foreach (StreamConsumerInfo info in res34)
            {
                Console.WriteLine($"name: {info.Name}, pending: {info.PendingMessageCount}, idle: {info.IdleTimeInMilliseconds}");
            }
            // >>> name: Alice, pending: 1, idle: 7717
            // >>> name: Bob, pending: 0, idle: 7722
    
            // Tests for 'xinfo_consumers' step.
    
    
            db.StreamAdd(
                "race:italy", [new("rider", "Jones")], null, 2, true
            );
    
            db.StreamAdd(
                "race:italy", [new("rider", "Wood")], null, 2, true
            );
    
            db.StreamAdd(
                "race:italy", [new("rider", "Henshaw")], null, 2, true
            );
    
            long res35 = db.StreamLength("race:italy");
            Console.WriteLine(res35); // >>> 8
    
            StreamEntry[] res36 = db.StreamRange("race:italy", "-", "+");
    
            foreach (StreamEntry entry in res36)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
            // >>> 1712758336128-0: [rider: Castilla]
            // >>> 1712758336128-1: [rider: Royce]
            // >>> 1712758336128-2: [rider: Sam-Bodden]
            // >>> 1712758336129-0: [rider: Prickett]
            // >>> 1712758336139-0: [rider: Norem]
            // >>> 1712758340854-0: [rider: Jones]
            // >>> 1712758341645-0: [rider: Wood]
            // >>> 1712758342134-0: [rider: Henshaw]
    
            db.StreamAdd(
                "race:italy", [new("rider", "Smith")], null, 2, false
            );
    
            StreamEntry[] res37 = db.StreamRange("race:italy", "-", "+");
    
            foreach (StreamEntry entry in res37)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
            // 1712758746476-1: [rider: Henshaw]
            // 1712758746477-0: [rider: Smith]
    
            // Tests for 'maxlen' step.
    
    
            long res38 = db.StreamTrim("race:italy", 10, false);
            Console.WriteLine(res38);   // >>> 0
    
            // Tests for 'xtrim' step.
    
    
            long res39 = db.StreamTrim("race:italy", 10, true);
            Console.WriteLine(res39);   // >>> 0
    
            // Tests for 'xtrim2' step.
    
    
            StreamEntry[] res40 = db.StreamRange("race:italy", "-", "+");
    
            foreach (StreamEntry entry in res40)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            }
            // >>> 1712759694003-0: [rider: Henshaw]
            // >>> 1712759694003-1: [rider: Smith]
    
            long res41 = db.StreamDelete("race:italy", ["1712759694003-1"]);
            Console.WriteLine(res41);   // >>> 1
    
            StreamEntry[] res42 = db.StreamRange("race:italy", "-", "+");
    
            foreach (StreamEntry entry in res42)
            {
                Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
    
            }
            // >>> 1712759694003-0: [rider: Henshaw]
    
            // Tests for 'xdel' step.
    
    
        }
    }
    
    
    require 'redis'
    
    r = Redis.new
    
    
    res1 = r.xadd('race:france', {
      'rider' => 'Castilla',
      'speed' => 30.2,
      'position' => 1,
      'location_id' => 1
    })
    puts res1 # 1692632086370-0, for example
    
    res2 = r.xadd('race:france', {
      'rider' => 'Norem',
      'speed' => 28.8,
      'position' => 3,
      'location_id' => 1
    })
    puts res2 # 1692632094485-0, for example
    
    res3 = r.xadd('race:france', {
      'rider' => 'Prickett',
      'speed' => 29.7,
      'position' => 2,
      'location_id' => 1
    })
    puts res3 # 1692632102976-0, for example
    
    
    r.del('race:france')
    r.xadd('race:france', {
      'rider' => 'Castilla',
      'speed' => '30.2',
      'position' => '1',
      'location_id' => '1'
    }, id: '1692632086370-0')
    r.xadd('race:france', {
      'rider' => 'Norem',
      'speed' => '28.8',
      'position' => '3',
      'location_id' => '1'
    }, id: '1692632094485-0')
    r.xadd('race:france', {
      'rider' => 'Prickett',
      'speed' => '29.7',
      'position' => '2',
      'location_id' => '1'
    }, id: '1692632102976-0')
    r.xadd('race:france', {
      'rider' => 'Castilla',
      'speed' => '29.9',
      'position' => '1',
      'location_id' => '2'
    }, id: '1692632147973-0')
    res4 = r.xrange('race:france', '1692632086370-0', '+', count: 2)
    puts res4.inspect
    # [["1692632086370-0", {"rider"=>"Castilla", "speed"=>"30.2", "position"=>"1", "location_id"=>"1"}],
    #  ["1692632094485-0", {"rider"=>"Norem", "speed"=>"28.8", "position"=>"3", "location_id"=>"1"}]]
    
    
    r.del('race:france')
    r.xadd('race:france', {'rider' => 'Castilla'}, id: '1692632086370-0')
    res5 = r.xread(['race:france'], ['$'], count: 100, block: 300)
    puts res5.inspect # {}
    
    
    res6 = r.xadd('race:france', {
      'rider' => 'Castilla',
      'speed' => 29.9,
      'position' => 1,
      'location_id' => 2
    })
    puts res6 # 1692632147973-0, for example
    
    
    r.del('race:france')
    r.xadd('race:france', {'rider' => 'Castilla'}, id: '1692632086370-0')
    r.xadd('race:france', {'rider' => 'Norem'}, id: '1692632094485-0')
    r.xadd('race:france', {'rider' => 'Prickett'}, id: '1692632102976-0')
    r.xadd('race:france', {'rider' => 'Castilla'}, id: '1692632147973-0')
    res7 = r.xlen('race:france')
    puts res7 # 4
    
    
    r.del('race:usa')
    res8 = r.xadd('race:usa', {'racer' => 'Castilla'}, id: '0-1')
    puts res8 # 0-1
    
    res9 = r.xadd('race:usa', {'racer' => 'Norem'}, id: '0-2')
    puts res9 # 0-2
    
    
    begin
      r.xadd('race:usa', {'racer' => 'Prickett'}, id: '0-1')
    rescue Redis::CommandError => e
      puts e.message
      # ERR The ID specified in XADD is equal or smaller than the target stream top item
    end
    
    
    r.del('race:usa')
    r.xadd('race:usa', {'racer' => 'Castilla'}, id: '0-1')
    r.xadd('race:usa', {'racer' => 'Norem'}, id: '0-2')
    res10 = r.xadd('race:usa', {'racer' => 'Prickett'}, id: '0-*')
    puts res10 # 0-3
    
    
    r.del('race:france')
    r.xadd('race:france', {'rider' => 'Castilla', 'speed' => '30.2', 'position' => '1', 'location_id' => '1'}, id: '1692632086370-0')
    r.xadd('race:france', {'rider' => 'Norem', 'speed' => '28.8', 'position' => '3', 'location_id' => '1'}, id: '1692632094485-0')
    r.xadd('race:france', {'rider' => 'Prickett', 'speed' => '29.7', 'position' => '2', 'location_id' => '1'}, id: '1692632102976-0')
    r.xadd('race:france', {'rider' => 'Castilla', 'speed' => '29.9', 'position' => '1', 'location_id' => '2'}, id: '1692632147973-0')
    res11 = r.xrange('race:france', '-', '+')
    puts res11.inspect
    # [["1692632086370-0", {"rider"=>"Castilla", "speed"=>"30.2", "position"=>"1", "location_id"=>"1"}],
    #  ["1692632094485-0", {"rider"=>"Norem", "speed"=>"28.8", "position"=>"3", "location_id"=>"1"}],
    #  ["1692632102976-0", {"rider"=>"Prickett", "speed"=>"29.7", "position"=>"2", "location_id"=>"1"}],
    #  ["1692632147973-0", {"rider"=>"Castilla", "speed"=>"29.9", "position"=>"1", "location_id"=>"2"}]]
    
    
    r.del('race:france')
    r.xadd('race:france', {'rider' => 'Castilla', 'speed' => '30.2', 'position' => '1', 'location_id' => '1'}, id: '1692632086370-0')
    r.xadd('race:france', {'rider' => 'Norem', 'speed' => '28.8', 'position' => '3', 'location_id' => '1'}, id: '1692632094485-0')
    res12 = r.xrange('race:france', '1692632086369', '1692632086371')
    puts res12.inspect
    # [["1692632086370-0", {"rider"=>"Castilla", "speed"=>"30.2", "position"=>"1", "location_id"=>"1"}]]
    
    
    r.del('race:france')
    r.xadd('race:france', {'rider' => 'Castilla', 'speed' => '30.2', 'position' => '1', 'location_id' => '1'}, id: '1692632086370-0')
    r.xadd('race:france', {'rider' => 'Norem', 'speed' => '28.8', 'position' => '3', 'location_id' => '1'}, id: '1692632094485-0')
    r.xadd('race:france', {'rider' => 'Prickett', 'speed' => '29.7', 'position' => '2', 'location_id' => '1'}, id: '1692632102976-0')
    r.xadd('race:france', {'rider' => 'Castilla', 'speed' => '29.9', 'position' => '1', 'location_id' => '2'}, id: '1692632147973-0')
    res13 = r.xrange('race:france', '-', '+', count: 2)
    puts res13.inspect
    # [["1692632086370-0", {"rider"=>"Castilla", "speed"=>"30.2", "position"=>"1", "location_id"=>"1"}],
    #  ["1692632094485-0", {"rider"=>"Norem", "speed"=>"28.8", "position"=>"3", "location_id"=>"1"}]]
    
    
    r.del('race:france')
    r.xadd('race:france', {'rider' => 'Castilla', 'speed' => '30.2', 'position' => '1', 'location_id' => '1'}, id: '1692632086370-0')
    r.xadd('race:france', {'rider' => 'Norem', 'speed' => '28.8', 'position' => '3', 'location_id' => '1'}, id: '1692632094485-0')
    r.xadd('race:france', {'rider' => 'Prickett', 'speed' => '29.7', 'position' => '2', 'location_id' => '1'}, id: '1692632102976-0')
    r.xadd('race:france', {'rider' => 'Castilla', 'speed' => '29.9', 'position' => '1', 'location_id' => '2'}, id: '1692632147973-0')
    res14 = r.xrange('race:france', '(1692632094485-0', '+', count: 2)
    puts res14.inspect
    # [["1692632102976-0", {"rider"=>"Prickett", "speed"=>"29.7", "position"=>"2", "location_id"=>"1"}],
    #  ["1692632147973-0", {"rider"=>"Castilla", "speed"=>"29.9", "position"=>"1", "location_id"=>"2"}]]
    
    
    res15 = r.xrange('race:france', '(1692632147973-0', '+', count: 2)
    puts res15.inspect # []
    
    
    res16 = r.xrevrange('race:france', '+', '-', count: 1)
    puts res16.inspect
    # [["1692632147973-0", {"rider"=>"Castilla", "speed"=>"29.9", "position"=>"1", "location_id"=>"2"}]]
    
    
    res17 = r.xread(['race:france'], ['0'], count: 2)
    puts res17.inspect
    # {"race:france"=>[["1692632086370-0", {"rider"=>"Castilla", "speed"=>"30.2", "position"=>"1", "location_id"=>"1"}],
    #                  ["1692632094485-0", {"rider"=>"Norem", "speed"=>"28.8", "position"=>"3", "location_id"=>"1"}]]}
    
    
    r.del('race:france')
    r.xadd('race:france', {'rider' => 'Castilla'}, id: '1692632086370-0')
    res18 = r.xgroup(:create, 'race:france', 'france_riders', '$')
    puts res18 # OK
    
    
    r.del('race:italy')
    res19 = r.xgroup(:create, 'race:italy', 'italy_riders', '$', mkstream: true)
    puts res19 # OK
    
    
    r.del('race:italy')
    r.xgroup(:create, 'race:italy', 'italy_riders', '$', mkstream: true)
    r.xadd('race:italy', {'rider' => 'Castilla'}, id: '1692632639151-0')
    r.xadd('race:italy', {'rider' => 'Royce'}, id: '1692632647899-0')
    r.xadd('race:italy', {'rider' => 'Sam-Bodden'}, id: '1692632662819-0')
    r.xadd('race:italy', {'rider' => 'Prickett'}, id: '1692632670501-0')
    r.xadd('race:italy', {'rider' => 'Norem'}, id: '1692632678249-0')
    
    res20 = r.xreadgroup('italy_riders', 'Alice', ['race:italy'], ['>'], count: 1)
    puts res20.inspect
    # {"race:italy"=>[["1692632639151-0", {"rider"=>"Castilla"}]]}
    
    
    res21 = r.xreadgroup('italy_riders', 'Alice', ['race:italy'], ['0'], count: 1)
    puts res21.inspect
    # {"race:italy"=>[["1692632639151-0", {"rider"=>"Castilla"}]]}
    
    
    res22 = r.xack('race:italy', 'italy_riders', '1692632639151-0')
    puts res22 # 1
    
    res23 = r.xreadgroup('italy_riders', 'Alice', ['race:italy'], ['0'])
    puts res23.inspect
    # {"race:italy"=>[]}
    
    
    res24 = r.xreadgroup('italy_riders', 'Bob', ['race:italy'], ['>'], count: 2)
    puts res24.inspect
    # {"race:italy"=>[["1692632647899-0", {"rider"=>"Royce"}],
    #                 ["1692632662819-0", {"rider"=>"Sam-Bodden"}]]}
    
    
    res25 = r.xpending('race:italy', 'italy_riders')
    puts res25.inspect
    # {"size"=>2, "min_entry_id"=>"1692632647899-0", "max_entry_id"=>"1692632662819-0", "consumers"=>{"Bob"=>"2"}}
    
    
    res26 = r.xpending('race:italy', 'italy_riders', '-', '+', 10)
    puts res26.inspect
    
    
    res27 = r.xrange('race:italy', '1692632647899-0', '1692632647899-0')
    puts res27.inspect
    # [["1692632647899-0", {"rider"=>"Royce"}]]
    
    
    res28 = r.xclaim('race:italy', 'italy_riders', 'Alice', 0, '1692632647899-0')
    puts res28.inspect
    # [["1692632647899-0", {"rider"=>"Royce"}]]
    
    
    res29 = r.xautoclaim('race:italy', 'italy_riders', 'Alice', 0, '0-0', count: 1)
    puts res29.inspect
    # {"next"=>"1692632662819-0", "entries"=>[["1692632647899-0", {"rider"=>"Royce"}]]}
    
    
    res30 = r.xautoclaim('race:italy', 'italy_riders', 'Lora', 0, res29['next'], count: 1)
    puts res30.inspect
    # {"next"=>"0-0", "entries"=>[["1692632662819-0", {"rider"=>"Sam-Bodden"}]]}
    
    
    res31 = r.xinfo(:stream, 'race:italy')
    puts res31.inspect
    
    
    res32 = r.xinfo(:groups, 'race:italy')
    puts res32.inspect
    
    
    res33 = r.xinfo(:consumers, 'race:italy', 'italy_riders')
    puts res33.inspect
    
    
    r.del('race:italy')
    r.xadd('race:italy', {'rider' => 'Castilla'}, id: '1692632639151-0')
    r.xadd('race:italy', {'rider' => 'Royce'}, id: '1692632647899-0')
    r.xadd('race:italy', {'rider' => 'Sam-Bodden'}, id: '1692632662819-0')
    r.xadd('race:italy', {'rider' => 'Prickett'}, id: '1692632670501-0')
    r.xadd('race:italy', {'rider' => 'Norem'}, id: '1692632678249-0')
    r.xadd('race:italy', {'rider' => 'Jones'}, id: '1692633189161-0', maxlen: 2)
    r.xadd('race:italy', {'rider' => 'Wood'}, id: '1692633198206-0', maxlen: 2)
    r.xadd('race:italy', {'rider' => 'Henshaw'}, id: '1692633208557-0', maxlen: 2)
    
    res34 = r.xlen('race:italy')
    puts res34 # 2
    
    res35 = r.xrange('race:italy', '-', '+')
    puts res35.inspect
    # [["1692633198206-0", {"rider"=>"Wood"}], ["1692633208557-0", {"rider"=>"Henshaw"}]]
    
    
    res36 = r.xtrim('race:italy', 10, approximate: false)
    puts res36 # 0
    
    
    r.del('mystream')
    1.upto(10) do |n|
      r.xadd('mystream', {'field' => 'value'}, id: "#{n}-0")
    end
    res37 = r.xtrim('mystream', 10, approximate: true)
    puts res37 # 0
    
    
    r.del('race:italy')
    r.xadd('race:italy', {'rider' => 'Wood'}, id: '1692633198206-0')
    r.xadd('race:italy', {'rider' => 'Henshaw'}, id: '1692633208557-0')
    res38 = r.xrange('race:italy', '-', '+', count: 2)
    puts res38.inspect
    # [["1692633198206-0", {"rider"=>"Wood"}], ["1692633208557-0", {"rider"=>"Henshaw"}]]
    
    res39 = r.xdel('race:italy', '1692633208557-0')
    puts res39 # 1
    
    res40 = r.xrange('race:italy', '-', '+', count: 2)
    puts res40.inspect
    # [["1692633198206-0", {"rider"=>"Wood"}]]
    
    
    mod stream_tests {
        use redis::{
            streams::{
                StreamAutoClaimOptions, StreamInfoConsumersReply, StreamInfoGroupsReply,
                StreamInfoStreamReply, StreamMaxlen, StreamPendingCountReply, StreamPendingReply,
                StreamRangeReply, StreamReadOptions, StreamReadReply, StreamTrimmingMode,
                StreamTrimOptions,
            },
            Commands,
        };
        use std::{thread::sleep, time::Duration};
    
        fn delete_keys(r: &mut redis::Connection, keys: &[&str]) {
            let _: usize = r.del(keys).unwrap_or(0);
        }
    
        fn add_france_fixed(r: &mut redis::Connection) {
            delete_keys(r, &["race:france"]);
            let _: Option<String> = r
                .xadd(
                    "race:france",
                    "1692632086370-0",
                    &[
                        ("rider", "Castilla"),
                        ("speed", "30.2"),
                        ("position", "1"),
                        ("location_id", "1"),
                    ],
                )
                .expect("add france 1");
            let _: Option<String> = r
                .xadd(
                    "race:france",
                    "1692632094485-0",
                    &[
                        ("rider", "Norem"),
                        ("speed", "28.8"),
                        ("position", "3"),
                        ("location_id", "1"),
                    ],
                )
                .expect("add france 2");
            let _: Option<String> = r
                .xadd(
                    "race:france",
                    "1692632102976-0",
                    &[
                        ("rider", "Prickett"),
                        ("speed", "29.7"),
                        ("position", "2"),
                        ("location_id", "1"),
                    ],
                )
                .expect("add france 3");
            let _: Option<String> = r
                .xadd(
                    "race:france",
                    "1692632147973-0",
                    &[
                        ("rider", "Castilla"),
                        ("speed", "29.9"),
                        ("position", "1"),
                        ("location_id", "2"),
                    ],
                )
                .expect("add france 4");
        }
    
        fn seed_usa_fixed(r: &mut redis::Connection) {
            delete_keys(r, &["race:usa"]);
            let _: Option<String> = r
                .xadd("race:usa", "0-1", &[("racer", "Castilla")])
                .expect("add usa 1");
            let _: Option<String> = r
                .xadd("race:usa", "0-2", &[("racer", "Norem")])
                .expect("add usa 2");
        }
    
        fn seed_italy_group_base(r: &mut redis::Connection) {
            delete_keys(r, &["race:italy"]);
            let _: () = r
                .xgroup_create_mkstream("race:italy", "italy_riders", "$")
                .expect("create italy group");
            let _: Option<String> = r
                .xadd("race:italy", "1692632639151-0", &[("rider", "Castilla")])
                .expect("add italy 1");
            let _: Option<String> = r
                .xadd("race:italy", "1692632647899-0", &[("rider", "Royce")])
                .expect("add italy 2");
            let _: Option<String> = r
                .xadd("race:italy", "1692632662819-0", &[("rider", "Sam-Bodden")])
                .expect("add italy 3");
            let _: Option<String> = r
                .xadd("race:italy", "1692632670501-0", &[("rider", "Prickett")])
                .expect("add italy 4");
            let _: Option<String> = r
                .xadd("race:italy", "1692632678249-0", &[("rider", "Norem")])
                .expect("add italy 5");
        }
    
        fn seed_italy_alice_pending(r: &mut redis::Connection) {
            seed_italy_group_base(r);
            let opts = StreamReadOptions::default().group("italy_riders", "Alice").count(1);
            let _: Option<StreamReadReply> = r
                .xread_options(&["race:italy"], &[">"], &opts)
                .expect("alice read pending");
        }
    
        fn seed_italy_after_ack(r: &mut redis::Connection) {
            seed_italy_alice_pending(r);
            let _: usize = r
                .xack("race:italy", "italy_riders", &["1692632639151-0"])
                .expect("ack first italy message");
        }
    
        fn seed_italy_bob_pending(r: &mut redis::Connection) {
            seed_italy_after_ack(r);
            let opts = StreamReadOptions::default().group("italy_riders", "Bob").count(2);
            let _: Option<StreamReadReply> = r
                .xread_options(&["race:italy"], &[">"], &opts)
                .expect("bob read pending");
        }
    
        fn seed_italy_info_state(r: &mut redis::Connection) {
            seed_italy_bob_pending(r);
            sleep(Duration::from_millis(5));
            let _: redis::streams::StreamClaimReply = r
                .xclaim("race:italy", "italy_riders", "Alice", 1, &["1692632647899-0"])
                .expect("alice claim");
            sleep(Duration::from_millis(5));
            let _: redis::streams::StreamClaimReply = r
                .xclaim("race:italy", "italy_riders", "Lora", 1, &["1692632662819-0"])
                .expect("lora claim");
        }
    
        fn seed_trim_stream(r: &mut redis::Connection) {
            delete_keys(r, &["mystream"]);
            for id in ["1-0", "2-0", "3-0", "4-0", "5-0", "6-0", "7-0", "8-0", "9-0", "10-0"] {
                let _: Option<String> = r
                    .xadd("mystream", id, &[("field", "value")])
                    .expect("seed mystream");
            }
        }
    
        fn run() {
            let mut r = match redis::Client::open("redis://127.0.0.1") {
                Ok(client) => match client.get_connection() {
                    Ok(conn) => conn,
                    Err(e) => {
                        println!("Failed to connect to Redis: {e}");
                        return;
                    }
                },
                Err(e) => {
                    println!("Failed to create Redis client: {e}");
                    return;
                }
            };
    
            let res1 = {
                let res: Option<String> = r
                    .xadd(
                        "race:france",
                        "*",
                        &[
                            ("rider", "Castilla"),
                            ("speed", "30.2"),
                            ("position", "1"),
                            ("location_id", "1"),
                        ],
                    )
                    .expect("xadd 1");
                res.expect("missing stream id")
            };
            println!("{res1}"); // >>> 1692632086370-0
    
            let res2 = {
                let res: Option<String> = r
                    .xadd(
                        "race:france",
                        "*",
                        &[
                            ("rider", "Norem"),
                            ("speed", "28.8"),
                            ("position", "3"),
                            ("location_id", "1"),
                        ],
                    )
                    .expect("xadd 2");
                res.expect("missing stream id")
            };
            println!("{res2}"); // >>> 1692632094485-0
    
            let res3 = {
                let res: Option<String> = r
                    .xadd(
                        "race:france",
                        "*",
                        &[
                            ("rider", "Prickett"),
                            ("speed", "29.7"),
                            ("position", "2"),
                            ("location_id", "1"),
                        ],
                    )
                    .expect("xadd 3");
                res.expect("missing stream id")
            };
            println!("{res3}"); // >>> 1692632102976-0
    
            add_france_fixed(&mut r);
            if let Ok(res) = r.xrange_count("race:france", "1692632086370-0", "+", 2) {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![
                                ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                (
                                    "location_id".to_string(),
                                    entry.get::<String>("location_id").expect("missing location_id"),
                                ),
                            ],
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("1692632086370-0", [("rider", "Castilla"), ("speed", "30.2"), ("position", "1"), ("location_id", "1")]), ("1692632094485-0", [("rider", "Norem"), ("speed", "28.8"), ("position", "3"), ("location_id", "1")])]
            }
    
            add_france_fixed(&mut r);
            let opts = StreamReadOptions::default().count(100).block(300);
            if let Ok(res) = r.xread_options(&["race:france"], &["$"], &opts) {
                let res: Option<StreamReadReply> = res;
                println!("{res:?}"); // >>> None
            }
    
            if let Ok(res) = r.xadd(
                "race:france",
                "*",
                &[
                    ("rider", "Castilla"),
                    ("speed", "29.9"),
                    ("position", "1"),
                    ("location_id", "2"),
                ],
            ) {
                let res: Option<String> = res;
                let res = res.expect("missing stream id");
                println!("{res}"); // >>> 1692632147973-0
            }
    
            add_france_fixed(&mut r);
            if let Ok(res) = r.xlen("race:france") {
                let res: usize = res;
                println!("{res}"); // >>> 4
            }
    
            delete_keys(&mut r, &["race:usa"]);
            if let Ok(res) = r.xadd("race:usa", "0-1", &[("racer", "Castilla")]) {
                let res: Option<String> = res;
                let res = res.expect("missing stream id");
                println!("{res}"); // >>> 0-1
            }
    
            if let Ok(res) = r.xadd("race:usa", "0-2", &[("racer", "Norem")]) {
                let res: Option<String> = res;
                let res = res.expect("missing stream id");
                println!("{res}"); // >>> 0-2
            }
    
            let res: redis::RedisResult<Option<String>> =
                r.xadd("race:usa", "0-1", &[("racer", "Prickett")]);
            match res {
                Ok(_) => {}
                Err(e) => {
                    let msg = e.to_string();
                    println!("{msg}");
                    // >>> An error was signalled by the server - ResponseError: The ID specified in XADD is equal or smaller than the target stream top item
                }
            }
    
            seed_usa_fixed(&mut r);
            if let Ok(res) = r.xadd("race:usa", "0-*", &[("racer", "Prickett")]) {
                let res: Option<String> = res;
                let res = res.expect("missing stream id");
                println!("{res}"); // >>> 0-3
            }
    
            add_france_fixed(&mut r);
            if let Ok(res) = r.xrange_all("race:france") {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![
                                ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                (
                                    "location_id".to_string(),
                                    entry.get::<String>("location_id").expect("missing location_id"),
                                ),
                            ],
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("1692632086370-0", [("rider", "Castilla"), ("speed", "30.2"), ("position", "1"), ("location_id", "1")]), ("1692632094485-0", [("rider", "Norem"), ("speed", "28.8"), ("position", "3"), ("location_id", "1")]), ("1692632102976-0", [("rider", "Prickett"), ("speed", "29.7"), ("position", "2"), ("location_id", "1")]), ("1692632147973-0", [("rider", "Castilla"), ("speed", "29.9"), ("position", "1"), ("location_id", "2")])]
            }
    
            add_france_fixed(&mut r);
            if let Ok(res) = r.xrange("race:france", "1692632086369", "1692632086371") {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![
                                ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                (
                                    "location_id".to_string(),
                                    entry.get::<String>("location_id").expect("missing location_id"),
                                ),
                            ],
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("1692632086370-0", [("rider", "Castilla"), ("speed", "30.2"), ("position", "1"), ("location_id", "1")])]
            }
    
            add_france_fixed(&mut r);
            if let Ok(res) = r.xrange_count("race:france", "-", "+", 2) {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![
                                ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                (
                                    "location_id".to_string(),
                                    entry.get::<String>("location_id").expect("missing location_id"),
                                ),
                            ],
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("1692632086370-0", [("rider", "Castilla"), ("speed", "30.2"), ("position", "1"), ("location_id", "1")]), ("1692632094485-0", [("rider", "Norem"), ("speed", "28.8"), ("position", "3"), ("location_id", "1")])]
            }
    
            add_france_fixed(&mut r);
            if let Ok(res) = r.xrange_count("race:france", "(1692632094485-0", "+", 2) {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![
                                ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                (
                                    "location_id".to_string(),
                                    entry.get::<String>("location_id").expect("missing location_id"),
                                ),
                            ],
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("1692632102976-0", [("rider", "Prickett"), ("speed", "29.7"), ("position", "2"), ("location_id", "1")]), ("1692632147973-0", [("rider", "Castilla"), ("speed", "29.9"), ("position", "1"), ("location_id", "2")])]
            }
    
            add_france_fixed(&mut r);
            if let Ok(res) = r.xrange_count("race:france", "(1692632147973-0", "+", 2) {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![
                                ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                (
                                    "location_id".to_string(),
                                    entry.get::<String>("location_id").expect("missing location_id"),
                                ),
                            ],
                        )
                    })
                    .collect();
                println!("{view:?}"); // >>> []
            }
    
            add_france_fixed(&mut r);
            if let Ok(res) = r.xrevrange_count("race:france", "+", "-", 1) {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![
                                ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                (
                                    "location_id".to_string(),
                                    entry.get::<String>("location_id").expect("missing location_id"),
                                ),
                            ],
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("1692632147973-0", [("rider", "Castilla"), ("speed", "29.9"), ("position", "1"), ("location_id", "2")])]
            }
    
            add_france_fixed(&mut r);
            let opts = StreamReadOptions::default().count(2);
            if let Ok(res) = r.xread_options(&["race:france"], &["0"], &opts) {
                let res: Option<StreamReadReply> = res;
                let reply = res.expect("xread should return data");
                let view: Vec<_> = reply
                    .keys
                    .iter()
                    .map(|stream| {
                        (
                            stream.key.clone(),
                            stream
                                .ids
                                .iter()
                                .map(|entry| {
                                    (
                                        entry.id.clone(),
                                        vec![
                                            ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                            ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                            ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                            (
                                                "location_id".to_string(),
                                                entry.get::<String>("location_id").expect("missing location_id"),
                                            ),
                                        ],
                                    )
                                })
                                .collect::<Vec<_>>(),
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("race:france", [("1692632086370-0", [("rider", "Castilla"), ("speed", "30.2"), ("position", "1"), ("location_id", "1")]), ("1692632094485-0", [("rider", "Norem"), ("speed", "28.8"), ("position", "3"), ("location_id", "1")])])]
            }
    
            add_france_fixed(&mut r);
            if let Ok(res) = r.xgroup_create("race:france", "france_riders", "$") {
                let res: () = res;
                let _ = res;
                println!("OK"); // >>> OK
            }
    
            delete_keys(&mut r, &["race:italy"]);
            if let Ok(res) = r.xgroup_create_mkstream("race:italy", "italy_riders", "$") {
                let res: () = res;
                let _ = res;
                println!("OK"); // >>> OK
            }
    
            delete_keys(&mut r, &["race:italy"]);
            let _: () = r
                .xgroup_create_mkstream("race:italy", "italy_riders", "$")
                .expect("create italy group");
            let italy_1: Option<String> = r
                .xadd("race:italy", "1692632639151-0", &[("rider", "Castilla")])
                .expect("italy1");
            let italy_1 = italy_1.expect("missing stream id");
            println!("{italy_1}"); // >>> 1692632639151-0
            let italy_2: Option<String> = r
                .xadd("race:italy", "1692632647899-0", &[("rider", "Royce")])
                .expect("italy2");
            let italy_2 = italy_2.expect("missing stream id");
            println!("{italy_2}"); // >>> 1692632647899-0
            let italy_3: Option<String> = r
                .xadd("race:italy", "1692632662819-0", &[("rider", "Sam-Bodden")])
                .expect("italy3");
            let italy_3 = italy_3.expect("missing stream id");
            println!("{italy_3}"); // >>> 1692632662819-0
            let italy_4: Option<String> = r
                .xadd("race:italy", "1692632670501-0", &[("rider", "Prickett")])
                .expect("italy4");
            let italy_4 = italy_4.expect("missing stream id");
            println!("{italy_4}"); // >>> 1692632670501-0
            let italy_5: Option<String> = r
                .xadd("race:italy", "1692632678249-0", &[("rider", "Norem")])
                .expect("italy5");
            let italy_5 = italy_5.expect("missing stream id");
            println!("{italy_5}"); // >>> 1692632678249-0
    
            let opts = StreamReadOptions::default().group("italy_riders", "Alice").count(1);
            if let Ok(res) = r.xread_options(&["race:italy"], &[">"], &opts) {
                let res: Option<StreamReadReply> = res;
                let view: Vec<_> = res
                    .expect("xgroup read should return data")
                    .keys
                    .iter()
                    .map(|stream| {
                        (
                            stream.key.clone(),
                            stream
                                .ids
                                .iter()
                                .map(|entry| {
                                    (
                                        entry.id.clone(),
                                        vec![(
                                            "rider".to_string(),
                                            entry.get::<String>("rider").expect("missing rider"),
                                        )],
                                    )
                                })
                                .collect::<Vec<_>>(),
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("race:italy", [("1692632639151-0", [("rider", "Castilla")])])]
            }
    
            seed_italy_alice_pending(&mut r);
            let opts = StreamReadOptions::default().group("italy_riders", "Alice");
            if let Ok(res) = r.xread_options(&["race:italy"], &["0"], &opts) {
                let res: Option<StreamReadReply> = res;
                let view: Vec<_> = res
                    .expect("xgroup history")
                    .keys
                    .iter()
                    .map(|stream| {
                        (
                            stream.key.clone(),
                            stream
                                .ids
                                .iter()
                                .map(|entry| {
                                    (
                                        entry.id.clone(),
                                        vec![(
                                            "rider".to_string(),
                                            entry.get::<String>("rider").expect("missing rider"),
                                        )],
                                    )
                                })
                                .collect::<Vec<_>>(),
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("race:italy", [("1692632639151-0", [("rider", "Castilla")])])]
            }
    
            seed_italy_alice_pending(&mut r);
            if let Ok(res) = r.xack("race:italy", "italy_riders", &["1692632639151-0"]) {
                let res: usize = res;
                println!("{res}"); // >>> 1
            }
    
            let opts = StreamReadOptions::default().group("italy_riders", "Alice");
            if let Ok(res) = r.xread_options(&["race:italy"], &["0"], &opts) {
                let res: Option<StreamReadReply> = res;
                let view: Vec<_> = res
                    .expect("xgroup history")
                    .keys
                    .iter()
                    .map(|stream| {
                        (
                            stream.key.clone(),
                            stream
                                .ids
                                .iter()
                                .map(|entry| {
                                    (
                                        entry.id.clone(),
                                        vec![(
                                            "rider".to_string(),
                                            entry.get::<String>("rider").expect("missing rider"),
                                        )],
                                    )
                                })
                                .collect::<Vec<_>>(),
                        )
                    })
                    .collect();
                println!("{view:?}"); // >>> [("race:italy", [])]
            }
    
            seed_italy_after_ack(&mut r);
            let opts = StreamReadOptions::default().group("italy_riders", "Bob").count(2);
            if let Ok(res) = r.xread_options(&["race:italy"], &[">"], &opts) {
                let res: Option<StreamReadReply> = res;
                let view: Vec<_> = res
                    .expect("bob should receive data")
                    .keys
                    .iter()
                    .map(|stream| {
                        (
                            stream.key.clone(),
                            stream
                                .ids
                                .iter()
                                .map(|entry| {
                                    (
                                        entry.id.clone(),
                                        vec![(
                                            "rider".to_string(),
                                            entry.get::<String>("rider").expect("missing rider"),
                                        )],
                                    )
                                })
                                .collect::<Vec<_>>(),
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("race:italy", [("1692632647899-0", [("rider", "Royce")]), ("1692632662819-0", [("rider", "Sam-Bodden")])])]
            }
    
            seed_italy_bob_pending(&mut r);
            if let Ok(res) = r.xpending("race:italy", "italy_riders") {
                let res: StreamPendingReply = res;
                let view = match res {
                    StreamPendingReply::Empty => None,
                    StreamPendingReply::Data(data) => Some((
                        data.count,
                        data.start_id.clone(),
                        data.end_id.clone(),
                        data.consumers
                            .iter()
                            .map(|consumer| (consumer.name.clone(), consumer.pending))
                            .collect::<Vec<_>>(),
                    )),
                }
                .expect("pending summary");
                println!("{view:?}");
                // >>> (2, "1692632647899-0", "1692632662819-0", [("Bob", 2)])
            }
    
            seed_italy_bob_pending(&mut r);
            sleep(Duration::from_millis(5));
            if let Ok(res) = r.xpending_count("race:italy", "italy_riders", "-", "+", 10) {
                let res: StreamPendingCountReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            entry.consumer.clone(),
                            entry.last_delivered_ms,
                            entry.times_delivered,
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("1692632647899-0", "Bob", 5, 1), ("1692632662819-0", "Bob", 5, 1)]
            }
    
            seed_italy_bob_pending(&mut r);
            if let Ok(res) = r.xrange("race:italy", "1692632647899-0", "1692632647899-0") {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![(
                                "rider".to_string(),
                                entry.get::<String>("rider").expect("missing rider"),
                            )],
                        )
                    })
                    .collect();
                println!("{view:?}"); // >>> [("1692632647899-0", [("rider", "Royce")])]
            }
    
            seed_italy_bob_pending(&mut r);
            sleep(Duration::from_millis(5));
            if let Ok(res) = r.xclaim("race:italy", "italy_riders", "Alice", 1, &["1692632647899-0"]) {
                let res: redis::streams::StreamClaimReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![(
                                "rider".to_string(),
                                entry.get::<String>("rider").expect("missing rider"),
                            )],
                        )
                    })
                    .collect();
                println!("{view:?}"); // >>> [("1692632647899-0", [("rider", "Royce")])]
            }
    
            seed_italy_bob_pending(&mut r);
            sleep(Duration::from_millis(5));
            let opts = StreamAutoClaimOptions::default().count(1);
            if let Ok(res) = r.xautoclaim_options("race:italy", "italy_riders", "Alice", 1, "0-0", opts) {
                let res: redis::streams::StreamAutoClaimReply = res;
                let claimed: Vec<_> = res
                    .claimed
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![(
                                "rider".to_string(),
                                entry.get::<String>("rider").expect("missing rider"),
                            )],
                        )
                    })
                    .collect();
                println!("{:?}", (res.next_stream_id.clone(), &claimed));
                // >>> ("1692632662819-0", [("1692632647899-0", [("rider", "Royce")])])
            }
    
            seed_italy_bob_pending(&mut r);
            sleep(Duration::from_millis(5));
            let first_opts = StreamAutoClaimOptions::default().count(1);
            let _: redis::streams::StreamAutoClaimReply = r
                .xautoclaim_options("race:italy", "italy_riders", "Alice", 1, "0-0", first_opts)
                .expect("first autoclaim");
            let next_opts = StreamAutoClaimOptions::default().count(1);
            if let Ok(res) = r.xautoclaim_options(
                "race:italy",
                "italy_riders",
                "Lora",
                1,
                "(1692632647899-0",
                next_opts,
            ) {
                let res: redis::streams::StreamAutoClaimReply = res;
                let claimed: Vec<_> = res
                    .claimed
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![(
                                "rider".to_string(),
                                entry.get::<String>("rider").expect("missing rider"),
                            )],
                        )
                    })
                    .collect();
                println!("{:?}", (res.next_stream_id.clone(), &claimed));
                // >>> ("0-0", [("1692632662819-0", [("rider", "Sam-Bodden")])])
            }
    
            seed_italy_info_state(&mut r);
            if let Ok(res) = r.xinfo_stream("race:italy") {
                let res: StreamInfoStreamReply = res;
                let view = (
                    res.length,
                    res.radix_tree_keys,
                    res.groups,
                    res.last_generated_id.clone(),
                    res.first_entry.id.clone(),
                    res.last_entry.id.clone(),
                );
                println!("{view:?}");
                // >>> (5, 1, 1, "1692632678249-0", "1692632639151-0", "1692632678249-0")
            }
    
            seed_italy_info_state(&mut r);
            if let Ok(res) = r.xinfo_groups("race:italy") {
                let res: StreamInfoGroupsReply = res;
                let view: Vec<_> = res
                    .groups
                    .iter()
                    .map(|group| {
                        (
                            group.name.clone(),
                            group.consumers,
                            group.pending,
                            group.last_delivered_id.clone(),
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("italy_riders", 3, 2, "1692632662819-0")]
            }
    
            seed_italy_info_state(&mut r);
            if let Ok(res) = r.xinfo_consumers("race:italy", "italy_riders") {
                let res: StreamInfoConsumersReply = res;
                let mut view: Vec<_> = res
                    .consumers
                    .iter()
                    .map(|consumer| (consumer.name.clone(), consumer.pending, consumer.idle))
                    .collect();
                view.sort_by(|a, b| a.0.cmp(&b.0));
                println!("{view:?}");
                // >>> [("Alice", 1, 5), ("Bob", 0, 5), ("Lora", 1, 5)]
            }
    
            delete_keys(&mut r, &["race:italy"]);
            let max1: Option<String> = r
                .xadd_maxlen("race:italy", StreamMaxlen::Equals(2), "1-0", &[("rider", "Jones")])
                .expect("maxlen add 1");
            let max1 = max1.expect("missing stream id");
            println!("{max1}"); // >>> 1-0
            let max2: Option<String> = r
                .xadd_maxlen("race:italy", StreamMaxlen::Equals(2), "2-0", &[("rider", "Wood")])
                .expect("maxlen add 2");
            let max2 = max2.expect("missing stream id");
            println!("{max2}"); // >>> 2-0
            let max3: Option<String> = r
                .xadd_maxlen("race:italy", StreamMaxlen::Equals(2), "3-0", &[("rider", "Henshaw")])
                .expect("maxlen add 3");
            let max3 = max3.expect("missing stream id");
            println!("{max3}"); // >>> 3-0
    
            if let Ok(res) = r.xlen("race:italy") {
                let res: usize = res;
                println!("{res}"); // >>> 2
            }
    
            if let Ok(res) = r.xrange_all("race:italy") {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![(
                                "rider".to_string(),
                                entry.get::<String>("rider").expect("missing rider"),
                            )],
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("2-0", [("rider", "Wood")]), ("3-0", [("rider", "Henshaw")])]
            }
    
            delete_keys(&mut r, &["race:italy"]);
            let _: Option<String> = r.xadd("race:italy", "1-0", &[("rider", "Wood")]).expect("trim seed 1");
            let _: Option<String> = r.xadd("race:italy", "2-0", &[("rider", "Henshaw")]).expect("trim seed 2");
            if let Ok(res) = r.xtrim("race:italy", StreamMaxlen::Equals(10)) {
                let res: usize = res;
                println!("{res}"); // >>> 0
            }
    
            seed_trim_stream(&mut r);
            if let Ok(res) = r.xtrim_options(
                "mystream",
                &StreamTrimOptions::maxlen(StreamTrimmingMode::Approx, 10),
            ) {
                let res: usize = res;
                println!("{res}"); // >>> 0
            }
    
            delete_keys(&mut r, &["race:italy"]);
            let _: Option<String> = r.xadd("race:italy", "2-0", &[("rider", "Wood")]).expect("xdel seed 1");
            let _: Option<String> = r.xadd("race:italy", "3-0", &[("rider", "Henshaw")]).expect("xdel seed 2");
            if let Ok(res) = r.xrange_count("race:italy", "-", "+", 2) {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![(
                                "rider".to_string(),
                                entry.get::<String>("rider").expect("missing rider"),
                            )],
                        )
                    })
                    .collect();
                println!("{view:?}"); // >>> [("2-0", [("rider", "Wood")]), ("3-0", [("rider", "Henshaw")])]
            }
    
            if let Ok(res) = r.xdel("race:italy", &["3-0"]) {
                let res: usize = res;
                println!("{res}"); // >>> 1
            }
    
            if let Ok(res) = r.xrange_count("race:italy", "-", "+", 2) {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![(
                                "rider".to_string(),
                                entry.get::<String>("rider").expect("missing rider"),
                            )],
                        )
                    })
                    .collect();
                println!("{view:?}"); // >>> [("2-0", [("rider", "Wood")])]
            }
        }
    }
    
    mod tests {
        use redis::{
            streams::{
                StreamAutoClaimOptions, StreamInfoConsumersReply, StreamInfoGroupsReply,
                StreamInfoStreamReply, StreamMaxlen, StreamPendingCountReply, StreamPendingReply,
                StreamRangeReply, StreamReadOptions, StreamReadReply, StreamTrimmingMode, StreamTrimOptions,
            },
            AsyncCommands,
        };
        use tokio::time::{sleep, Duration};
    
        async fn delete_keys(r: &mut redis::aio::MultiplexedConnection, keys: &[&str]) {
            let _: usize = r.del(keys).await.unwrap_or(0);
        }
    
        async fn add_france_fixed(r: &mut redis::aio::MultiplexedConnection) {
            delete_keys(r, &["race:france"]).await;
            let _: Option<String> = r
                .xadd(
                    "race:france",
                    "1692632086370-0",
                    &[
                        ("rider", "Castilla"),
                        ("speed", "30.2"),
                        ("position", "1"),
                        ("location_id", "1"),
                    ],
                )
                .await
                .expect("add france 1");
            let _: Option<String> = r
                .xadd(
                    "race:france",
                    "1692632094485-0",
                    &[
                        ("rider", "Norem"),
                        ("speed", "28.8"),
                        ("position", "3"),
                        ("location_id", "1"),
                    ],
                )
                .await
                .expect("add france 2");
            let _: Option<String> = r
                .xadd(
                    "race:france",
                    "1692632102976-0",
                    &[
                        ("rider", "Prickett"),
                        ("speed", "29.7"),
                        ("position", "2"),
                        ("location_id", "1"),
                    ],
                )
                .await
                .expect("add france 3");
            let _: Option<String> = r
                .xadd(
                    "race:france",
                    "1692632147973-0",
                    &[
                        ("rider", "Castilla"),
                        ("speed", "29.9"),
                        ("position", "1"),
                        ("location_id", "2"),
                    ],
                )
                .await
                .expect("add france 4");
        }
    
        async fn seed_usa_fixed(r: &mut redis::aio::MultiplexedConnection) {
            delete_keys(r, &["race:usa"]).await;
            let _: Option<String> = r
                .xadd("race:usa", "0-1", &[("racer", "Castilla")])
                .await
                .expect("add usa 1");
            let _: Option<String> = r
                .xadd("race:usa", "0-2", &[("racer", "Norem")])
                .await
                .expect("add usa 2");
        }
    
        async fn seed_italy_group_base(r: &mut redis::aio::MultiplexedConnection) {
            delete_keys(r, &["race:italy"]).await;
            let _: () = r
                .xgroup_create_mkstream("race:italy", "italy_riders", "$")
                .await
                .expect("create italy group");
            let _: Option<String> = r
                .xadd("race:italy", "1692632639151-0", &[("rider", "Castilla")])
                .await
                .expect("add italy 1");
            let _: Option<String> = r
                .xadd("race:italy", "1692632647899-0", &[("rider", "Royce")])
                .await
                .expect("add italy 2");
            let _: Option<String> = r
                .xadd("race:italy", "1692632662819-0", &[("rider", "Sam-Bodden")])
                .await
                .expect("add italy 3");
            let _: Option<String> = r
                .xadd("race:italy", "1692632670501-0", &[("rider", "Prickett")])
                .await
                .expect("add italy 4");
            let _: Option<String> = r
                .xadd("race:italy", "1692632678249-0", &[("rider", "Norem")])
                .await
                .expect("add italy 5");
        }
    
        async fn seed_italy_alice_pending(r: &mut redis::aio::MultiplexedConnection) {
            seed_italy_group_base(r).await;
            let opts = StreamReadOptions::default().group("italy_riders", "Alice").count(1);
            let _: Option<StreamReadReply> = r
                .xread_options(&["race:italy"], &[">"], &opts)
                .await
                .expect("alice read pending");
        }
    
        async fn seed_italy_after_ack(r: &mut redis::aio::MultiplexedConnection) {
            seed_italy_alice_pending(r).await;
            let _: usize = r
                .xack("race:italy", "italy_riders", &["1692632639151-0"])
                .await
                .expect("ack first italy message");
        }
    
        async fn seed_italy_bob_pending(r: &mut redis::aio::MultiplexedConnection) {
            seed_italy_after_ack(r).await;
            let opts = StreamReadOptions::default().group("italy_riders", "Bob").count(2);
            let _: Option<StreamReadReply> = r
                .xread_options(&["race:italy"], &[">"], &opts)
                .await
                .expect("bob read pending");
        }
    
        async fn seed_italy_info_state(r: &mut redis::aio::MultiplexedConnection) {
            seed_italy_bob_pending(r).await;
            sleep(Duration::from_millis(5)).await;
            let _: redis::streams::StreamClaimReply = r
                .xclaim("race:italy", "italy_riders", "Alice", 1, &["1692632647899-0"])
                .await
                .expect("alice claim");
            sleep(Duration::from_millis(5)).await;
            let _: redis::streams::StreamClaimReply = r
                .xclaim("race:italy", "italy_riders", "Lora", 1, &["1692632662819-0"])
                .await
                .expect("lora claim");
        }
    
        async fn seed_trim_stream(r: &mut redis::aio::MultiplexedConnection) {
            delete_keys(r, &["mystream"]).await;
            for id in ["1-0", "2-0", "3-0", "4-0", "5-0", "6-0", "7-0", "8-0", "9-0", "10-0"] {
                let _: Option<String> = r
                    .xadd("mystream", id, &[("field", "value")])
                    .await
                    .expect("seed mystream");
            }
        }
    
        async fn run() {
            let mut r = match redis::Client::open("redis://127.0.0.1") {
                Ok(client) => match client.get_multiplexed_async_connection().await {
                    Ok(conn) => conn,
                    Err(e) => {
                        println!("Failed to connect to Redis: {e}");
                        return;
                    }
                },
                Err(e) => {
                    println!("Failed to create Redis client: {e}");
                    return;
                }
            };
    
            let res1: Option<String> = r
                .xadd(
                    "race:france",
                    "*",
                    &[
                        ("rider", "Castilla"),
                        ("speed", "30.2"),
                        ("position", "1"),
                        ("location_id", "1"),
                    ],
                )
                .await
                .expect("xadd 1");
            let res1 = res1.expect("missing stream id");
            println!("{res1}"); // >>> 1692632086370-0
    
            let res2: Option<String> = r
                .xadd(
                    "race:france",
                    "*",
                    &[
                        ("rider", "Norem"),
                        ("speed", "28.8"),
                        ("position", "3"),
                        ("location_id", "1"),
                    ],
                )
                .await
                .expect("xadd 2");
            let res2 = res2.expect("missing stream id");
            println!("{res2}"); // >>> 1692632094485-0
    
            let res3: Option<String> = r
                .xadd(
                    "race:france",
                    "*",
                    &[
                        ("rider", "Prickett"),
                        ("speed", "29.7"),
                        ("position", "2"),
                        ("location_id", "1"),
                    ],
                )
                .await
                .expect("xadd 3");
            let res3 = res3.expect("missing stream id");
            println!("{res3}"); // >>> 1692632102976-0
    
            add_france_fixed(&mut r).await;
            if let Ok(res) = r.xrange_count("race:france", "1692632086370-0", "+", 2).await {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![
                                ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                (
                                    "location_id".to_string(),
                                    entry.get::<String>("location_id").expect("missing location_id"),
                                ),
                            ],
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("1692632086370-0", [("rider", "Castilla"), ("speed", "30.2"), ("position", "1"), ("location_id", "1")]), ("1692632094485-0", [("rider", "Norem"), ("speed", "28.8"), ("position", "3"), ("location_id", "1")])]
            }
    
            add_france_fixed(&mut r).await;
            let opts = StreamReadOptions::default().count(100).block(300);
            if let Ok(res) = r.xread_options(&["race:france"], &["$"], &opts).await {
                let res: Option<StreamReadReply> = res;
                println!("{res:?}"); // >>> None
            }
    
            if let Ok(res) = r
                .xadd(
                    "race:france",
                    "*",
                    &[
                        ("rider", "Castilla"),
                        ("speed", "29.9"),
                        ("position", "1"),
                        ("location_id", "2"),
                    ],
                )
                .await
            {
                let res: Option<String> = res;
                let res = res.expect("missing stream id");
                println!("{res}"); // >>> 1692632147973-0
            }
    
            add_france_fixed(&mut r).await;
            if let Ok(res) = r.xlen("race:france").await {
                let res: usize = res;
                println!("{res}"); // >>> 4
            }
    
            delete_keys(&mut r, &["race:usa"]).await;
            if let Ok(res) = r.xadd("race:usa", "0-1", &[("racer", "Castilla")]).await {
                let res: Option<String> = res;
                let res = res.expect("missing stream id");
                println!("{res}"); // >>> 0-1
            }
            if let Ok(res) = r.xadd("race:usa", "0-2", &[("racer", "Norem")]).await {
                let res: Option<String> = res;
                let res = res.expect("missing stream id");
                println!("{res}"); // >>> 0-2
            }
    
            let res: redis::RedisResult<Option<String>> =
                r.xadd("race:usa", "0-1", &[("racer", "Prickett")]).await;
            match res {
                Ok(_) => {}
                Err(e) => {
                    let msg = e.to_string();
                    println!("{msg}");
                    // >>> An error was signalled by the server - ResponseError: The ID specified in XADD is equal or smaller than the target stream top item
                }
            }
    
            seed_usa_fixed(&mut r).await;
            if let Ok(res) = r.xadd("race:usa", "0-*", &[("racer", "Prickett")]).await {
                let res: Option<String> = res;
                let res = res.expect("missing stream id");
                println!("{res}"); // >>> 0-3
            }
    
            add_france_fixed(&mut r).await;
            if let Ok(res) = r.xrange_all("race:france").await {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![
                                ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                (
                                    "location_id".to_string(),
                                    entry.get::<String>("location_id").expect("missing location_id"),
                                ),
                            ],
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("1692632086370-0", [("rider", "Castilla"), ("speed", "30.2"), ("position", "1"), ("location_id", "1")]), ("1692632094485-0", [("rider", "Norem"), ("speed", "28.8"), ("position", "3"), ("location_id", "1")]), ("1692632102976-0", [("rider", "Prickett"), ("speed", "29.7"), ("position", "2"), ("location_id", "1")]), ("1692632147973-0", [("rider", "Castilla"), ("speed", "29.9"), ("position", "1"), ("location_id", "2")])]
            }
    
            add_france_fixed(&mut r).await;
            if let Ok(res) = r.xrange("race:france", "1692632086369", "1692632086371").await {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![
                                ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                (
                                    "location_id".to_string(),
                                    entry.get::<String>("location_id").expect("missing location_id"),
                                ),
                            ],
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("1692632086370-0", [("rider", "Castilla"), ("speed", "30.2"), ("position", "1"), ("location_id", "1")])]
            }
    
            add_france_fixed(&mut r).await;
            if let Ok(res) = r.xrange_count("race:france", "-", "+", 2).await {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![
                                ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                (
                                    "location_id".to_string(),
                                    entry.get::<String>("location_id").expect("missing location_id"),
                                ),
                            ],
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("1692632086370-0", [("rider", "Castilla"), ("speed", "30.2"), ("position", "1"), ("location_id", "1")]), ("1692632094485-0", [("rider", "Norem"), ("speed", "28.8"), ("position", "3"), ("location_id", "1")])]
            }
    
            add_france_fixed(&mut r).await;
            if let Ok(res) = r.xrange_count("race:france", "(1692632094485-0", "+", 2).await {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![
                                ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                (
                                    "location_id".to_string(),
                                    entry.get::<String>("location_id").expect("missing location_id"),
                                ),
                            ],
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("1692632102976-0", [("rider", "Prickett"), ("speed", "29.7"), ("position", "2"), ("location_id", "1")]), ("1692632147973-0", [("rider", "Castilla"), ("speed", "29.9"), ("position", "1"), ("location_id", "2")])]
            }
    
            add_france_fixed(&mut r).await;
            if let Ok(res) = r.xrange_count("race:france", "(1692632147973-0", "+", 2).await {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![
                                ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                (
                                    "location_id".to_string(),
                                    entry.get::<String>("location_id").expect("missing location_id"),
                                ),
                            ],
                        )
                    })
                    .collect();
                println!("{view:?}"); // >>> []
            }
    
            add_france_fixed(&mut r).await;
            if let Ok(res) = r.xrevrange_count("race:france", "+", "-", 1).await {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![
                                ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                (
                                    "location_id".to_string(),
                                    entry.get::<String>("location_id").expect("missing location_id"),
                                ),
                            ],
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("1692632147973-0", [("rider", "Castilla"), ("speed", "29.9"), ("position", "1"), ("location_id", "2")])]
            }
    
            add_france_fixed(&mut r).await;
            let opts = StreamReadOptions::default().count(2);
            if let Ok(res) = r.xread_options(&["race:france"], &["0"], &opts).await {
                let res: Option<StreamReadReply> = res;
                let view: Vec<_> = res
                    .expect("xread should return data")
                    .keys
                    .iter()
                    .map(|stream| {
                        (
                            stream.key.clone(),
                            stream
                                .ids
                                .iter()
                                .map(|entry| {
                                    (
                                        entry.id.clone(),
                                        vec![
                                            ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                            ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                            ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                            (
                                                "location_id".to_string(),
                                                entry.get::<String>("location_id").expect("missing location_id"),
                                            ),
                                        ],
                                    )
                                })
                                .collect::<Vec<_>>(),
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("race:france", [("1692632086370-0", [("rider", "Castilla"), ("speed", "30.2"), ("position", "1"), ("location_id", "1")]), ("1692632094485-0", [("rider", "Norem"), ("speed", "28.8"), ("position", "3"), ("location_id", "1")])])]
            }
    
            add_france_fixed(&mut r).await;
            if let Ok(res) = r.xgroup_create("race:france", "france_riders", "$").await {
                let res: () = res;
                let _ = res;
                println!("OK"); // >>> OK
            }
    
            delete_keys(&mut r, &["race:italy"]).await;
            if let Ok(res) = r.xgroup_create_mkstream("race:italy", "italy_riders", "$").await {
                let res: () = res;
                let _ = res;
                println!("OK"); // >>> OK
            }
    
            delete_keys(&mut r, &["race:italy"]).await;
            let _: () = r
                .xgroup_create_mkstream("race:italy", "italy_riders", "$")
                .await
                .expect("create italy group");
            let italy_1: Option<String> = r
                .xadd("race:italy", "1692632639151-0", &[("rider", "Castilla")])
                .await
                .expect("italy1");
            let italy_1 = italy_1.expect("missing stream id");
            println!("{italy_1}"); // >>> 1692632639151-0
            let italy_2: Option<String> = r
                .xadd("race:italy", "1692632647899-0", &[("rider", "Royce")])
                .await
                .expect("italy2");
            let italy_2 = italy_2.expect("missing stream id");
            println!("{italy_2}"); // >>> 1692632647899-0
            let italy_3: Option<String> = r
                .xadd("race:italy", "1692632662819-0", &[("rider", "Sam-Bodden")])
                .await
                .expect("italy3");
            let italy_3 = italy_3.expect("missing stream id");
            println!("{italy_3}"); // >>> 1692632662819-0
            let italy_4: Option<String> = r
                .xadd("race:italy", "1692632670501-0", &[("rider", "Prickett")])
                .await
                .expect("italy4");
            let italy_4 = italy_4.expect("missing stream id");
            println!("{italy_4}"); // >>> 1692632670501-0
            let italy_5: Option<String> = r
                .xadd("race:italy", "1692632678249-0", &[("rider", "Norem")])
                .await
                .expect("italy5");
            let italy_5 = italy_5.expect("missing stream id");
            println!("{italy_5}"); // >>> 1692632678249-0
            let opts = StreamReadOptions::default().group("italy_riders", "Alice").count(1);
            if let Ok(res) = r.xread_options(&["race:italy"], &[">"], &opts).await {
                let res: Option<StreamReadReply> = res;
                let view: Vec<_> = res
                    .expect("xgroup read should return data")
                    .keys
                    .iter()
                    .map(|stream| {
                        (
                            stream.key.clone(),
                            stream
                                .ids
                                .iter()
                                .map(|entry| {
                                    (
                                        entry.id.clone(),
                                        vec![(
                                            "rider".to_string(),
                                            entry.get::<String>("rider").expect("missing rider"),
                                        )],
                                    )
                                })
                                .collect::<Vec<_>>(),
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("race:italy", [("1692632639151-0", [("rider", "Castilla")])])]
            }
    
            seed_italy_alice_pending(&mut r).await;
            let opts = StreamReadOptions::default().group("italy_riders", "Alice");
            if let Ok(res) = r.xread_options(&["race:italy"], &["0"], &opts).await {
                let res: Option<StreamReadReply> = res;
                let view: Vec<_> = res
                    .expect("xgroup history")
                    .keys
                    .iter()
                    .map(|stream| {
                        (
                            stream.key.clone(),
                            stream
                                .ids
                                .iter()
                                .map(|entry| {
                                    (
                                        entry.id.clone(),
                                        vec![(
                                            "rider".to_string(),
                                            entry.get::<String>("rider").expect("missing rider"),
                                        )],
                                    )
                                })
                                .collect::<Vec<_>>(),
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("race:italy", [("1692632639151-0", [("rider", "Castilla")])])]
            }
    
            seed_italy_alice_pending(&mut r).await;
            if let Ok(res) = r.xack("race:italy", "italy_riders", &["1692632639151-0"]).await {
                let res: usize = res;
                println!("{res}"); // >>> 1
            }
            let opts = StreamReadOptions::default().group("italy_riders", "Alice");
            if let Ok(res) = r.xread_options(&["race:italy"], &["0"], &opts).await {
                let res: Option<StreamReadReply> = res;
                let view: Vec<_> = res
                    .expect("xgroup history")
                    .keys
                    .iter()
                    .map(|stream| {
                        (
                            stream.key.clone(),
                            stream
                                .ids
                                .iter()
                                .map(|entry| {
                                    (
                                        entry.id.clone(),
                                        vec![(
                                            "rider".to_string(),
                                            entry.get::<String>("rider").expect("missing rider"),
                                        )],
                                    )
                                })
                                .collect::<Vec<_>>(),
                        )
                    })
                    .collect();
                println!("{view:?}"); // >>> [("race:italy", [])]
            }
    
            seed_italy_after_ack(&mut r).await;
            let opts = StreamReadOptions::default().group("italy_riders", "Bob").count(2);
            if let Ok(res) = r.xread_options(&["race:italy"], &[">"], &opts).await {
                let res: Option<StreamReadReply> = res;
                let view: Vec<_> = res
                    .expect("bob should receive data")
                    .keys
                    .iter()
                    .map(|stream| {
                        (
                            stream.key.clone(),
                            stream
                                .ids
                                .iter()
                                .map(|entry| {
                                    (
                                        entry.id.clone(),
                                        vec![(
                                            "rider".to_string(),
                                            entry.get::<String>("rider").expect("missing rider"),
                                        )],
                                    )
                                })
                                .collect::<Vec<_>>(),
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("race:italy", [("1692632647899-0", [("rider", "Royce")]), ("1692632662819-0", [("rider", "Sam-Bodden")])])]
            }
    
            seed_italy_bob_pending(&mut r).await;
            if let Ok(res) = r.xpending("race:italy", "italy_riders").await {
                let res: StreamPendingReply = res;
                let view = match res {
                    StreamPendingReply::Empty => None,
                    StreamPendingReply::Data(data) => Some((
                        data.count,
                        data.start_id.clone(),
                        data.end_id.clone(),
                        data.consumers
                            .iter()
                            .map(|consumer| (consumer.name.clone(), consumer.pending))
                            .collect::<Vec<_>>(),
                    )),
                }
                .expect("pending summary");
                println!("{view:?}");
                // >>> (2, "1692632647899-0", "1692632662819-0", [("Bob", 2)])
            }
    
            seed_italy_bob_pending(&mut r).await;
            sleep(Duration::from_millis(5)).await;
            if let Ok(res) = r.xpending_count("race:italy", "italy_riders", "-", "+", 10).await {
                let res: StreamPendingCountReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            entry.consumer.clone(),
                            entry.last_delivered_ms,
                            entry.times_delivered,
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("1692632647899-0", "Bob", 5, 1), ("1692632662819-0", "Bob", 5, 1)]
            }
    
            seed_italy_bob_pending(&mut r).await;
            if let Ok(res) = r.xrange("race:italy", "1692632647899-0", "1692632647899-0").await {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![(
                                "rider".to_string(),
                                entry.get::<String>("rider").expect("missing rider"),
                            )],
                        )
                    })
                    .collect();
                println!("{view:?}"); // >>> [("1692632647899-0", [("rider", "Royce")])]
            }
    
            seed_italy_bob_pending(&mut r).await;
            sleep(Duration::from_millis(5)).await;
            if let Ok(res) = r
                .xclaim("race:italy", "italy_riders", "Alice", 1, &["1692632647899-0"])
                .await
            {
                let res: redis::streams::StreamClaimReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![(
                                "rider".to_string(),
                                entry.get::<String>("rider").expect("missing rider"),
                            )],
                        )
                    })
                    .collect();
                println!("{view:?}"); // >>> [("1692632647899-0", [("rider", "Royce")])]
            }
    
            seed_italy_bob_pending(&mut r).await;
            sleep(Duration::from_millis(5)).await;
            let opts = StreamAutoClaimOptions::default().count(1);
            if let Ok(res) = r
                .xautoclaim_options("race:italy", "italy_riders", "Alice", 1, "0-0", opts)
                .await
            {
                let res: redis::streams::StreamAutoClaimReply = res;
                let claimed: Vec<_> = res
                    .claimed
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![(
                                "rider".to_string(),
                                entry.get::<String>("rider").expect("missing rider"),
                            )],
                        )
                    })
                    .collect();
                println!("{:?}", (res.next_stream_id.clone(), &claimed));
                // >>> ("1692632662819-0", [("1692632647899-0", [("rider", "Royce")])])
            }
    
            seed_italy_bob_pending(&mut r).await;
            sleep(Duration::from_millis(5)).await;
            let first_opts = StreamAutoClaimOptions::default().count(1);
            let _: redis::streams::StreamAutoClaimReply = r
                .xautoclaim_options("race:italy", "italy_riders", "Alice", 1, "0-0", first_opts)
                .await
                .expect("first autoclaim");
            let next_opts = StreamAutoClaimOptions::default().count(1);
            if let Ok(res) = r
                .xautoclaim_options(
                    "race:italy",
                    "italy_riders",
                    "Lora",
                    1,
                    "(1692632647899-0",
                    next_opts,
                )
                .await
            {
                let res: redis::streams::StreamAutoClaimReply = res;
                let claimed: Vec<_> = res
                    .claimed
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![(
                                "rider".to_string(),
                                entry.get::<String>("rider").expect("missing rider"),
                            )],
                        )
                    })
                    .collect();
                println!("{:?}", (res.next_stream_id.clone(), &claimed));
                // >>> ("0-0", [("1692632662819-0", [("rider", "Sam-Bodden")])])
            }
    
            seed_italy_info_state(&mut r).await;
            if let Ok(res) = r.xinfo_stream("race:italy").await {
                let res: StreamInfoStreamReply = res;
                let view = (
                    res.length,
                    res.radix_tree_keys,
                    res.groups,
                    res.last_generated_id.clone(),
                    res.first_entry.id.clone(),
                    res.last_entry.id.clone(),
                );
                println!("{view:?}");
                // >>> (5, 1, 1, "1692632678249-0", "1692632639151-0", "1692632678249-0")
            }
    
            seed_italy_info_state(&mut r).await;
            if let Ok(res) = r.xinfo_groups("race:italy").await {
                let res: StreamInfoGroupsReply = res;
                let view: Vec<_> = res
                    .groups
                    .iter()
                    .map(|group| {
                        (
                            group.name.clone(),
                            group.consumers,
                            group.pending,
                            group.last_delivered_id.clone(),
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("italy_riders", 3, 2, "1692632662819-0")]
            }
    
            seed_italy_info_state(&mut r).await;
            if let Ok(res) = r.xinfo_consumers("race:italy", "italy_riders").await {
                let res: StreamInfoConsumersReply = res;
                let mut view: Vec<_> = res
                    .consumers
                    .iter()
                    .map(|consumer| (consumer.name.clone(), consumer.pending, consumer.idle))
                    .collect();
                view.sort_by(|a, b| a.0.cmp(&b.0));
                println!("{view:?}");
                // >>> [("Alice", 1, 5), ("Bob", 0, 5), ("Lora", 1, 5)]
            }
    
            delete_keys(&mut r, &["race:italy"]).await;
            let max1: Option<String> = r
                .xadd_maxlen("race:italy", StreamMaxlen::Equals(2), "1-0", &[("rider", "Jones")])
                .await
                .expect("maxlen add 1");
            let max1 = max1.expect("missing stream id");
            println!("{max1}"); // >>> 1-0
            let max2: Option<String> = r
                .xadd_maxlen("race:italy", StreamMaxlen::Equals(2), "2-0", &[("rider", "Wood")])
                .await
                .expect("maxlen add 2");
            let max2 = max2.expect("missing stream id");
            println!("{max2}"); // >>> 2-0
            let max3: Option<String> = r
                .xadd_maxlen("race:italy", StreamMaxlen::Equals(2), "3-0", &[("rider", "Henshaw")])
                .await
                .expect("maxlen add 3");
            let max3 = max3.expect("missing stream id");
            println!("{max3}"); // >>> 3-0
            if let Ok(res) = r.xlen("race:italy").await {
                let res: usize = res;
                println!("{res}"); // >>> 2
            }
            if let Ok(res) = r.xrange_all("race:italy").await {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![(
                                "rider".to_string(),
                                entry.get::<String>("rider").expect("missing rider"),
                            )],
                        )
                    })
                    .collect();
                println!("{view:?}");
                // >>> [("2-0", [("rider", "Wood")]), ("3-0", [("rider", "Henshaw")])]
            }
    
            delete_keys(&mut r, &["race:italy"]).await;
            let _: Option<String> = r.xadd("race:italy", "1-0", &[("rider", "Wood")]).await.expect("trim seed 1");
            let _: Option<String> = r.xadd("race:italy", "2-0", &[("rider", "Henshaw")]).await.expect("trim seed 2");
            if let Ok(res) = r.xtrim("race:italy", StreamMaxlen::Equals(10)).await {
                let res: usize = res;
                println!("{res}"); // >>> 0
            }
    
            seed_trim_stream(&mut r).await;
            if let Ok(res) = r
                .xtrim_options(
                    "mystream",
                    &StreamTrimOptions::maxlen(StreamTrimmingMode::Approx, 10),
                )
                .await
            {
                let res: usize = res;
                println!("{res}"); // >>> 0
            }
    
            delete_keys(&mut r, &["race:italy"]).await;
            let _: Option<String> = r.xadd("race:italy", "2-0", &[("rider", "Wood")]).await.expect("xdel seed 1");
            let _: Option<String> = r.xadd("race:italy", "3-0", &[("rider", "Henshaw")]).await.expect("xdel seed 2");
            if let Ok(res) = r.xrange_count("race:italy", "-", "+", 2).await {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![(
                                "rider".to_string(),
                                entry.get::<String>("rider").expect("missing rider"),
                            )],
                        )
                    })
                    .collect();
                println!("{view:?}"); // >>> [("2-0", [("rider", "Wood")]), ("3-0", [("rider", "Henshaw")])]
            }
            if let Ok(res) = r.xdel("race:italy", &["3-0"]).await {
                let res: usize = res;
                println!("{res}"); // >>> 1
            }
            if let Ok(res) = r.xrange_count("race:italy", "-", "+", 2).await {
                let res: StreamRangeReply = res;
                let view: Vec<_> = res
                    .ids
                    .iter()
                    .map(|entry| {
                        (
                            entry.id.clone(),
                            vec![(
                                "rider".to_string(),
                                entry.get::<String>("rider").expect("missing rider"),
                            )],
                        )
                    })
                    .collect();
                println!("{view:?}"); // >>> [("2-0", [("rider", "Wood")])]
            }
        }
    }
    

Performance

Adding an entry to a stream is O(1). Accessing any single entry is O(n), where n is the length of the ID. Since stream IDs are typically short and of a fixed length, this effectively reduces to a constant time lookup. For details on why, note that streams are implemented as radix trees.

Simply put, Redis streams provide highly efficient inserts and reads. See each command's time complexity for the details.

Streams basics

Streams are an append-only data structure. The fundamental write command, called XADD, appends a new entry to the specified stream.

Each stream entry consists of one or more field-value pairs, somewhat like a dictionary or a Redis hash:

Foundational: Add a single entry to a stream with multiple field-value pairs using XADD
> XADD race:france * rider Castilla speed 29.9 position 1 location_id 2
"1692632147973-0"
"""
Code samples for Stream doc pages:
    https://redis.io/docs/latest/develop/data-types/streams/
"""

import redis

r = redis.Redis(decode_responses=True)

res1 = r.xadd(
    "race:france",
    {"rider": "Castilla", "speed": 30.2, "position": 1, "location_id": 1},
)
print(res1)  # >>> 1692629576966-0

res2 = r.xadd(
    "race:france",
    {"rider": "Norem", "speed": 28.8, "position": 3, "location_id": 1},
)
print(res2)  # >>> 1692629594113-0

res3 = r.xadd(
    "race:france",
    {"rider": "Prickett", "speed": 29.7, "position": 2, "location_id": 1},
)
print(res3)  # >>> 1692629613374-0


res4 = r.xrange("race:france", "1691765278160-0", "+", 2)
print(
    res4
)  # >>> [
#   ('1692629576966-0',
#       {'rider': 'Castilla', 'speed': '30.2', 'position': '1', 'location_id': '1'}
#   ),
#   ('1692629594113-0',
#       {'rider': 'Norem', 'speed': '28.8', 'position': '3', 'location_id': '1'}
#   )
# ]

res5 = r.xread(streams={"race:france": 0}, count=100, block=300)
print(
    res5
)
# >>> [
#   ['race:france',
#       [('1692629576966-0',
#           {'rider': 'Castilla', 'speed': '30.2', 'position': '1', 'location_id': '1'}
#       ),
#       ('1692629594113-0',
#           {'rider': 'Norem', 'speed': '28.8', 'position': '3', 'location_id': '1'}
#       ),
#       ('1692629613374-0',
#           {'rider': 'Prickett', 'speed': '29.7', 'position': '2', 'location_id': '1'}
#       )]
# ]
# ]

res6 = r.xadd(
    "race:france",
    {"rider": "Castilla", "speed": 29.9, "position": 1, "location_id": 2},
)
print(res6)  # >>> 1692629676124-0

res7 = r.xlen("race:france")
print(res7)  # >>> 4


res8 = r.xadd("race:usa", {"racer": "Castilla"}, id="0-1")
print(res8)  # >>> 0-1

res9 = r.xadd("race:usa", {"racer": "Norem"}, id="0-2")
print(res9)  # >>> 0-2

try:
    res10 = r.xadd("race:usa", {"racer": "Prickett"}, id="0-1")
    print(res10)  # >>> 0-1
except redis.exceptions.ResponseError as e:
    print(e)  # >>> WRONGID

# Not yet implemented

res11 = r.xrange("race:france", "-", "+")
print(
    res11
)
# >>> [
#   ('1692629576966-0',
#       {'rider': 'Castilla', 'speed': '30.2', 'position': '1', 'location_id': '1'}
#   ),
#   ('1692629594113-0',
#       {'rider': 'Norem', 'speed': '28.8', 'position': '3', 'location_id': '1'}
#   ),
#   ('1692629613374-0',
#       {'rider': 'Prickett', 'speed': '29.7', 'position': '2', 'location_id': '1'}
#   ),
#   ('1692629676124-0',
#       {'rider': 'Castilla', 'speed': '29.9', 'position': '1', 'location_id': '2'}
#   )
# ]

res12 = r.xrange("race:france", 1692629576965, 1692629576967)
print(
    res12
)
# >>> [
#       ('1692629576966-0',
#           {'rider': 'Castilla', 'speed': '30.2', 'position': '1', 'location_id': '1'}
#       )
# ]

res13 = r.xrange("race:france", "-", "+", 2)
print(
    res13
)
# >>> [
#   ('1692629576966-0',
#       {'rider': 'Castilla', 'speed': '30.2', 'position': '1', 'location_id': '1'}
#   ),
#   ('1692629594113-0',
#       {'rider': 'Norem', 'speed': '28.8', 'position': '3', 'location_id': '1'}
#   )
# ]

res14 = r.xrange("race:france", "(1692629594113-0", "+", 2)
print(
    res14
)
# >>> [
#   ('1692629613374-0',
#       {'rider': 'Prickett', 'speed': '29.7', 'position': '2', 'location_id': '1'}
#   ),
#   ('1692629676124-0',
#       {'rider': 'Castilla', 'speed': '29.9', 'position': '1', 'location_id': '2'}
#   )
# ]

res15 = r.xrange("race:france", "(1692629676124-0", "+", 2)
print(res15)  # >>> []

res16 = r.xrevrange("race:france", "+", "-", 1)
print(
    res16
)
# >>> [
#       ('1692629676124-0',
#           {'rider': 'Castilla', 'speed': '29.9', 'position': '1', 'location_id': '2'}
#       )
# ]

res17 = r.xread(streams={"race:france": 0}, count=2)
print(
    res17
)
# >>> [
#       ['race:france', [
#       ('1692629576966-0',
#           {'rider': 'Castilla', 'speed': '30.2', 'position': '1', 'location_id': '1'}
#       ),
#       ('1692629594113-0',
#           {'rider': 'Norem', 'speed': '28.8', 'position': '3', 'location_id': '1'}
#       )
#       ]
#       ]
#   ]

res18 = r.xgroup_create("race:france", "france_riders", "$")
print(res18)  # >>> True

res19 = r.xgroup_create("race:italy", "italy_riders", "$", mkstream=True)
print(res19)  # >>> True

r.xadd("race:italy", {"rider": "Castilla"})
r.xadd("race:italy", {"rider": "Royce"})
r.xadd("race:italy", {"rider": "Sam-Bodden"})
r.xadd("race:italy", {"rider": "Prickett"})
r.xadd("race:italy", {"rider": "Norem"})

res20 = r.xreadgroup(
    streams={"race:italy": ">"},
    consumername="Alice",
    groupname="italy_riders",
    count=1,
)
print(res20)  # >>> [['race:italy', [('1692629925771-0', {'rider': 'Castilla'})]]]

res21 = r.xreadgroup(
    streams={"race:italy": 0},
    consumername="Alice",
    groupname="italy_riders",
    count=1,
)
print(res21)  # >>> [['race:italy', [('1692629925771-0', {'rider': 'Castilla'})]]]

res22 = r.xack("race:italy", "italy_riders", "1692629925771-0")
print(res22)  # >>> 1

res23 = r.xreadgroup(
    streams={"race:italy": 0},
    consumername="Alice",
    groupname="italy_riders",
    count=1,
)
print(res23)  # >>> [['race:italy', []]]

res24 = r.xreadgroup(
    streams={"race:italy": ">"},
    consumername="Bob",
    groupname="italy_riders",
    count=2,
)
print(
    res24
)
# >>> [
#       ['race:italy', [
#           ('1692629925789-0',
#               {'rider': 'Royce'}
#           ),
#           ('1692629925790-0',
#               {'rider': 'Sam-Bodden'}
#           )
#       ]
#       ]
# ]

res25 = r.xpending("race:italy", "italy_riders")
print(
    res25
)
# >>> {
#       'pending': 2, 'min': '1692629925789-0', 'max': '1692629925790-0',
#       'consumers': [{'name': 'Bob', 'pending': 2}]
# }

res26 = r.xpending_range("race:italy", "italy_riders", "-", "+", 10)
print(
    res26
)
# >>> [
#       {
#           'message_id': '1692629925789-0', 'consumer': 'Bob',
#           'time_since_delivered': 31084, 'times_delivered': 1
#       },
#       {
#           'message_id': '1692629925790-0', 'consumer': 'Bob',
#           'time_since_delivered': 31084, 'times_delivered': 1
#       }
# ]

res27 = r.xrange("race:italy", "1692629925789-0", "1692629925789-0")
print(res27)  # >>> [('1692629925789-0', {'rider': 'Royce'})]

res28 = r.xclaim("race:italy", "italy_riders", "Alice", 60000, ["1692629925789-0"])
print(res28)  # >>> [('1692629925789-0', {'rider': 'Royce'})]

res29 = r.xautoclaim("race:italy", "italy_riders", "Alice", 1, "0-0", 1)
print(res29)  # >>> ['1692629925790-0', [('1692629925789-0', {'rider': 'Royce'})]]

res30 = r.xautoclaim("race:italy", "italy_riders", "Alice", 1, "(1692629925789-0", 1)
print(res30)  # >>> ['0-0', [('1692629925790-0', {'rider': 'Sam-Bodden'})]]

res31 = r.xinfo_stream("race:italy")
print(
    res31
)
# >>> {
#       'length': 5, 'radix-tree-keys': 1, 'radix-tree-nodes': 2,
#       'last-generated-id': '1692629926436-0', 'groups': 1,
#       'first-entry': ('1692629925771-0', {'rider': 'Castilla'}),
#       'last-entry': ('1692629926436-0', {'rider': 'Norem'})
# }

res32 = r.xinfo_groups("race:italy")
print(
    res32
)
# >>> [
#       {
#           'name': 'italy_riders', 'consumers': 2, 'pending': 2,
#           'last-delivered-id': '1692629925790-0'
#       }
# ]

res33 = r.xinfo_consumers("race:italy", "italy_riders")
print(
    res33
)
# >>> [
#       {'name': 'Alice', 'pending': 2, 'idle': 199332},
#       {'name': 'Bob', 'pending': 0, 'idle': 489170}
# ]

r.xadd("race:italy", {"rider": "Jones"}, maxlen=2)
r.xadd("race:italy", {"rider": "Wood"}, maxlen=2)
r.xadd("race:italy", {"rider": "Henshaw"}, maxlen=2)

res34 = r.xlen("race:italy")
print(res34)  # >>> 8

res35 = r.xrange("race:italy", "-", "+")
print(
    res35
)
# >>> [
#       ('1692629925771-0', {'rider': 'Castilla'}),
#       ('1692629925789-0', {'rider': 'Royce'}),
#       ('1692629925790-0', {'rider': 'Sam-Bodden'}),
#       ('1692629925791-0', {'rider': 'Prickett'}),
#       ('1692629926436-0', {'rider': 'Norem'}),
#       ('1692630612602-0', {'rider': 'Jones'}),
#       ('1692630641947-0', {'rider': 'Wood'}),
#       ('1692630648281-0', {'rider': 'Henshaw'})
# ]

r.xadd("race:italy", {"rider": "Smith"}, maxlen=2, approximate=False)

res36 = r.xrange("race:italy", "-", "+")
print(
    res36
)
# >>> [
#       ('1692630648281-0', {'rider': 'Henshaw'}),
#       ('1692631018238-0', {'rider': 'Smith'})
# ]

res37 = r.xtrim("race:italy", maxlen=10, approximate=False)
print(res37)  # >>> 0

res38 = r.xtrim("race:italy", maxlen=10)
print(res38)  # >>> 0

res39 = r.xrange("race:italy", "-", "+")
print(
    res39
)
# >>> [
#       ('1692630648281-0', {'rider': 'Henshaw'}),
#       ('1692631018238-0', {'rider': 'Smith'})
# ]

res40 = r.xdel("race:italy", "1692631018238-0")
print(res40)  # >>> 1

res41 = r.xrange("race:italy", "-", "+")
print(res41)  # >>> [('1692630648281-0', {'rider': 'Henshaw'})]
import assert from 'assert';
import {
  createClient
} from 'redis';

const client = await createClient();
await client.connect();

const res1 = await client.xAdd(
  'race:france', '*', {
    'rider': 'Castilla',
    'speed': '30.2',
    'position': '1',
    'location_id': '1'
  }
);
console.log(res1); // >>> 1700073067968-0 N.B. actual values will differ from these examples

const res2 = await client.xAdd(
  'race:france', '*', {
    'rider': 'Norem',
    'speed': '28.8',
    'position': '3',
    'location_id': '1'
  },
);
console.log(res2); // >>> 1692629594113-0

const res3 = await client.xAdd(
  'race:france', '*', {
    'rider': 'Prickett',
    'speed': '29.7',
    'position': '2',
    'location_id': '1'
  },
);
console.log(res3); // >>> 1692629613374-0


const res4 = await client.xRange('race:france', '1691765278160-0', '+', {COUNT: 2});
console.log(res4); // >>> [{ id: '1692629576966-0', message: { rider: 'Castilla', speed: '30.2', position: '1', location_id: '1' } }, { id: '1692629594113-0', message: { rider: 'Norem', speed: '28.8', position: '3', location_id: '1' } }]

const res5 = await client.xRead({
  key: 'race:france',
  id: '0-0'
}, {
  COUNT: 100,
  BLOCK: 300
});
console.log(res5); // >>> [{ name: 'race:france', messages: [{ id: '1692629576966-0', message: { rider: 'Castilla', speed: '30.2', position: '1', location_id: '1' } }, { id: '1692629594113-0', message: { rider: 'Norem', speed: '28.8', position: '3', location_id: '1' } }, { id: '1692629613374-0', message: { rider: 'Prickett', speed: '29.7', position: '2', location_id: '1' } }] }]

const res6 = await client.xAdd(
  'race:france', '*', {
    'rider': 'Castilla',
    'speed': '29.9',
    'position': '1',
    'location_id': '2'
  }
);
console.log(res6); // >>> 1692629676124-0

const res7 = await client.xLen('race:france');
console.log(res7); // >>> 4


const res8 = await client.xAdd('race:usa', '0-1', {
  'racer': 'Castilla'
});
console.log(res8); // >>> 0-1

const res9 = await client.xAdd('race:usa', '0-2', {
  'racer': 'Norem'
});
console.log(res9); // >>> 0-2

try {
  const res10 = await client.xAdd('race:usa', '0-1', {
    'racer': 'Prickett'
  });
  console.log(res10); // >>> 0-1
} catch (error) {
  console.error(error); // >>> [SimpleError: ERR The ID specified in XADD is equal or smaller than the target stream top item]
}

const res11a = await client.xAdd('race:usa', '0-*', { racer: 'Norem' });
console.log(res11a); // >>> 0-3

const res11 = await client.xRange('race:france', '-', '+');
console.log(res11); // >>> [{ id: '1692629576966-0', message: { rider: 'Castilla', speed: '30.2', position: '1', location_id: '1' } }, { id: '1692629594113-0', message: { rider: 'Norem', speed: '28.8', position: '3', location_id: '1' } }, { id: '1692629613374-0', message: { rider: 'Prickett', speed: '29.7', position: '2', location_id: '1' } }, { id: '1692629676124-0', message: { rider: 'Castilla', speed: '29.9', position: '1', location_id: '2' } }]

const res12 = await client.xRange('race:france', '1692629576965', '1692629576967');
console.log(res12); // >>> [{ id: '1692629576966-0', message: { rider: 'Castilla', speed: '30.2', position: '1', location_id: '1' } }]

const res13 = await client.xRange('race:france', '-', '+', {COUNT: 2});
console.log(res13); // >>> [{ id: '1692629576966-0', message: { rider: 'Castilla', speed: '30.2', position: '1', location_id: '1' } }, { id: '1692629594113-0', message: { rider: 'Norem', speed: '28.8', position: '3', location_id: '1' } }]

const res14 = await client.xRange('race:france', '(1692629594113-0', '+', {COUNT: 2});
console.log(res14); // >>> [{ id: '1692629613374-0', message: { rider: 'Prickett', speed: '29.7', position: '2', location_id: '1' } }, { id: '1692629676124-0', message: { rider: 'Castilla', speed: '29.9', position: '1', location_id: '2' } }]

const res15 = await client.xRange('race:france', '(1692629676124-0', '+', {COUNT: 2});
console.log(res15); // >>> []

const res16 = await client.xRevRange('race:france', '+', '-', {COUNT: 1});
console.log(
  res16
); // >>> [{ id: '1692629676124-0', message: { rider: 'Castilla', speed: '29.9', position: '1', location_id: '2' } }]

const res17 = await client.xRead({
  key: 'race:france',
  id: '0-0'
}, {
  COUNT: 2
});
console.log(res17); // >>> [{ name: 'race:france', messages: [{ id: '1692629576966-0', message: { rider: 'Castilla', speed: '30.2', position: '1', location_id: '1' } }, { id: '1692629594113-0', message: { rider: 'Norem', speed: '28.8', position: '3', location_id: '1' } }] }]

const res18 = await client.xGroupCreate('race:france', 'france_riders', '$');
console.log(res18); // >>> OK

const res19 = await client.xGroupCreate('race:italy', 'italy_riders', '$', {
  MKSTREAM: true
});
console.log(res19); // >>> OK

await client.xAdd('race:italy', '*', {
  'rider': 'Castilla'
});
await client.xAdd('race:italy', '*', {
  'rider': 'Royce'
});
await client.xAdd('race:italy', '*', {
  'rider': 'Sam-Bodden'
});
await client.xAdd('race:italy', '*', {
  'rider': 'Prickett'
});
await client.xAdd('race:italy', '*', {
  'rider': 'Norem'
});

const res20 = await client.xReadGroup(
  'italy_riders',
  'Alice', {
    key: 'race:italy',
    id: '>'
  }, {
    COUNT: 1
  }
);
console.log(res20); // >>> [{ name: 'race:italy', messages: [{ id: '1692629925771-0', message: { rider: 'Castilla' } }] }]

const res21 = await client.xReadGroup(
  'italy_riders',
  'Alice', {
    key: 'race:italy',
    id: '0'
  }, {
    COUNT: 1
  }
);
console.log(res21); // >>> [{ name: 'race:italy', messages: [{ id: '1692629925771-0', message: { rider: 'Castilla' } }] }]

const res22 = await client.xAck('race:italy', 'italy_riders', '1692629925771-0')
console.log(res22); // >>> 1

const res23 = await client.xReadGroup(
  'italy_riders',
  'Alice', {
    key: 'race:italy',
    id: '0'
  }, {
    COUNT: 1
  }
);
console.log(res23); // >>> [{ name: 'race:italy', messages: [] }]

const res24 = await client.xReadGroup(
  'italy_riders',
  'Bob', {
    key: 'race:italy',
    id: '>'
  }, {
    COUNT: 2
  }
);
console.log(res24); // >>> [{ name: 'race:italy', messages: [{ id: '1692629925789-0', message: { rider: 'Royce' } }, { id: '1692629925790-0', message: { rider: 'Sam-Bodden' } }] }]

const res25 = await client.xPending('race:italy', 'italy_riders');
console.log(res25); // >>> {'pending': 2, 'firstId': '1692629925789-0', 'lastId': '1692629925790-0', 'consumers': [{'name': 'Bob', 'deliveriesCounter': 2}]}

const res26 = await client.xPendingRange('race:italy', 'italy_riders', '-', '+', 10);
console.log(res26); // >>> [{'id': '1692629925789-0', 'consumer': 'Bob', 'millisecondsSinceLastDelivery': 31084, 'deliveriesCounter:': 1}, {'id': '1692629925790-0', 'consumer': 'Bob', 'millisecondsSinceLastDelivery': 31084, 'deliveriesCounter': 1}]

const res27 = await client.xRange('race:italy', '1692629925789-0', '1692629925789-0');
console.log(res27); // >>> [{ id: '1692629925789-0', message: { rider: 'Royce' } }]

const res28 = await client.xClaim(
  'race:italy', 'italy_riders', 'Alice', 60000, ['1692629925789-0']
);
console.log(res28); // >>> [{ id: '1692629925789-0', message: { rider: 'Royce' } }]

const res29 = await client.xAutoClaim('race:italy', 'italy_riders', 'Alice', 1, '0-0', {
  COUNT: 1
});
console.log(res29); // >>> { nextId: '1692629925790-0', messages: [{ id: '1692629925789-0', message: { rider: 'Royce' } }], deletedMessages: [] }

const res30 = await client.xAutoClaim(
  'race:italy', 'italy_riders', 'Alice', 1, '(1692629925789-0',
  {
    COUNT: 1
  }
);
console.log(res30); // >>> { nextId: '0-0', messages: [{ id: '1692629925790-0', message: { rider: 'Sam-Bodden' } }], deletedMessages: [] }

const res31 = await client.xInfoStream('race:italy');
console.log(res31); // >>> { length: 5, 'radix-tree-keys': 1, 'radix-tree-nodes': 2, 'last-generated-id': '1692629926436-0', 'max-deleted-entry-id': '0-0', 'entries-added': 5, 'recorded-first-entry-id': '1692629925771-0', groups: 1, 'first-entry': { id: '1692629925771-0', message: { rider: 'Castilla' } }, 'last-entry': { id: '1692629926436-0', message: { rider: 'Norem' } } }

const res32 = await client.xInfoGroups('race:italy');
console.log(res32); // >>> [{ name: 'italy_riders', consumers: 2, pending: 3, 'last-delivered-id': '1692629925790-0', 'entries-read': 3, lag: 2 }]

const res33 = await client.xInfoConsumers('race:italy', 'italy_riders');
console.log(res33); // >>> [{ name: 'Alice', pending: 3, idle: 170582, inactive: 170582 }, { name: 'Bob', pending: 0, idle: 489404, inactive: 489404 }]

await client.xAdd('race:italy', '*', {
  'rider': 'Jones'
}, {
  TRIM: {
    strategy: 'MAXLEN',
    strategyModifier: '~',
    threshold: 2
  }
});
await client.xAdd('race:italy', '*', {
  'rider': 'Wood'
}, {
  TRIM: {
    strategy: 'MAXLEN',
    strategyModifier: '~',
    threshold: 2
  }
});
await client.xAdd('race:italy', '*', {
  'rider': 'Henshaw'
}, {
  TRIM: {
    strategy: 'MAXLEN',
    strategyModifier: '~',
    threshold: 2
  }
});

const res34 = await client.xLen('race:italy');
console.log(res34); // >>> 8

const res35 = await client.xRange('race:italy', '-', '+');
console.log(res35); // >>> [{ id: '1692629925771-0', message: { rider: 'Castilla' } }, { id: '1692629925789-0', message: { rider: 'Royce' } }, { id: '1692629925790-0', message: { rider: 'Sam-Bodden' } }, { id: '1692629925791-0', message: { rider: 'Prickett' } }, { id: '1692629926436-0', message: { rider: 'Norem' } }, { id: '1692630612602-0', message: { rider: 'Jones' } }, { id: '1692630641947-0', message: { rider: 'Wood' } }, { id: '1692630648281-0', message: { rider: 'Henshaw' } }]

await client.xAdd('race:italy', '*', {
  'rider': 'Smith'
}, {
  TRIM: {
    strategy: 'MAXLEN',
    strategyModifier: '=',
    threshold: 2
  }
});

const res36 = await client.xRange('race:italy', '-', '+');
console.log(res36); // >>> [{ id: '1692630648281-0', message: { rider: 'Henshaw' } }, { id: '1692631018238-0', message: { rider: 'Smith' } }]

const res37 = await client.xTrim('race:italy', 'MAXLEN', 10, {
  strategyModifier: '=',
});
console.log(res37); // >>> 0

const res38 = await client.xTrim('race:italy', "MAXLEN", 10);
console.log(res38); // >>> 0

const res39 = await client.xRange('race:italy', '-', '+');
console.log(res39); // >>> [{ id: '1692630648281-0', message: { rider: 'Henshaw' } }, { id: '1692631018238-0', message: { rider: 'Smith' } }]

const res40 = await client.xDel('race:italy', '1692631018238-0');
console.log(res40); // >>> 1

const res41 = await client.xRange('race:italy', '-', '+');
console.log(res41); // >>> [{ id: '1692630648281-0', message: { rider: 'Henshaw' } }]

package io.redis.examples;

import redis.clients.jedis.StreamEntryID;
import redis.clients.jedis.RedisClient;


public class StreamsExample {

  public void run() {
    RedisClient jedis = RedisClient.create("redis://localhost:6379");


    StreamEntryID res1 = jedis.xadd("race:france",new HashMap<String,String>(){{put("rider","Castilla");put("speed","30.2");put("position","1");put("location_id","1");}} , XAddParams.xAddParams());

    System.out.println(res1); // >>> 1701760582225-0

    StreamEntryID res2 = jedis.xadd("race:france",new HashMap<String,String>(){{put("rider","Norem");put("speed","28.8");put("position","3");put("location_id","1");}} , XAddParams.xAddParams());

    System.out.println(res2); // >>> 1701760582225-1

    StreamEntryID res3 = jedis.xadd("race:france",new HashMap<String,String>(){{put("rider","Prickett");put("speed","29.7");put("position","2");put("location_id","1");}} , XAddParams.xAddParams());

    System.out.println(res3); // >>> 1701760582226-0


    List<StreamEntry> res4 = jedis.xrange("race:france","1701760582225-0","+",2);

    System.out.println(res4); // >>> [1701760841292-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701760841292-1 {rider=Norem, speed=28.8, location_id=1, position=3}]

    List<Map.Entry<String, List<StreamEntry>>> res5= jedis.xread(XReadParams.xReadParams().block(300).count(100),new HashMap<String,StreamEntryID>(){{put("race:france",new StreamEntryID());}});
    System.out.println(
      res5
    ); // >>> [race:france=[1701761996660-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701761996661-0 {rider=Norem, speed=28.8, location_id=1, position=3}, 1701761996661-1 {rider=Prickett, speed=29.7, location_id=1, position=2}]]

    StreamEntryID res6 = jedis.xadd("race:france",new HashMap<String,String>(){{put("rider","Castilla");put("speed","29.9");put("position","2");put("location_id","1");}} , XAddParams.xAddParams());
    System.out.println(res6); // >>> 1701762285679-0

    long res7 = jedis.xlen("race:france");
    System.out.println(res7); // >>> 4

    StreamEntryID res8 = jedis.xadd("race:usa", new HashMap<String,String>(){{put("racer","Castilla");}},XAddParams.xAddParams().id("0-1"));
    System.out.println(res8); // >>> 0-1

    StreamEntryID res9 = jedis.xadd("race:usa", new HashMap<String,String>(){{put("racer","Norem");}},XAddParams.xAddParams().id("0-2"));
    System.out.println(res9); // >>> 0-2

    try {
      StreamEntryID res10 = jedis.xadd("race:usa", new HashMap<String,String>(){{put("racer","Prickett");}},XAddParams.xAddParams().id("0-1"));
      System.out.println(res10); // >>> 0-1
    }
    catch (JedisDataException e){
      System.out.println(e); // >>> ERR The ID specified in XADD is equal or smaller than the target stream top item
    }

    StreamEntryID res11 = jedis.xadd("race:usa", new HashMap<String,String>(){{put("racer","Norem");}},XAddParams.xAddParams().id("0-*"));
    System.out.println(res11);

    List<StreamEntry> res12 = jedis.xrange("race:france","-","+");
    System.out.println(
      res12
    ); // >>> [1701764734160-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701764734160-1 {rider=Norem, speed=28.8, location_id=1, position=3}, 1701764734161-0 {rider=Prickett, speed=29.7, location_id=1, position=2}, 1701764734162-0 {rider=Castilla, speed=29.9, location_id=1, position=2}]

    List<StreamEntry> res13 = jedis.xrange("race:france",String.valueOf(System.currentTimeMillis()-1000),String.valueOf(System.currentTimeMillis()+1000));
    System.out.println(
      res13
    ); // >>> [1701764734160-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701764734160-1 {rider=Norem, speed=28.8, location_id=1, position=3}, 1701764734161-0 {rider=Prickett, speed=29.7, location_id=1, position=2}, 1701764734162-0 {rider=Castilla, speed=29.9, location_id=1, position=2}]

    List<StreamEntry> res14 = jedis.xrange("race:france","-","+",2);
    System.out.println(res14); // >>> [1701764887638-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701764887638-1 {rider=Norem, speed=28.8, location_id=1, position=3}]

    List<StreamEntry> res15 = jedis.xrange("race:france",String.valueOf(System.currentTimeMillis()-1000)+"-0","+",2);
    System.out.println(res15); // >>> [1701764887638-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701764887638-1 {rider=Norem, speed=28.8, location_id=1, position=3}]

    List<StreamEntry> res16 = jedis.xrange("race:france",String.valueOf(System.currentTimeMillis()+1000)+"-0","+",2);
    System.out.println(res16); // >>> []

    List<StreamEntry> res17 = jedis.xrevrange("race:france","+","-",1);
    System.out.println(res17); // >>> [1701765218592-0 {rider=Castilla, speed=29.9, location_id=1, position=2}]

    List<Map.Entry<String, List<StreamEntry>>> res18= jedis.xread(XReadParams.xReadParams().count(2),new HashMap<String,StreamEntryID>(){{put("race:france",new StreamEntryID());}});
    System.out.println(
      res18
    ); // >>> [race:france=[1701765384638-0 {rider=Castilla, speed=30.2, location_id=1, position=1}, 1701765384638-1 {rider=Norem, speed=28.8, location_id=1, position=3}]]

    String res19 = jedis.xgroupCreate("race:france","france_riders",StreamEntryID.LAST_ENTRY,false);
    System.out.println(res19); // >>> OK

    String res20 = jedis.xgroupCreate("race:italy","italy_riders",StreamEntryID.LAST_ENTRY,true);
    System.out.println(res20); // >>> OK

    StreamEntryID id1 = jedis.xadd("race:italy", new HashMap<String,String>(){{put("rider","Castilaa");}},XAddParams.xAddParams());
    StreamEntryID id2 = jedis.xadd("race:italy", new HashMap<String,String>(){{put("rider","Royce");}},XAddParams.xAddParams());
    StreamEntryID id3 = jedis.xadd("race:italy", new HashMap<String,String>(){{put("rider","Sam-Bodden");}},XAddParams.xAddParams());
    StreamEntryID id4 = jedis.xadd("race:italy", new HashMap<String,String>(){{put("rider","Prickett");}},XAddParams.xAddParams());
    StreamEntryID id5 = jedis.xadd("race:italy", new HashMap<String,String>(){{put("rider","Norem");}},XAddParams.xAddParams());

    List<Map.Entry<String, List<StreamEntry>>> res21 = jedis.xreadGroup("italy_riders","Alice", XReadGroupParams.xReadGroupParams().count(1),new HashMap<String,StreamEntryID>(){{put("race:italy",StreamEntryID.UNRECEIVED_ENTRY);}});
    System.out.println(res21); // >>> [race:italy=[1701766299006-0 {rider=Castilaa}]]

    List<Map.Entry<String, List<StreamEntry>>> res22 = jedis.xreadGroup("italy_riders","Alice", XReadGroupParams.xReadGroupParams().count(1),new HashMap<String,StreamEntryID>(){{put("race:italy",new StreamEntryID());}});
    System.out.println(res22); // >>> [race:italy=[1701766299006-0 {rider=Castilaa}]]

    long res23 = jedis.xack("race:italy","italy_riders",id1);
    System.out.println(res23); // >>> 1

    List<Map.Entry<String, List<StreamEntry>>> res24 = jedis.xreadGroup("italy_riders","Alice", XReadGroupParams.xReadGroupParams().count(1),new HashMap<String,StreamEntryID>(){{put("race:italy",new StreamEntryID());}});
    System.out.println(res24); // >>> [race:italy=[]]

    List<Map.Entry<String, List<StreamEntry>>> res25 = jedis.xreadGroup("italy_riders","Bob", XReadGroupParams.xReadGroupParams().count(2),new HashMap<String,StreamEntryID>(){{put("race:italy",StreamEntryID.UNRECEIVED_ENTRY);}});
    System.out.println(res25); // >>> [race:italy=[1701767632261-1 {rider=Royce}, 1701767632262-0 {rider=Sam-Bodden}]]

    StreamPendingSummary res26 = jedis.xpending("race:italy","italy_riders");
    System.out.println(res26.getConsumerMessageCount()); // >>> {Bob=2}

    List<StreamPendingEntry> res27 = jedis.xpending("race:italy","italy_riders",XPendingParams.xPendingParams().start(StreamEntryID.MINIMUM_ID).end(StreamEntryID.MAXIMUM_ID).count(10));
    System.out.println(res27); // >>> [1701768567412-1 Bob idle:0 times:1, 1701768567412-2 Bob idle:0 times:1]

    List<StreamEntry> res28 = jedis.xrange("race:italy",id2.toString(),id2.toString());
    System.out.println(res28); // >>> [1701768744819-1 {rider=Royce}]

    List<StreamEntry> res29 = jedis.xclaim("race:italy","italy_riders","Alice", 0L, XClaimParams.xClaimParams().time(60000),id2);
    System.out.println(res29); // >>> [1701769004195-1 {rider=Royce}]

    Map.Entry<StreamEntryID, List<StreamEntry>> res30 = jedis.xautoclaim("race:italy","italy_riders","Alice",1L,new StreamEntryID("0-0"),XAutoClaimParams.xAutoClaimParams().count(1));
    System.out.println(res30); // >>> [1701769266831-2=[1701769266831-1 {rider=Royce}]

    Map.Entry<StreamEntryID, List<StreamEntry>> res31 = jedis.xautoclaim("race:italy","italy_riders","Alice",1L,new StreamEntryID(id2.toString()),XAutoClaimParams.xAutoClaimParams().count(1));
    System.out.println(res31); // >>> [0-0=[1701769605847-2 {rider=Sam-Bodden}]

    StreamInfo res32 = jedis.xinfoStream("race:italy");
    System.out.println(
      res32.getStreamInfo()
    ); // >>> {radix-tree-keys=1, radix-tree-nodes=2, entries-added=5, length=5, groups=1, max-deleted-entry-id=0-0, first-entry=1701769637612-0 {rider=Castilaa}, last-generated-id=1701769637612-4, last-entry=1701769637612-4 {rider=Norem}, recorded-first-entry-id=1701769637612-0}

    List<StreamGroupInfo> res33 = jedis.xinfoGroups("race:italy");
    for (StreamGroupInfo a : res33){
      System.out.println(
        a.getGroupInfo()
      ); // >>> {last-delivered-id=1701770253659-0, lag=2, pending=2, name=italy_riders, consumers=2, entries-read=3}
    }

    List<StreamConsumersInfo> res34 = jedis.xinfoConsumers("race:italy","italy_riders");
    for (StreamConsumerInfo a : res34){
      System.out.println(
        a.getConsumerInfo()
      ); // {inactive=1, idle=1, pending=1, name=Alice} , {inactive=3, idle=3, pending=1, name=Bob}
    }

    jedis.xadd("race:italy", new HashMap<String,String>(){{put("rider","Jones");}},XAddParams.xAddParams().maxLen(10));
    jedis.xadd("race:italy", new HashMap<String,String>(){{put("rider","Wood");}},XAddParams.xAddParams().maxLen(10));
    jedis.xadd("race:italy", new HashMap<String,String>(){{put("rider","Henshaw");}},XAddParams.xAddParams().maxLen(10));
    long res35 = jedis.xlen("race:italy");
    System.out.println(res35); // >>> 8

    List<StreamEntry> res36 = jedis.xrange("race:italy","-","+");
    System.out.println(res36); // >>> [1701771219852-0 {rider=Castilaa}, 1701771219852-1 {rider=Royce}, 1701771219853-0 {rider=Sam-Bodden}, 1701771219853-1 {rider=Prickett}, 1701771219853-2 {rider=Norem}, 1701771219858-0 {rider=Jones}, 1701771219858-1 {rider=Wood}, 1701771219859-0 {rider=Henshaw}]

    StreamEntryID id6 = jedis.xadd("race:italy", new HashMap<String,String>(){{put("rider","Smith");}},XAddParams.xAddParams().maxLen(2));

    List<StreamEntry> res37 = jedis.xrange("race:italy","-","+");
    System.out.println(res37); // >>> [1701771067332-1 {rider=Henshaw}, 1701771067332-2 {rider=Smith}]

    long res38 = jedis.xtrim("race:italy",XTrimParams.xTrimParams().maxLen(10).exactTrimming());
    System.out.println(res38); /// >>> 0

    long res39 = jedis.xtrim("race:italy",XTrimParams.xTrimParams().maxLen(10));
    System.out.println(res39); /// >>> 0

    List<StreamEntry> res40 = jedis.xrange("race:italy","-","+");
    System.out.println(res40); // >>> [1701771356428-2 {rider=Henshaw}, 1701771356429-0 {rider=Smith}]

    long res41 = jedis.xdel("race:italy",id6);
    System.out.println(res41); // >>> 1

    List<StreamEntry> res42 = jedis.xrange("race:italy","-","+");
    System.out.println(res42); // >>> [1701771517639-1 {rider=Henshaw}]

    jedis.close();
  }

}
package example_commands_test

import (
	"context"
	"fmt"

	"github.com/redis/go-redis/v9"
)



func ExampleClient_xadd() {
	ctx := context.Background()

	rdb := redis.NewClient(&redis.Options{
		Addr:     "localhost:6379",
		Password: "", // no password docs
		DB:       0,  // use default DB
	})


	res1, err := rdb.XAdd(ctx, &redis.XAddArgs{
		Stream: "race:france",
		Values: map[string]interface{}{
			"rider":       "Castilla",
			"speed":       30.2,
			"position":    1,
			"location_id": 1,
		},
	}).Result()

	if err != nil {
		panic(err)
	}

	// fmt.Println(res1) // >>> 1692632086370-0

	res2, err := rdb.XAdd(ctx, &redis.XAddArgs{
		Stream: "race:france",
		Values: map[string]interface{}{
			"rider":       "Norem",
			"speed":       28.8,
			"position":    3,
			"location_id": 1,
		},
	}).Result()

	if err != nil {
		panic(err)
	}

	// fmt.PrintLn(res2) // >>> 1692632094485-0

	res3, err := rdb.XAdd(ctx, &redis.XAddArgs{
		Stream: "race:france",
		Values: map[string]interface{}{
			"rider":       "Prickett",
			"speed":       29.7,
			"position":    2,
			"location_id": 1,
		},
	}).Result()

	if err != nil {
		panic(err)
	}

	// fmt.Println(res3) // >>> 1692632102976-0


	xlen, err := rdb.XLen(ctx, "race:france").Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(xlen) // >>> 3

}

func ExampleClient_racefrance1() {
	ctx := context.Background()

	rdb := redis.NewClient(&redis.Options{
		Addr:     "localhost:6379",
		Password: "", // no password docs
		DB:       0,  // use default DB
	})


	_, err := rdb.XAdd(ctx, &redis.XAddArgs{
		Stream: "race:france",
		Values: map[string]interface{}{
			"rider":       "Castilla",
			"speed":       30.2,
			"position":    1,
			"location_id": 1,
		},
		ID: "1692632086370-0",
	}).Result()

	if err != nil {
		panic(err)
	}

	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
		Stream: "race:france",
		Values: map[string]interface{}{
			"rider":       "Norem",
			"speed":       28.8,
			"position":    3,
			"location_id": 1,
		},
		ID: "1692632094485-0",
	}).Result()

	if err != nil {
		panic(err)
	}

	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
		Stream: "race:france",
		Values: map[string]interface{}{
			"rider":       "Prickett",
			"speed":       29.7,
			"position":    2,
			"location_id": 1,
		},
		ID: "1692632102976-0",
	}).Result()

	if err != nil {
		panic(err)
	}

	res4, err := rdb.XRangeN(ctx, "race:france", "1691765278160-0", "+", 2).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res4)
	// >>> [{1692632086370-0 map[location_id:1 position:1 rider:Castilla...

	res5, err := rdb.XRead(ctx, &redis.XReadArgs{
		Streams: []string{"race:france", "0"},
		Count:   100,
		Block:   300,
	}).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res5)
	// >>> // [{race:france [{1692632086370-0 map[location_id:1 position:1...

	res6, err := rdb.XAdd(ctx, &redis.XAddArgs{
		Stream: "race:france",
		Values: map[string]interface{}{
			"rider":       "Castilla",
			"speed":       29.9,
			"position":    1,
			"location_id": 2,
		},
	}).Result()

	if err != nil {
		panic(err)
	}

	//fmt.Println(res6) // >>> 1692632147973-0

	res7, err := rdb.XLen(ctx, "race:france").Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res7) // >>> 4


}

func ExampleClient_raceusa() {
	ctx := context.Background()

	rdb := redis.NewClient(&redis.Options{
		Addr:     "localhost:6379",
		Password: "", // no password docs
		DB:       0,  // use default DB
	})


	res8, err := rdb.XAdd(ctx, &redis.XAddArgs{
		Stream: "race:usa",
		Values: map[string]interface{}{
			"racer": "Castilla",
		},
		ID: "0-1",
	}).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res8) // >>> 0-1

	res9, err := rdb.XAdd(ctx, &redis.XAddArgs{
		Stream: "race:usa",
		Values: map[string]interface{}{
			"racer": "Norem",
		},
		ID: "0-2",
	}).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res9) // >>> 0-2

	res10, err := rdb.XAdd(ctx, &redis.XAddArgs{
		Values: map[string]interface{}{
			"racer": "Prickett",
		},
		ID: "0-1",
	}).Result()

	if err != nil {
		// fmt.Println(err)
		// >>> ERR The ID specified in XADD is equal or smaller than the target stream top item
	}

	res11, err := rdb.XAdd(ctx, &redis.XAddArgs{
		Stream: "race:usa",
		Values: map[string]interface{}{
			"racer": "Prickett",
		},
		ID: "0-*",
	}).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res11) // >>> 0-3


}

func ExampleClient_racefrance2() {
	ctx := context.Background()

	rdb := redis.NewClient(&redis.Options{
		Addr:     "localhost:6379",
		Password: "", // no password docs
		DB:       0,  // use default DB
	})


	_, err := rdb.XAdd(ctx, &redis.XAddArgs{
		Stream: "race:france",
		Values: map[string]interface{}{
			"rider":       "Castilla",
			"speed":       30.2,
			"position":    1,
			"location_id": 1,
		},
		ID: "1692632086370-0",
	}).Result()

	if err != nil {
		panic(err)
	}

	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
		Stream: "race:france",
		Values: map[string]interface{}{
			"rider":       "Norem",
			"speed":       28.8,
			"position":    3,
			"location_id": 1,
		},
		ID: "1692632094485-0",
	}).Result()

	if err != nil {
		panic(err)
	}

	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
		Stream: "race:france",
		Values: map[string]interface{}{
			"rider":       "Prickett",
			"speed":       29.7,
			"position":    2,
			"location_id": 1,
		},
		ID: "1692632102976-0",
	}).Result()

	if err != nil {
		panic(err)
	}

	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
		Stream: "race:france",
		Values: map[string]interface{}{
			"rider":       "Castilla",
			"speed":       29.9,
			"position":    1,
			"location_id": 2,
		},
		ID: "1692632147973-0",
	}).Result()

	if err != nil {
		panic(err)
	}
	res12, err := rdb.XRange(ctx, "race:france", "-", "+").Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res12)
	// >>> [{1692632086370-0 map[location_id:1 position:1 rider:Castilla...

	res13, err := rdb.XRange(ctx, "race:france",
		"1692632086369", "1692632086371",
	).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res13)
	// >>> [{1692632086370-0 map[location_id:1 position:1 rider:Castilla speed:30.2] 0 0}]

	res14, err := rdb.XRangeN(ctx, "race:france", "-", "+", 2).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res14)
	// >>> [{1692632086370-0 map[location_id:1 position:1 rider:Castilla speed:30.2] 0 0} {1692632094485-0 map[location_id:1 position:3 rider:Norem speed:28.8] 0 0}]

	res15, err := rdb.XRangeN(ctx, "race:france",
		"(1692632094485-0", "+", 2,
	).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res15)
	// >>> [{1692632102976-0 map[location_id:1 position:2 rider:Prickett speed:29.7] 0 0} {1692632147973-0 map[location_id:2 position:1 rider:Castilla speed:29.9] 0 0}]

	res16, err := rdb.XRangeN(ctx, "race:france",
		"(1692632147973-0", "+", 2,
	).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res16)
	// >>> []

	res17, err := rdb.XRevRangeN(ctx, "race:france", "+", "-", 1).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res17)
	// >>> [{1692632147973-0 map[location_id:2 position:1 rider:Castilla speed:29.9] 0 0}]

	res18, err := rdb.XRead(ctx, &redis.XReadArgs{
		Streams: []string{"race:france", "0"},
		Count:   2,
	}).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res18)
	// >>> [{race:france [{1692632086370-0 map[location_id:1 position:1 rider:Castilla speed:30.2] 0 0} {1692632094485-0 map[location_id:1 position:3 rider:Norem speed:28.8] 0 0}]}]

}

func ExampleClient_xgroupcreate() {
	ctx := context.Background()

	rdb := redis.NewClient(&redis.Options{
		Addr:     "localhost:6379",
		Password: "", // no password docs
		DB:       0,  // use default DB
	})


	_, err := rdb.XAdd(ctx, &redis.XAddArgs{
		Stream: "race:france",
		Values: map[string]interface{}{
			"rider":       "Castilla",
			"speed":       30.2,
			"position":    1,
			"location_id": 1,
		},
		ID: "1692632086370-0",
	}).Result()

	if err != nil {
		panic(err)
	}

	res19, err := rdb.XGroupCreate(ctx, "race:france", "france_riders", "$").Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res19) // >>> OK

}

func ExampleClient_xgroupcreatemkstream() {
	ctx := context.Background()

	rdb := redis.NewClient(&redis.Options{
		Addr:     "localhost:6379",
		Password: "", // no password docs
		DB:       0,  // use default DB
	})


	res20, err := rdb.XGroupCreateMkStream(ctx,
		"race:italy", "italy_riders", "$",
	).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res20) // >>> OK

}

func ExampleClient_xgroupread() {
	ctx := context.Background()

	rdb := redis.NewClient(&redis.Options{
		Addr:     "localhost:6379",
		Password: "", // no password docs
		DB:       0,  // use default DB
	})


	_, err := rdb.XGroupCreateMkStream(ctx,
		"race:italy", "italy_riders", "$",
	).Result()

	if err != nil {
		panic(err)
	}

	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
		Stream: "race:italy",
		Values: map[string]interface{}{"rider": "Castilla"},
	}).Result()
	// >>> 1692632639151-0

	if err != nil {
		panic(err)
	}

	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
		Stream: "race:italy",
		Values: map[string]interface{}{"rider": "Royce"},
	}).Result()
	// >>> 1692632647899-0

	if err != nil {
		panic(err)
	}

	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
		Stream: "race:italy",
		Values: map[string]interface{}{"rider": "Sam-Bodden"},
	}).Result()
	// >>> 1692632662819-0

	if err != nil {
		panic(err)
	}

	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
		Stream: "race:italy",
		Values: map[string]interface{}{"rider": "Prickett"},
	}).Result()
	// >>> 1692632670501-0

	if err != nil {
		panic(err)
	}

	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
		Stream: "race:italy",
		Values: map[string]interface{}{"rider": "Norem"},
	}).Result()
	// >>> 1692632678249-0

	if err != nil {
		panic(err)
	}

	// fmt.Println(res25)

	res21, err := rdb.XReadGroup(ctx, &redis.XReadGroupArgs{
		Streams:  []string{"race:italy", ">"},
		Group:    "italy_riders",
		Consumer: "Alice",
		Count:    1,
	}).Result()

	if err != nil {
		panic(err)
	}

	// fmt.Println(res21)
	// >>> [{race:italy [{1692632639151-0 map[rider:Castilla] 0 0}]}]


	xlen, err := rdb.XLen(ctx, "race:italy").Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(xlen)

}

func ExampleClient_raceitaly() {
	ctx := context.Background()

	rdb := redis.NewClient(&redis.Options{
		Addr:     "localhost:6379",
		Password: "", // no password docs
		DB:       0,  // use default DB
	})


	_, err := rdb.XGroupCreateMkStream(ctx,
		"race:italy", "italy_riders", "$",
	).Result()

	if err != nil {
		panic(err)
	}

	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
		Stream: "race:italy",
		Values: map[string]interface{}{"rider": "Castilla"},
		ID:     "1692632639151-0",
	}).Result()

	if err != nil {
		panic(err)
	}

	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
		Stream: "race:italy",
		Values: map[string]interface{}{"rider": "Royce"},
		ID:     "1692632647899-0",
	}).Result()

	if err != nil {
		panic(err)
	}

	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
		Stream: "race:italy",
		Values: map[string]interface{}{"rider": "Sam-Bodden"},
		ID:     "1692632662819-0",
	}).Result()

	if err != nil {
		panic(err)
	}

	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
		Stream: "race:italy",
		Values: map[string]interface{}{"rider": "Prickett"},
		ID:     "1692632670501-0",
	}).Result()

	if err != nil {
		panic(err)
	}

	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
		Stream: "race:italy",
		Values: map[string]interface{}{"rider": "Norem"},
		ID:     "1692632678249-0",
	}).Result()

	if err != nil {
		panic(err)
	}

	_, err = rdb.XReadGroup(ctx, &redis.XReadGroupArgs{
		Streams:  []string{"race:italy", ">"},
		Group:    "italy_riders",
		Consumer: "Alice",
		Count:    1,
	}).Result()

	if err != nil {
		panic(err)
	}
	res22, err := rdb.XReadGroup(ctx, &redis.XReadGroupArgs{
		Streams:  []string{"race:italy", "0"},
		Group:    "italy_riders",
		Consumer: "Alice",
	}).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res22)
	// >>> [{race:italy [{1692632639151-0 map[rider:Castilla] 0 0}]}]

	res23, err := rdb.XAck(ctx,
		"race:italy", "italy_riders", "1692632639151-0",
	).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res23) // >>> 1

	res24, err := rdb.XReadGroup(ctx, &redis.XReadGroupArgs{
		Streams:  []string{"race:italy", "0"},
		Group:    "italy_riders",
		Consumer: "Alice",
	}).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res24)
	// >>> [{race:italy []}]

	res25, err := rdb.XReadGroup(ctx, &redis.XReadGroupArgs{
		Streams:  []string{"race:italy", ">"},
		Group:    "italy_riders",
		Consumer: "Bob",
		Count:    2,
	}).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res25)
	// >>> [{race:italy [{1692632647899-0 map[rider:Royce] 0 0} {1692632662819-0 map[rider:Sam-Bodden] 0 0}]}]


	res26, err := rdb.XPending(ctx, "race:italy", "italy_riders").Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res26)
	// >>> &{2 1692632647899-0 1692632662819-0 map[Bob:2]}

	res27, err := rdb.XPendingExt(ctx, &redis.XPendingExtArgs{
		Stream: "race:italy",
		Group:  "italy_riders",
		Start:  "-",
		End:    "+",
		Count:  10,
	}).Result()

	if err != nil {
		panic(err)
	}

	// fmt.Println(res27)
	// >>> [{1692632647899-0 Bob 0s 1} {1692632662819-0 Bob 0s 1}]

	res28, err := rdb.XRange(ctx, "race:italy",
		"1692632647899-0", "1692632647899-0",
	).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res28) // >>> [{1692632647899-0 map[rider:Royce] 0 0}]

	res29, err := rdb.XClaim(ctx, &redis.XClaimArgs{
		Stream:   "race:italy",
		Group:    "italy_riders",
		Consumer: "Alice",
		MinIdle:  0,
		Messages: []string{"1692632647899-0"},
	}).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res29)

	res30, res30a, err := rdb.XAutoClaim(ctx, &redis.XAutoClaimArgs{
		Stream:   "race:italy",
		Group:    "italy_riders",
		Consumer: "Alice",
		Start:    "0-0",
		Count:    1,
	}).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res30)  // >>> [{1692632647899-0 map[rider:Royce] 0 0}]
	fmt.Println(res30a) // >>> 1692632662819-0

	res31, res31a, err := rdb.XAutoClaim(ctx, &redis.XAutoClaimArgs{
		Stream:   "race:italy",
		Group:    "italy_riders",
		Consumer: "Lora",
		Start:    "(1692632662819-0",
		Count:    1,
	}).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res31)  // >>> []
	fmt.Println(res31a) // >>> 0-0

	res32, err := rdb.XInfoStream(ctx, "race:italy").Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res32.Length)
	// >>> 5
	fmt.Println(res32.FirstEntry)
	// >>> {1692632639151-0 map[rider:Castilla] 0 0}

	res33, err := rdb.XInfoGroups(ctx, "race:italy").Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res33)
	// >>> [{italy_riders 3 2 1692632662819-0 3 2}]

	res34, err := rdb.XInfoConsumers(ctx, "race:italy", "italy_riders").Result()

	if err != nil {
		panic(err)
	}

	// fmt.Println(res34)
	// >>> [{Alice 1 1ms 1ms} {Bob 1 2ms 2ms} {Lora 0 1ms -1ms}]

	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
		Stream: "race:italy",
		MaxLen: 2,
		Values: map[string]interface{}{"rider": "Jones"},
	},
	).Result()

	if err != nil {
		panic(err)
	}

	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
		Stream: "race:italy",
		MaxLen: 2,
		Values: map[string]interface{}{"rider": "Wood"},
	},
	).Result()

	if err != nil {
		panic(err)
	}

	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
		Stream: "race:italy",
		MaxLen: 2,
		Values: map[string]interface{}{"rider": "Henshaw"},
	},
	).Result()

	if err != nil {
		panic(err)
	}

	res35, err := rdb.XLen(ctx, "race:italy").Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res35) // >>> 2

	res36, err := rdb.XRange(ctx, "race:italy", "-", "+").Result()

	if err != nil {
		panic(err)
	}

	// fmt.Println(res36)
	// >>> [{1726649529170-1 map[rider:Wood] 0 0} {1726649529171-0 map[rider:Henshaw] 0 0}]

	res37, err := rdb.XTrimMaxLen(ctx, "race:italy", 10).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res37) // >>> 0

	res38, err := rdb.XTrimMaxLenApprox(ctx, "race:italy", 10, 20).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res38) // >>> 0


}

func ExampleClient_xdel() {
	ctx := context.Background()

	rdb := redis.NewClient(&redis.Options{
		Addr:     "localhost:6379",
		Password: "", // no password docs
		DB:       0,  // use default DB
	})


	_, err := rdb.XAdd(ctx, &redis.XAddArgs{
		Stream: "race:italy",
		MaxLen: 2,
		Values: map[string]interface{}{"rider": "Wood"},
		ID:     "1692633198206-0",
	},
	).Result()

	if err != nil {
		panic(err)
	}

	_, err = rdb.XAdd(ctx, &redis.XAddArgs{
		Stream: "race:italy",
		MaxLen: 2,
		Values: map[string]interface{}{"rider": "Henshaw"},
		ID:     "1692633208557-0",
	},
	).Result()

	if err != nil {
		panic(err)
	}

	res39, err := rdb.XRangeN(ctx, "race:italy", "-", "+", 2).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res39)
	// >>> [{1692633198206-0 map[rider:Wood] 0 0} {1692633208557-0 map[rider:Henshaw] 0 0}]

	res40, err := rdb.XDel(ctx, "race:italy", "1692633208557-0").Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res40) // 1

	res41, err := rdb.XRangeN(ctx, "race:italy", "-", "+", 2).Result()

	if err != nil {
		panic(err)
	}

	fmt.Println(res41)
	// >>> [{1692633198206-0 map[rider:Wood] 0 0}]

}

using NRedisStack.Tests;
using StackExchange.Redis;



public class StreamTutorial
{
    public void Run()
    {
        var muxer = ConnectionMultiplexer.Connect("localhost:6379");
        var db = muxer.GetDatabase();

        RedisValue res1 = db.StreamAdd(
            "race:france",
            [
                new("rider", "Castilla"),
                new("speed", 30.2),
                new("position", 1),
                new("location_id", 1)
            ]
        );
        Console.WriteLine(res1);    // >>> 1712668482289-0

        RedisValue res2 = db.StreamAdd(
            "race:france",
            [
                new("rider", "Norem"),
                new("speed", 28.8),
                new("position", 3),
                new("location_id", 1)
            ]
        );
        Console.WriteLine(res2);    // >>> 1712668766534-1

        RedisValue res3 = db.StreamAdd(
            "race:france",
            [
                new("rider", "Prickett"),
                new("speed", 29.7),
                new("position", 2),
                new("location_id", 1)
            ]
        );
        Console.WriteLine(res3);    // >>> 1712669055705-0


        // Tests for 'xadd' step.


        StreamEntry[] res4 = db.StreamRange("race:france", "1712668482289-0", "+", 2);

        foreach (StreamEntry entry in res4)
        {
            Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
        }

        // >>> 1712668482289-0: [rider: Castilla, speed: 30.199999999999999, position: 1, location_id: 1]
        // >>> 1712668766534-1: [rider: Norem, speed: 28.800000000000001, position: 3, location_id: 1]

        // Tests for 'xrange' step.


        StreamEntry[] res5 = db.StreamRead("race:france", 0, 100);

        foreach (StreamEntry entry in res4)
        {
            Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
        }

        // >>> 1712668482289-0: [rider: Castilla, speed: 30.199999999999999, position: 1, location_id: 1]
        // >>> 1712668766534-1: [rider: Norem, speed: 28.800000000000001, position: 3, location_id: 1]
        // >>> 1712669055705-0: [rider: Prickett, speed: 29.699999999999999, position: 2, location_id: 1]

        // Tests for 'xread_block' step.


        RedisValue res6 = db.StreamAdd(
            "race:france",
            [
                new("rider", "Castilla"),
                new("speed", 29.9),
                new("position", 1),
                new("location_id", 2)
            ]
        );

        Console.WriteLine(res6);    // >>> 1712675674750-0

        // Tests for 'xadd_2' step.


        long res7 = db.StreamLength("race:france");
        Console.WriteLine(res7);    // >>> 4

        // Tests for 'xlen' step.


        RedisValue res8 = db.StreamAdd(
            "race:usa",
            [
                new("racer", "Castilla")
            ],
            "0-1"
        );
        Console.WriteLine(res8);    // >>> 0-1

        RedisValue res9 = db.StreamAdd(
            "race:usa",
            [
                new("racer", "Norem")
            ],
            "0-2"
        );
        Console.WriteLine(res9);    // >>> 0-2

        // Tests for 'xadd_id' step.


        try
        {
            RedisValue res10 = db.StreamAdd(
                "race:usa",
                [
                    new("racer", "Prickett")
                ],
                "0-1"
            );
        }
        catch (RedisServerException ex)
        {
            Console.WriteLine(ex);  // >>> ERR The ID specified in XADD is equal or smaller than the target stream top item
        }

        // Tests for 'xadd_bad_id' step.


        RedisValue res11 = "";
        Version version = muxer.GetServer("localhost:6379").Version;
        if (version.Major >= 7)
        {
            res11 = db.StreamAdd(
                "race:usa",
                [
                    new("rider", "Norem")
                ],
                "0-*"
            );

            Console.WriteLine(res11);   // >>> "0-3"
        }

        // Tests for 'xadd_7' step.


        StreamEntry[] res12 = db.StreamRange("race:france", "-", "+");

        foreach (StreamEntry entry in res12)
        {
            Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
        }
        // >>> 1712668482289-0: [rider: Castilla, speed: 30.199999999999999, position: 1, location_id: 1]
        // >>> 1712668766534-1: [rider: Norem, speed: 28.800000000000001, position: 3, location_id: 1]
        // >>> 1712669055705-0: [rider: Prickett, speed: 29.699999999999999, position: 2, location_id: 1]
        // >>> 1712675674750-0: [rider: Castilla, speed: 29.899999999999999, position: 1, location_id: 2]

        // Tests for 'xrange_all' step.


        StreamEntry[] res13 = db.StreamRange("race:france", 1712668482289, 1712668482291);

        foreach (StreamEntry entry in res13)
        {
            Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
        }
        // >>> 1712668482289-0: [rider: Castilla, speed: 30.199999999999999, position: 1, location_id: 1]

        // Tests for 'xrange_time' step.


        StreamEntry[] res14 = db.StreamRange("race:france", "-", "+", 2);

        foreach (StreamEntry entry in res14)
        {
            Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
        }
        // >>> 1712668482289-0: [rider: Castilla, speed: 30.199999999999999, position: 1, location_id: 1]
        // >>> 1712668766534-1: [rider: Norem, speed: 28.800000000000001, position: 3, location_id: 1]

        // Tests for 'xrange_step_1' step.


        StreamEntry[] res15 = db.StreamRange("race:france", "(1712668766534-1", "+", 2);

        foreach (StreamEntry entry in res15)
        {
            Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
        }
        // >>> 1712669055705-0: [rider: Prickett, speed: 29.699999999999999, position: 2, location_id: 1]
        // >>> 1712675674750-0: [rider: Castilla, speed: 29.899999999999999, position: 1, location_id: 2]

        // Tests for 'xrange_step_2' step.


        StreamEntry[] res16 = db.StreamRange("race:france", "(1712675674750-0", "+", 2);

        foreach (StreamEntry entry in res16)
        {
            Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
        }
        // >>> <empty array>

        // Tests for 'xrange_empty' step.


        StreamEntry[] res17 = db.StreamRange("race:france", "+", "-", 1, Order.Descending);

        foreach (StreamEntry entry in res17)
        {
            Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
        }
        // >>> 1712675674750-0: [rider: Castilla, speed: 29.899999999999999, position: 1, location_id: 2]

        // Tests for 'xrevrange' step.


        StreamEntry[] res18 = db.StreamRead("race:france", 0, 2);

        foreach (StreamEntry entry in res18)
        {
            Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
        }
        // >>> 1712668482289-0: [rider: Castilla, speed: 30.199999999999999, position: 1, location_id: 1]
        // >>> 1712668766534-1: [rider: Norem, speed: 28.800000000000001, position: 3, location_id: 1]

        // Tests for 'xread' step.


        bool res19 = db.StreamCreateConsumerGroup("race:france", "france_riders", "$");
        Console.WriteLine(res19);   // >>> true

        // Tests for 'xgroup_create' step.


        bool res20 = db.StreamCreateConsumerGroup("race:italy", "italy_riders", "$", true);
        Console.WriteLine(res20);   // >>> true

        // Tests for 'xgroup_create_mkstream' step.


        RedisValue groupRes = db.StreamAdd(
            "race:italy",
            [new("rider", "Castilla")]
        ); // 1712744323758-0

        groupRes = db.StreamAdd(
            "race:italy",
            [new("rider", "Royce")]
        ); // 1712744358384-0

        groupRes = db.StreamAdd(
            "race:italy",
            [new("rider", "Sam-Bodden")]
        ); // 1712744379676-0

        groupRes = db.StreamAdd(
            "race:italy",
            [new("rider", "Prickett")]
        ); // 1712744399401-0

        groupRes = db.StreamAdd(
            "race:italy",
            [new("rider", "Norem")]
        ); // 1712744413117-0

        StreamEntry[] res21 = db.StreamReadGroup("race:italy", "italy_riders", "Alice", ">", 1);

        foreach (StreamEntry entry in res21)
        {
            Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
        }
        // >>> 1712744323758-0: [rider: Castilla]

        // Tests for 'xgroup_read' step.


        StreamEntry[] res22 = db.StreamReadGroup("race:italy", "italy_riders", "Alice", "0");

        foreach (StreamEntry entry in res22)
        {
            Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
            // >>> 1712744323758-0: [rider: Castilla]
        }

        // Tests for 'xgroup_read_id' step.


        long res23 = db.StreamAcknowledge("race:italy", "italy_riders", "1712744323758-0");
        Console.WriteLine(res23);   // >>> 1

        StreamEntry[] res24 = db.StreamReadGroup("race:italy", "italy_riders", "Alice", "0");

        foreach (StreamEntry entry in res24)
        {
            Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
        }
        // >>> <empty array>

        // Tests for 'xack' step.


        StreamEntry[] res25 = db.StreamReadGroup("race:italy", "italy_riders", "Bob", ">", 2);

        foreach (StreamEntry entry in res25)
        {
            Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
        }
        // >>> 1712744358384-0: [rider: Royce]
        // >>> 1712744379676-0: [rider: Sam-Bodden]

        // Tests for 'xgroup_read_bob' step.


        StreamPendingInfo res26 = db.StreamPending("race:italy", "italy_riders");
        Console.WriteLine($"pending: {res26.PendingMessageCount}, min: {res26.LowestPendingMessageId}, max: {res26.HighestPendingMessageId}, consumers:[{string.Join(", ", res26.Consumers.Select(c => $"{c.Name}: {c.PendingMessageCount}"))}]");
        // >>> pending: 2, min: 1712747506906-0, max: 1712747506907-0, consumers:[name: Bob, pending:2]

        // Tests for 'xpending' step.


        StreamPendingMessageInfo[] res27 = db.StreamPendingMessages(
            "race:italy", "italy_riders", 10, "", "-", "+"
        );

        foreach (StreamPendingMessageInfo info in res27)
        {
            Console.WriteLine($"message_id: {info.MessageId}, consumer: {info.ConsumerName}, time_since_delivered: {info.IdleTimeInMilliseconds}, times_delivered: {info.DeliveryCount}");
        }
        // >>> message_id: min: 1712747506906-0, consumer: Bob, time_since_delivered: 31084, times_delivered: 1
        // >>> message_id: min: 1712747506907-0, consumer: Bob, time_since_delivered: 31084, times_delivered: 1

        // Tests for 'xpending_plus_minus' step.


        StreamEntry[] res28 = db.StreamRange("race:italy", "1712744358384-0", "1712744358384-0");

        foreach (StreamEntry entry in res28)
        {
            Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
        }
        // >>> 1712744358384-0: [rider: Royce]

        // Tests for 'xrange_pending' step.


        StreamEntry[] res29 = db.StreamClaim(
            "race:italy", "italy_riders", "Alice", 60000, [1712744358384 - 0]
        );

        foreach (StreamEntry entry in res29)
        {
            Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
        }
        // >>> 1712744358384-0: [rider: Royce]

        // Tests for 'xclaim' step.

        StreamAutoClaimResult res30 = db.StreamAutoClaim(
            "race:italy", "italy_riders", "Alice", 1, "0-0", 1
        );

        Console.WriteLine($"{res30.NextStartId}, ({string.Join(", ", res30.ClaimedEntries.Select(entry => $"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]"))})");
        // >>> 1712744379676-0, (1712744358384-0: [rider: Royce])

        // Tests for 'xautoclaim' step.


        StreamAutoClaimResult res31 = db.StreamAutoClaim(
            "race:italy", "italy_riders", "Alice", 1, "(1712744358384-0", 1
        );

        Console.WriteLine($"{res31.NextStartId}, ({string.Join(", ", res31.ClaimedEntries.Select(entry => $"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]"))})");
        // >>> 0-0, (1712744379676-0: [rider: Sam-Bodden])

        // Tests for 'xautoclaim_cursor' step.


        StreamInfo res32 = db.StreamInfo("race:italy");
        Console.WriteLine($"length: {res32.Length}, radix-tree-keys: {res32.RadixTreeKeys}, radix-tree-nodes: {res32.RadixTreeNodes}, last-generated-id: {res32.LastGeneratedId}, first-entry: {$"{res32.FirstEntry.Id}: [{string.Join(", ", res32.FirstEntry.Values.Select(b => $"{b.Name}: {b.Value}"))}]"}, last-entry: {$"{res32.LastEntry.Id}: [{string.Join(", ", res32.LastEntry.Values.Select(b => $"{b.Name}: {b.Value}"))}]"}");
        // >>> length: 5, radix-tree-keys: 1, radix-tree-nodes: 2, last-generated-id: 1712756762686-1, first-entry: 1712756762685-0: [rider: Castilla], last-entry: 1712756762686-1: [rider: Norem]

        // Tests for 'xinfo' step.


        StreamGroupInfo[] res33 = db.StreamGroupInfo("race:italy");

        foreach (StreamGroupInfo info in res33)
        {
            Console.WriteLine($"name: {info.Name}, consumers: {info.ConsumerCount}, pending: {info.PendingMessageCount}, last-delivered-id: {info.LastDeliveredId}");
        }
        // >>> name: italy_riders, consumers: 2, pending: 2, last-delivered-id: 1712757192730-2

        // Tests for 'xinfo_groups' step.


        StreamConsumerInfo[] res34 = db.StreamConsumerInfo("race:italy", "italy_riders");

        foreach (StreamConsumerInfo info in res34)
        {
            Console.WriteLine($"name: {info.Name}, pending: {info.PendingMessageCount}, idle: {info.IdleTimeInMilliseconds}");
        }
        // >>> name: Alice, pending: 1, idle: 7717
        // >>> name: Bob, pending: 0, idle: 7722

        // Tests for 'xinfo_consumers' step.


        db.StreamAdd(
            "race:italy", [new("rider", "Jones")], null, 2, true
        );

        db.StreamAdd(
            "race:italy", [new("rider", "Wood")], null, 2, true
        );

        db.StreamAdd(
            "race:italy", [new("rider", "Henshaw")], null, 2, true
        );

        long res35 = db.StreamLength("race:italy");
        Console.WriteLine(res35); // >>> 8

        StreamEntry[] res36 = db.StreamRange("race:italy", "-", "+");

        foreach (StreamEntry entry in res36)
        {
            Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
        }
        // >>> 1712758336128-0: [rider: Castilla]
        // >>> 1712758336128-1: [rider: Royce]
        // >>> 1712758336128-2: [rider: Sam-Bodden]
        // >>> 1712758336129-0: [rider: Prickett]
        // >>> 1712758336139-0: [rider: Norem]
        // >>> 1712758340854-0: [rider: Jones]
        // >>> 1712758341645-0: [rider: Wood]
        // >>> 1712758342134-0: [rider: Henshaw]

        db.StreamAdd(
            "race:italy", [new("rider", "Smith")], null, 2, false
        );

        StreamEntry[] res37 = db.StreamRange("race:italy", "-", "+");

        foreach (StreamEntry entry in res37)
        {
            Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
        }
        // 1712758746476-1: [rider: Henshaw]
        // 1712758746477-0: [rider: Smith]

        // Tests for 'maxlen' step.


        long res38 = db.StreamTrim("race:italy", 10, false);
        Console.WriteLine(res38);   // >>> 0

        // Tests for 'xtrim' step.


        long res39 = db.StreamTrim("race:italy", 10, true);
        Console.WriteLine(res39);   // >>> 0

        // Tests for 'xtrim2' step.


        StreamEntry[] res40 = db.StreamRange("race:italy", "-", "+");

        foreach (StreamEntry entry in res40)
        {
            Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");
        }
        // >>> 1712759694003-0: [rider: Henshaw]
        // >>> 1712759694003-1: [rider: Smith]

        long res41 = db.StreamDelete("race:italy", ["1712759694003-1"]);
        Console.WriteLine(res41);   // >>> 1

        StreamEntry[] res42 = db.StreamRange("race:italy", "-", "+");

        foreach (StreamEntry entry in res42)
        {
            Console.WriteLine($"{entry.Id}: [{string.Join(", ", entry.Values.Select(b => $"{b.Name}: {b.Value}"))}]");

        }
        // >>> 1712759694003-0: [rider: Henshaw]

        // Tests for 'xdel' step.


    }
}

require 'redis'

r = Redis.new


res1 = r.xadd('race:france', {
  'rider' => 'Castilla',
  'speed' => 30.2,
  'position' => 1,
  'location_id' => 1
})
puts res1 # 1692632086370-0, for example

res2 = r.xadd('race:france', {
  'rider' => 'Norem',
  'speed' => 28.8,
  'position' => 3,
  'location_id' => 1
})
puts res2 # 1692632094485-0, for example

res3 = r.xadd('race:france', {
  'rider' => 'Prickett',
  'speed' => 29.7,
  'position' => 2,
  'location_id' => 1
})
puts res3 # 1692632102976-0, for example


r.del('race:france')
r.xadd('race:france', {
  'rider' => 'Castilla',
  'speed' => '30.2',
  'position' => '1',
  'location_id' => '1'
}, id: '1692632086370-0')
r.xadd('race:france', {
  'rider' => 'Norem',
  'speed' => '28.8',
  'position' => '3',
  'location_id' => '1'
}, id: '1692632094485-0')
r.xadd('race:france', {
  'rider' => 'Prickett',
  'speed' => '29.7',
  'position' => '2',
  'location_id' => '1'
}, id: '1692632102976-0')
r.xadd('race:france', {
  'rider' => 'Castilla',
  'speed' => '29.9',
  'position' => '1',
  'location_id' => '2'
}, id: '1692632147973-0')
res4 = r.xrange('race:france', '1692632086370-0', '+', count: 2)
puts res4.inspect
# [["1692632086370-0", {"rider"=>"Castilla", "speed"=>"30.2", "position"=>"1", "location_id"=>"1"}],
#  ["1692632094485-0", {"rider"=>"Norem", "speed"=>"28.8", "position"=>"3", "location_id"=>"1"}]]


r.del('race:france')
r.xadd('race:france', {'rider' => 'Castilla'}, id: '1692632086370-0')
res5 = r.xread(['race:france'], ['$'], count: 100, block: 300)
puts res5.inspect # {}


res6 = r.xadd('race:france', {
  'rider' => 'Castilla',
  'speed' => 29.9,
  'position' => 1,
  'location_id' => 2
})
puts res6 # 1692632147973-0, for example


r.del('race:france')
r.xadd('race:france', {'rider' => 'Castilla'}, id: '1692632086370-0')
r.xadd('race:france', {'rider' => 'Norem'}, id: '1692632094485-0')
r.xadd('race:france', {'rider' => 'Prickett'}, id: '1692632102976-0')
r.xadd('race:france', {'rider' => 'Castilla'}, id: '1692632147973-0')
res7 = r.xlen('race:france')
puts res7 # 4


r.del('race:usa')
res8 = r.xadd('race:usa', {'racer' => 'Castilla'}, id: '0-1')
puts res8 # 0-1

res9 = r.xadd('race:usa', {'racer' => 'Norem'}, id: '0-2')
puts res9 # 0-2


begin
  r.xadd('race:usa', {'racer' => 'Prickett'}, id: '0-1')
rescue Redis::CommandError => e
  puts e.message
  # ERR The ID specified in XADD is equal or smaller than the target stream top item
end


r.del('race:usa')
r.xadd('race:usa', {'racer' => 'Castilla'}, id: '0-1')
r.xadd('race:usa', {'racer' => 'Norem'}, id: '0-2')
res10 = r.xadd('race:usa', {'racer' => 'Prickett'}, id: '0-*')
puts res10 # 0-3


r.del('race:france')
r.xadd('race:france', {'rider' => 'Castilla', 'speed' => '30.2', 'position' => '1', 'location_id' => '1'}, id: '1692632086370-0')
r.xadd('race:france', {'rider' => 'Norem', 'speed' => '28.8', 'position' => '3', 'location_id' => '1'}, id: '1692632094485-0')
r.xadd('race:france', {'rider' => 'Prickett', 'speed' => '29.7', 'position' => '2', 'location_id' => '1'}, id: '1692632102976-0')
r.xadd('race:france', {'rider' => 'Castilla', 'speed' => '29.9', 'position' => '1', 'location_id' => '2'}, id: '1692632147973-0')
res11 = r.xrange('race:france', '-', '+')
puts res11.inspect
# [["1692632086370-0", {"rider"=>"Castilla", "speed"=>"30.2", "position"=>"1", "location_id"=>"1"}],
#  ["1692632094485-0", {"rider"=>"Norem", "speed"=>"28.8", "position"=>"3", "location_id"=>"1"}],
#  ["1692632102976-0", {"rider"=>"Prickett", "speed"=>"29.7", "position"=>"2", "location_id"=>"1"}],
#  ["1692632147973-0", {"rider"=>"Castilla", "speed"=>"29.9", "position"=>"1", "location_id"=>"2"}]]


r.del('race:france')
r.xadd('race:france', {'rider' => 'Castilla', 'speed' => '30.2', 'position' => '1', 'location_id' => '1'}, id: '1692632086370-0')
r.xadd('race:france', {'rider' => 'Norem', 'speed' => '28.8', 'position' => '3', 'location_id' => '1'}, id: '1692632094485-0')
res12 = r.xrange('race:france', '1692632086369', '1692632086371')
puts res12.inspect
# [["1692632086370-0", {"rider"=>"Castilla", "speed"=>"30.2", "position"=>"1", "location_id"=>"1"}]]


r.del('race:france')
r.xadd('race:france', {'rider' => 'Castilla', 'speed' => '30.2', 'position' => '1', 'location_id' => '1'}, id: '1692632086370-0')
r.xadd('race:france', {'rider' => 'Norem', 'speed' => '28.8', 'position' => '3', 'location_id' => '1'}, id: '1692632094485-0')
r.xadd('race:france', {'rider' => 'Prickett', 'speed' => '29.7', 'position' => '2', 'location_id' => '1'}, id: '1692632102976-0')
r.xadd('race:france', {'rider' => 'Castilla', 'speed' => '29.9', 'position' => '1', 'location_id' => '2'}, id: '1692632147973-0')
res13 = r.xrange('race:france', '-', '+', count: 2)
puts res13.inspect
# [["1692632086370-0", {"rider"=>"Castilla", "speed"=>"30.2", "position"=>"1", "location_id"=>"1"}],
#  ["1692632094485-0", {"rider"=>"Norem", "speed"=>"28.8", "position"=>"3", "location_id"=>"1"}]]


r.del('race:france')
r.xadd('race:france', {'rider' => 'Castilla', 'speed' => '30.2', 'position' => '1', 'location_id' => '1'}, id: '1692632086370-0')
r.xadd('race:france', {'rider' => 'Norem', 'speed' => '28.8', 'position' => '3', 'location_id' => '1'}, id: '1692632094485-0')
r.xadd('race:france', {'rider' => 'Prickett', 'speed' => '29.7', 'position' => '2', 'location_id' => '1'}, id: '1692632102976-0')
r.xadd('race:france', {'rider' => 'Castilla', 'speed' => '29.9', 'position' => '1', 'location_id' => '2'}, id: '1692632147973-0')
res14 = r.xrange('race:france', '(1692632094485-0', '+', count: 2)
puts res14.inspect
# [["1692632102976-0", {"rider"=>"Prickett", "speed"=>"29.7", "position"=>"2", "location_id"=>"1"}],
#  ["1692632147973-0", {"rider"=>"Castilla", "speed"=>"29.9", "position"=>"1", "location_id"=>"2"}]]


res15 = r.xrange('race:france', '(1692632147973-0', '+', count: 2)
puts res15.inspect # []


res16 = r.xrevrange('race:france', '+', '-', count: 1)
puts res16.inspect
# [["1692632147973-0", {"rider"=>"Castilla", "speed"=>"29.9", "position"=>"1", "location_id"=>"2"}]]


res17 = r.xread(['race:france'], ['0'], count: 2)
puts res17.inspect
# {"race:france"=>[["1692632086370-0", {"rider"=>"Castilla", "speed"=>"30.2", "position"=>"1", "location_id"=>"1"}],
#                  ["1692632094485-0", {"rider"=>"Norem", "speed"=>"28.8", "position"=>"3", "location_id"=>"1"}]]}


r.del('race:france')
r.xadd('race:france', {'rider' => 'Castilla'}, id: '1692632086370-0')
res18 = r.xgroup(:create, 'race:france', 'france_riders', '$')
puts res18 # OK


r.del('race:italy')
res19 = r.xgroup(:create, 'race:italy', 'italy_riders', '$', mkstream: true)
puts res19 # OK


r.del('race:italy')
r.xgroup(:create, 'race:italy', 'italy_riders', '$', mkstream: true)
r.xadd('race:italy', {'rider' => 'Castilla'}, id: '1692632639151-0')
r.xadd('race:italy', {'rider' => 'Royce'}, id: '1692632647899-0')
r.xadd('race:italy', {'rider' => 'Sam-Bodden'}, id: '1692632662819-0')
r.xadd('race:italy', {'rider' => 'Prickett'}, id: '1692632670501-0')
r.xadd('race:italy', {'rider' => 'Norem'}, id: '1692632678249-0')

res20 = r.xreadgroup('italy_riders', 'Alice', ['race:italy'], ['>'], count: 1)
puts res20.inspect
# {"race:italy"=>[["1692632639151-0", {"rider"=>"Castilla"}]]}


res21 = r.xreadgroup('italy_riders', 'Alice', ['race:italy'], ['0'], count: 1)
puts res21.inspect
# {"race:italy"=>[["1692632639151-0", {"rider"=>"Castilla"}]]}


res22 = r.xack('race:italy', 'italy_riders', '1692632639151-0')
puts res22 # 1

res23 = r.xreadgroup('italy_riders', 'Alice', ['race:italy'], ['0'])
puts res23.inspect
# {"race:italy"=>[]}


res24 = r.xreadgroup('italy_riders', 'Bob', ['race:italy'], ['>'], count: 2)
puts res24.inspect
# {"race:italy"=>[["1692632647899-0", {"rider"=>"Royce"}],
#                 ["1692632662819-0", {"rider"=>"Sam-Bodden"}]]}


res25 = r.xpending('race:italy', 'italy_riders')
puts res25.inspect
# {"size"=>2, "min_entry_id"=>"1692632647899-0", "max_entry_id"=>"1692632662819-0", "consumers"=>{"Bob"=>"2"}}


res26 = r.xpending('race:italy', 'italy_riders', '-', '+', 10)
puts res26.inspect


res27 = r.xrange('race:italy', '1692632647899-0', '1692632647899-0')
puts res27.inspect
# [["1692632647899-0", {"rider"=>"Royce"}]]


res28 = r.xclaim('race:italy', 'italy_riders', 'Alice', 0, '1692632647899-0')
puts res28.inspect
# [["1692632647899-0", {"rider"=>"Royce"}]]


res29 = r.xautoclaim('race:italy', 'italy_riders', 'Alice', 0, '0-0', count: 1)
puts res29.inspect
# {"next"=>"1692632662819-0", "entries"=>[["1692632647899-0", {"rider"=>"Royce"}]]}


res30 = r.xautoclaim('race:italy', 'italy_riders', 'Lora', 0, res29['next'], count: 1)
puts res30.inspect
# {"next"=>"0-0", "entries"=>[["1692632662819-0", {"rider"=>"Sam-Bodden"}]]}


res31 = r.xinfo(:stream, 'race:italy')
puts res31.inspect


res32 = r.xinfo(:groups, 'race:italy')
puts res32.inspect


res33 = r.xinfo(:consumers, 'race:italy', 'italy_riders')
puts res33.inspect


r.del('race:italy')
r.xadd('race:italy', {'rider' => 'Castilla'}, id: '1692632639151-0')
r.xadd('race:italy', {'rider' => 'Royce'}, id: '1692632647899-0')
r.xadd('race:italy', {'rider' => 'Sam-Bodden'}, id: '1692632662819-0')
r.xadd('race:italy', {'rider' => 'Prickett'}, id: '1692632670501-0')
r.xadd('race:italy', {'rider' => 'Norem'}, id: '1692632678249-0')
r.xadd('race:italy', {'rider' => 'Jones'}, id: '1692633189161-0', maxlen: 2)
r.xadd('race:italy', {'rider' => 'Wood'}, id: '1692633198206-0', maxlen: 2)
r.xadd('race:italy', {'rider' => 'Henshaw'}, id: '1692633208557-0', maxlen: 2)

res34 = r.xlen('race:italy')
puts res34 # 2

res35 = r.xrange('race:italy', '-', '+')
puts res35.inspect
# [["1692633198206-0", {"rider"=>"Wood"}], ["1692633208557-0", {"rider"=>"Henshaw"}]]


res36 = r.xtrim('race:italy', 10, approximate: false)
puts res36 # 0


r.del('mystream')
1.upto(10) do |n|
  r.xadd('mystream', {'field' => 'value'}, id: "#{n}-0")
end
res37 = r.xtrim('mystream', 10, approximate: true)
puts res37 # 0


r.del('race:italy')
r.xadd('race:italy', {'rider' => 'Wood'}, id: '1692633198206-0')
r.xadd('race:italy', {'rider' => 'Henshaw'}, id: '1692633208557-0')
res38 = r.xrange('race:italy', '-', '+', count: 2)
puts res38.inspect
# [["1692633198206-0", {"rider"=>"Wood"}], ["1692633208557-0", {"rider"=>"Henshaw"}]]

res39 = r.xdel('race:italy', '1692633208557-0')
puts res39 # 1

res40 = r.xrange('race:italy', '-', '+', count: 2)
puts res40.inspect
# [["1692633198206-0", {"rider"=>"Wood"}]]

mod stream_tests {
    use redis::{
        streams::{
            StreamAutoClaimOptions, StreamInfoConsumersReply, StreamInfoGroupsReply,
            StreamInfoStreamReply, StreamMaxlen, StreamPendingCountReply, StreamPendingReply,
            StreamRangeReply, StreamReadOptions, StreamReadReply, StreamTrimmingMode,
            StreamTrimOptions,
        },
        Commands,
    };
    use std::{thread::sleep, time::Duration};

    fn delete_keys(r: &mut redis::Connection, keys: &[&str]) {
        let _: usize = r.del(keys).unwrap_or(0);
    }

    fn add_france_fixed(r: &mut redis::Connection) {
        delete_keys(r, &["race:france"]);
        let _: Option<String> = r
            .xadd(
                "race:france",
                "1692632086370-0",
                &[
                    ("rider", "Castilla"),
                    ("speed", "30.2"),
                    ("position", "1"),
                    ("location_id", "1"),
                ],
            )
            .expect("add france 1");
        let _: Option<String> = r
            .xadd(
                "race:france",
                "1692632094485-0",
                &[
                    ("rider", "Norem"),
                    ("speed", "28.8"),
                    ("position", "3"),
                    ("location_id", "1"),
                ],
            )
            .expect("add france 2");
        let _: Option<String> = r
            .xadd(
                "race:france",
                "1692632102976-0",
                &[
                    ("rider", "Prickett"),
                    ("speed", "29.7"),
                    ("position", "2"),
                    ("location_id", "1"),
                ],
            )
            .expect("add france 3");
        let _: Option<String> = r
            .xadd(
                "race:france",
                "1692632147973-0",
                &[
                    ("rider", "Castilla"),
                    ("speed", "29.9"),
                    ("position", "1"),
                    ("location_id", "2"),
                ],
            )
            .expect("add france 4");
    }

    fn seed_usa_fixed(r: &mut redis::Connection) {
        delete_keys(r, &["race:usa"]);
        let _: Option<String> = r
            .xadd("race:usa", "0-1", &[("racer", "Castilla")])
            .expect("add usa 1");
        let _: Option<String> = r
            .xadd("race:usa", "0-2", &[("racer", "Norem")])
            .expect("add usa 2");
    }

    fn seed_italy_group_base(r: &mut redis::Connection) {
        delete_keys(r, &["race:italy"]);
        let _: () = r
            .xgroup_create_mkstream("race:italy", "italy_riders", "$")
            .expect("create italy group");
        let _: Option<String> = r
            .xadd("race:italy", "1692632639151-0", &[("rider", "Castilla")])
            .expect("add italy 1");
        let _: Option<String> = r
            .xadd("race:italy", "1692632647899-0", &[("rider", "Royce")])
            .expect("add italy 2");
        let _: Option<String> = r
            .xadd("race:italy", "1692632662819-0", &[("rider", "Sam-Bodden")])
            .expect("add italy 3");
        let _: Option<String> = r
            .xadd("race:italy", "1692632670501-0", &[("rider", "Prickett")])
            .expect("add italy 4");
        let _: Option<String> = r
            .xadd("race:italy", "1692632678249-0", &[("rider", "Norem")])
            .expect("add italy 5");
    }

    fn seed_italy_alice_pending(r: &mut redis::Connection) {
        seed_italy_group_base(r);
        let opts = StreamReadOptions::default().group("italy_riders", "Alice").count(1);
        let _: Option<StreamReadReply> = r
            .xread_options(&["race:italy"], &[">"], &opts)
            .expect("alice read pending");
    }

    fn seed_italy_after_ack(r: &mut redis::Connection) {
        seed_italy_alice_pending(r);
        let _: usize = r
            .xack("race:italy", "italy_riders", &["1692632639151-0"])
            .expect("ack first italy message");
    }

    fn seed_italy_bob_pending(r: &mut redis::Connection) {
        seed_italy_after_ack(r);
        let opts = StreamReadOptions::default().group("italy_riders", "Bob").count(2);
        let _: Option<StreamReadReply> = r
            .xread_options(&["race:italy"], &[">"], &opts)
            .expect("bob read pending");
    }

    fn seed_italy_info_state(r: &mut redis::Connection) {
        seed_italy_bob_pending(r);
        sleep(Duration::from_millis(5));
        let _: redis::streams::StreamClaimReply = r
            .xclaim("race:italy", "italy_riders", "Alice", 1, &["1692632647899-0"])
            .expect("alice claim");
        sleep(Duration::from_millis(5));
        let _: redis::streams::StreamClaimReply = r
            .xclaim("race:italy", "italy_riders", "Lora", 1, &["1692632662819-0"])
            .expect("lora claim");
    }

    fn seed_trim_stream(r: &mut redis::Connection) {
        delete_keys(r, &["mystream"]);
        for id in ["1-0", "2-0", "3-0", "4-0", "5-0", "6-0", "7-0", "8-0", "9-0", "10-0"] {
            let _: Option<String> = r
                .xadd("mystream", id, &[("field", "value")])
                .expect("seed mystream");
        }
    }

    fn run() {
        let mut r = match redis::Client::open("redis://127.0.0.1") {
            Ok(client) => match client.get_connection() {
                Ok(conn) => conn,
                Err(e) => {
                    println!("Failed to connect to Redis: {e}");
                    return;
                }
            },
            Err(e) => {
                println!("Failed to create Redis client: {e}");
                return;
            }
        };

        let res1 = {
            let res: Option<String> = r
                .xadd(
                    "race:france",
                    "*",
                    &[
                        ("rider", "Castilla"),
                        ("speed", "30.2"),
                        ("position", "1"),
                        ("location_id", "1"),
                    ],
                )
                .expect("xadd 1");
            res.expect("missing stream id")
        };
        println!("{res1}"); // >>> 1692632086370-0

        let res2 = {
            let res: Option<String> = r
                .xadd(
                    "race:france",
                    "*",
                    &[
                        ("rider", "Norem"),
                        ("speed", "28.8"),
                        ("position", "3"),
                        ("location_id", "1"),
                    ],
                )
                .expect("xadd 2");
            res.expect("missing stream id")
        };
        println!("{res2}"); // >>> 1692632094485-0

        let res3 = {
            let res: Option<String> = r
                .xadd(
                    "race:france",
                    "*",
                    &[
                        ("rider", "Prickett"),
                        ("speed", "29.7"),
                        ("position", "2"),
                        ("location_id", "1"),
                    ],
                )
                .expect("xadd 3");
            res.expect("missing stream id")
        };
        println!("{res3}"); // >>> 1692632102976-0

        add_france_fixed(&mut r);
        if let Ok(res) = r.xrange_count("race:france", "1692632086370-0", "+", 2) {
            let res: StreamRangeReply = res;
            let view: Vec<_> = res
                .ids
                .iter()
                .map(|entry| {
                    (
                        entry.id.clone(),
                        vec![
                            ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                            ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                            ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                            (
                                "location_id".to_string(),
                                entry.get::<String>("location_id").expect("missing location_id"),
                            ),
                        ],
                    )
                })
                .collect();
            println!("{view:?}");
            // >>> [("1692632086370-0", [("rider", "Castilla"), ("speed", "30.2"), ("position", "1"), ("location_id", "1")]), ("1692632094485-0", [("rider", "Norem"), ("speed", "28.8"), ("position", "3"), ("location_id", "1")])]
        }

        add_france_fixed(&mut r);
        let opts = StreamReadOptions::default().count(100).block(300);
        if let Ok(res) = r.xread_options(&["race:france"], &["$"], &opts) {
            let res: Option<StreamReadReply> = res;
            println!("{res:?}"); // >>> None
        }

        if let Ok(res) = r.xadd(
            "race:france",
            "*",
            &[
                ("rider", "Castilla"),
                ("speed", "29.9"),
                ("position", "1"),
                ("location_id", "2"),
            ],
        ) {
            let res: Option<String> = res;
            let res = res.expect("missing stream id");
            println!("{res}"); // >>> 1692632147973-0
        }

        add_france_fixed(&mut r);
        if let Ok(res) = r.xlen("race:france") {
            let res: usize = res;
            println!("{res}"); // >>> 4
        }

        delete_keys(&mut r, &["race:usa"]);
        if let Ok(res) = r.xadd("race:usa", "0-1", &[("racer", "Castilla")]) {
            let res: Option<String> = res;
            let res = res.expect("missing stream id");
            println!("{res}"); // >>> 0-1
        }

        if let Ok(res) = r.xadd("race:usa", "0-2", &[("racer", "Norem")]) {
            let res: Option<String> = res;
            let res = res.expect("missing stream id");
            println!("{res}"); // >>> 0-2
        }

        let res: redis::RedisResult<Option<String>> =
            r.xadd("race:usa", "0-1", &[("racer", "Prickett")]);
        match res {
            Ok(_) => {}
            Err(e) => {
                let msg = e.to_string();
                println!("{msg}");
                // >>> An error was signalled by the server - ResponseError: The ID specified in XADD is equal or smaller than the target stream top item
            }
        }

        seed_usa_fixed(&mut r);
        if let Ok(res) = r.xadd("race:usa", "0-*", &[("racer", "Prickett")]) {
            let res: Option<String> = res;
            let res = res.expect("missing stream id");
            println!("{res}"); // >>> 0-3
        }

        add_france_fixed(&mut r);
        if let Ok(res) = r.xrange_all("race:france") {
            let res: StreamRangeReply = res;
            let view: Vec<_> = res
                .ids
                .iter()
                .map(|entry| {
                    (
                        entry.id.clone(),
                        vec![
                            ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                            ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                            ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                            (
                                "location_id".to_string(),
                                entry.get::<String>("location_id").expect("missing location_id"),
                            ),
                        ],
                    )
                })
                .collect();
            println!("{view:?}");
            // >>> [("1692632086370-0", [("rider", "Castilla"), ("speed", "30.2"), ("position", "1"), ("location_id", "1")]), ("1692632094485-0", [("rider", "Norem"), ("speed", "28.8"), ("position", "3"), ("location_id", "1")]), ("1692632102976-0", [("rider", "Prickett"), ("speed", "29.7"), ("position", "2"), ("location_id", "1")]), ("1692632147973-0", [("rider", "Castilla"), ("speed", "29.9"), ("position", "1"), ("location_id", "2")])]
        }

        add_france_fixed(&mut r);
        if let Ok(res) = r.xrange("race:france", "1692632086369", "1692632086371") {
            let res: StreamRangeReply = res;
            let view: Vec<_> = res
                .ids
                .iter()
                .map(|entry| {
                    (
                        entry.id.clone(),
                        vec![
                            ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                            ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                            ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                            (
                                "location_id".to_string(),
                                entry.get::<String>("location_id").expect("missing location_id"),
                            ),
                        ],
                    )
                })
                .collect();
            println!("{view:?}");
            // >>> [("1692632086370-0", [("rider", "Castilla"), ("speed", "30.2"), ("position", "1"), ("location_id", "1")])]
        }

        add_france_fixed(&mut r);
        if let Ok(res) = r.xrange_count("race:france", "-", "+", 2) {
            let res: StreamRangeReply = res;
            let view: Vec<_> = res
                .ids
                .iter()
                .map(|entry| {
                    (
                        entry.id.clone(),
                        vec![
                            ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                            ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                            ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                            (
                                "location_id".to_string(),
                                entry.get::<String>("location_id").expect("missing location_id"),
                            ),
                        ],
                    )
                })
                .collect();
            println!("{view:?}");
            // >>> [("1692632086370-0", [("rider", "Castilla"), ("speed", "30.2"), ("position", "1"), ("location_id", "1")]), ("1692632094485-0", [("rider", "Norem"), ("speed", "28.8"), ("position", "3"), ("location_id", "1")])]
        }

        add_france_fixed(&mut r);
        if let Ok(res) = r.xrange_count("race:france", "(1692632094485-0", "+", 2) {
            let res: StreamRangeReply = res;
            let view: Vec<_> = res
                .ids
                .iter()
                .map(|entry| {
                    (
                        entry.id.clone(),
                        vec![
                            ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                            ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                            ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                            (
                                "location_id".to_string(),
                                entry.get::<String>("location_id").expect("missing location_id"),
                            ),
                        ],
                    )
                })
                .collect();
            println!("{view:?}");
            // >>> [("1692632102976-0", [("rider", "Prickett"), ("speed", "29.7"), ("position", "2"), ("location_id", "1")]), ("1692632147973-0", [("rider", "Castilla"), ("speed", "29.9"), ("position", "1"), ("location_id", "2")])]
        }

        add_france_fixed(&mut r);
        if let Ok(res) = r.xrange_count("race:france", "(1692632147973-0", "+", 2) {
            let res: StreamRangeReply = res;
            let view: Vec<_> = res
                .ids
                .iter()
                .map(|entry| {
                    (
                        entry.id.clone(),
                        vec![
                            ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                            ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                            ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                            (
                                "location_id".to_string(),
                                entry.get::<String>("location_id").expect("missing location_id"),
                            ),
                        ],
                    )
                })
                .collect();
            println!("{view:?}"); // >>> []
        }

        add_france_fixed(&mut r);
        if let Ok(res) = r.xrevrange_count("race:france", "+", "-", 1) {
            let res: StreamRangeReply = res;
            let view: Vec<_> = res
                .ids
                .iter()
                .map(|entry| {
                    (
                        entry.id.clone(),
                        vec![
                            ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                            ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                            ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                            (
                                "location_id".to_string(),
                                entry.get::<String>("location_id").expect("missing location_id"),
                            ),
                        ],
                    )
                })
                .collect();
            println!("{view:?}");
            // >>> [("1692632147973-0", [("rider", "Castilla"), ("speed", "29.9"), ("position", "1"), ("location_id", "2")])]
        }

        add_france_fixed(&mut r);
        let opts = StreamReadOptions::default().count(2);
        if let Ok(res) = r.xread_options(&["race:france"], &["0"], &opts) {
            let res: Option<StreamReadReply> = res;
            let reply = res.expect("xread should return data");
            let view: Vec<_> = reply
                .keys
                .iter()
                .map(|stream| {
                    (
                        stream.key.clone(),
                        stream
                            .ids
                            .iter()
                            .map(|entry| {
                                (
                                    entry.id.clone(),
                                    vec![
                                        ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                        ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                        ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                        (
                                            "location_id".to_string(),
                                            entry.get::<String>("location_id").expect("missing location_id"),
                                        ),
                                    ],
                                )
                            })
                            .collect::<Vec<_>>(),
                    )
                })
                .collect();
            println!("{view:?}");
            // >>> [("race:france", [("1692632086370-0", [("rider", "Castilla"), ("speed", "30.2"), ("position", "1"), ("location_id", "1")]), ("1692632094485-0", [("rider", "Norem"), ("speed", "28.8"), ("position", "3"), ("location_id", "1")])])]
        }

        add_france_fixed(&mut r);
        if let Ok(res) = r.xgroup_create("race:france", "france_riders", "$") {
            let res: () = res;
            let _ = res;
            println!("OK"); // >>> OK
        }

        delete_keys(&mut r, &["race:italy"]);
        if let Ok(res) = r.xgroup_create_mkstream("race:italy", "italy_riders", "$") {
            let res: () = res;
            let _ = res;
            println!("OK"); // >>> OK
        }

        delete_keys(&mut r, &["race:italy"]);
        let _: () = r
            .xgroup_create_mkstream("race:italy", "italy_riders", "$")
            .expect("create italy group");
        let italy_1: Option<String> = r
            .xadd("race:italy", "1692632639151-0", &[("rider", "Castilla")])
            .expect("italy1");
        let italy_1 = italy_1.expect("missing stream id");
        println!("{italy_1}"); // >>> 1692632639151-0
        let italy_2: Option<String> = r
            .xadd("race:italy", "1692632647899-0", &[("rider", "Royce")])
            .expect("italy2");
        let italy_2 = italy_2.expect("missing stream id");
        println!("{italy_2}"); // >>> 1692632647899-0
        let italy_3: Option<String> = r
            .xadd("race:italy", "1692632662819-0", &[("rider", "Sam-Bodden")])
            .expect("italy3");
        let italy_3 = italy_3.expect("missing stream id");
        println!("{italy_3}"); // >>> 1692632662819-0
        let italy_4: Option<String> = r
            .xadd("race:italy", "1692632670501-0", &[("rider", "Prickett")])
            .expect("italy4");
        let italy_4 = italy_4.expect("missing stream id");
        println!("{italy_4}"); // >>> 1692632670501-0
        let italy_5: Option<String> = r
            .xadd("race:italy", "1692632678249-0", &[("rider", "Norem")])
            .expect("italy5");
        let italy_5 = italy_5.expect("missing stream id");
        println!("{italy_5}"); // >>> 1692632678249-0

        let opts = StreamReadOptions::default().group("italy_riders", "Alice").count(1);
        if let Ok(res) = r.xread_options(&["race:italy"], &[">"], &opts) {
            let res: Option<StreamReadReply> = res;
            let view: Vec<_> = res
                .expect("xgroup read should return data")
                .keys
                .iter()
                .map(|stream| {
                    (
                        stream.key.clone(),
                        stream
                            .ids
                            .iter()
                            .map(|entry| {
                                (
                                    entry.id.clone(),
                                    vec![(
                                        "rider".to_string(),
                                        entry.get::<String>("rider").expect("missing rider"),
                                    )],
                                )
                            })
                            .collect::<Vec<_>>(),
                    )
                })
                .collect();
            println!("{view:?}");
            // >>> [("race:italy", [("1692632639151-0", [("rider", "Castilla")])])]
        }

        seed_italy_alice_pending(&mut r);
        let opts = StreamReadOptions::default().group("italy_riders", "Alice");
        if let Ok(res) = r.xread_options(&["race:italy"], &["0"], &opts) {
            let res: Option<StreamReadReply> = res;
            let view: Vec<_> = res
                .expect("xgroup history")
                .keys
                .iter()
                .map(|stream| {
                    (
                        stream.key.clone(),
                        stream
                            .ids
                            .iter()
                            .map(|entry| {
                                (
                                    entry.id.clone(),
                                    vec![(
                                        "rider".to_string(),
                                        entry.get::<String>("rider").expect("missing rider"),
                                    )],
                                )
                            })
                            .collect::<Vec<_>>(),
                    )
                })
                .collect();
            println!("{view:?}");
            // >>> [("race:italy", [("1692632639151-0", [("rider", "Castilla")])])]
        }

        seed_italy_alice_pending(&mut r);
        if let Ok(res) = r.xack("race:italy", "italy_riders", &["1692632639151-0"]) {
            let res: usize = res;
            println!("{res}"); // >>> 1
        }

        let opts = StreamReadOptions::default().group("italy_riders", "Alice");
        if let Ok(res) = r.xread_options(&["race:italy"], &["0"], &opts) {
            let res: Option<StreamReadReply> = res;
            let view: Vec<_> = res
                .expect("xgroup history")
                .keys
                .iter()
                .map(|stream| {
                    (
                        stream.key.clone(),
                        stream
                            .ids
                            .iter()
                            .map(|entry| {
                                (
                                    entry.id.clone(),
                                    vec![(
                                        "rider".to_string(),
                                        entry.get::<String>("rider").expect("missing rider"),
                                    )],
                                )
                            })
                            .collect::<Vec<_>>(),
                    )
                })
                .collect();
            println!("{view:?}"); // >>> [("race:italy", [])]
        }

        seed_italy_after_ack(&mut r);
        let opts = StreamReadOptions::default().group("italy_riders", "Bob").count(2);
        if let Ok(res) = r.xread_options(&["race:italy"], &[">"], &opts) {
            let res: Option<StreamReadReply> = res;
            let view: Vec<_> = res
                .expect("bob should receive data")
                .keys
                .iter()
                .map(|stream| {
                    (
                        stream.key.clone(),
                        stream
                            .ids
                            .iter()
                            .map(|entry| {
                                (
                                    entry.id.clone(),
                                    vec![(
                                        "rider".to_string(),
                                        entry.get::<String>("rider").expect("missing rider"),
                                    )],
                                )
                            })
                            .collect::<Vec<_>>(),
                    )
                })
                .collect();
            println!("{view:?}");
            // >>> [("race:italy", [("1692632647899-0", [("rider", "Royce")]), ("1692632662819-0", [("rider", "Sam-Bodden")])])]
        }

        seed_italy_bob_pending(&mut r);
        if let Ok(res) = r.xpending("race:italy", "italy_riders") {
            let res: StreamPendingReply = res;
            let view = match res {
                StreamPendingReply::Empty => None,
                StreamPendingReply::Data(data) => Some((
                    data.count,
                    data.start_id.clone(),
                    data.end_id.clone(),
                    data.consumers
                        .iter()
                        .map(|consumer| (consumer.name.clone(), consumer.pending))
                        .collect::<Vec<_>>(),
                )),
            }
            .expect("pending summary");
            println!("{view:?}");
            // >>> (2, "1692632647899-0", "1692632662819-0", [("Bob", 2)])
        }

        seed_italy_bob_pending(&mut r);
        sleep(Duration::from_millis(5));
        if let Ok(res) = r.xpending_count("race:italy", "italy_riders", "-", "+", 10) {
            let res: StreamPendingCountReply = res;
            let view: Vec<_> = res
                .ids
                .iter()
                .map(|entry| {
                    (
                        entry.id.clone(),
                        entry.consumer.clone(),
                        entry.last_delivered_ms,
                        entry.times_delivered,
                    )
                })
                .collect();
            println!("{view:?}");
            // >>> [("1692632647899-0", "Bob", 5, 1), ("1692632662819-0", "Bob", 5, 1)]
        }

        seed_italy_bob_pending(&mut r);
        if let Ok(res) = r.xrange("race:italy", "1692632647899-0", "1692632647899-0") {
            let res: StreamRangeReply = res;
            let view: Vec<_> = res
                .ids
                .iter()
                .map(|entry| {
                    (
                        entry.id.clone(),
                        vec![(
                            "rider".to_string(),
                            entry.get::<String>("rider").expect("missing rider"),
                        )],
                    )
                })
                .collect();
            println!("{view:?}"); // >>> [("1692632647899-0", [("rider", "Royce")])]
        }

        seed_italy_bob_pending(&mut r);
        sleep(Duration::from_millis(5));
        if let Ok(res) = r.xclaim("race:italy", "italy_riders", "Alice", 1, &["1692632647899-0"]) {
            let res: redis::streams::StreamClaimReply = res;
            let view: Vec<_> = res
                .ids
                .iter()
                .map(|entry| {
                    (
                        entry.id.clone(),
                        vec![(
                            "rider".to_string(),
                            entry.get::<String>("rider").expect("missing rider"),
                        )],
                    )
                })
                .collect();
            println!("{view:?}"); // >>> [("1692632647899-0", [("rider", "Royce")])]
        }

        seed_italy_bob_pending(&mut r);
        sleep(Duration::from_millis(5));
        let opts = StreamAutoClaimOptions::default().count(1);
        if let Ok(res) = r.xautoclaim_options("race:italy", "italy_riders", "Alice", 1, "0-0", opts) {
            let res: redis::streams::StreamAutoClaimReply = res;
            let claimed: Vec<_> = res
                .claimed
                .iter()
                .map(|entry| {
                    (
                        entry.id.clone(),
                        vec![(
                            "rider".to_string(),
                            entry.get::<String>("rider").expect("missing rider"),
                        )],
                    )
                })
                .collect();
            println!("{:?}", (res.next_stream_id.clone(), &claimed));
            // >>> ("1692632662819-0", [("1692632647899-0", [("rider", "Royce")])])
        }

        seed_italy_bob_pending(&mut r);
        sleep(Duration::from_millis(5));
        let first_opts = StreamAutoClaimOptions::default().count(1);
        let _: redis::streams::StreamAutoClaimReply = r
            .xautoclaim_options("race:italy", "italy_riders", "Alice", 1, "0-0", first_opts)
            .expect("first autoclaim");
        let next_opts = StreamAutoClaimOptions::default().count(1);
        if let Ok(res) = r.xautoclaim_options(
            "race:italy",
            "italy_riders",
            "Lora",
            1,
            "(1692632647899-0",
            next_opts,
        ) {
            let res: redis::streams::StreamAutoClaimReply = res;
            let claimed: Vec<_> = res
                .claimed
                .iter()
                .map(|entry| {
                    (
                        entry.id.clone(),
                        vec![(
                            "rider".to_string(),
                            entry.get::<String>("rider").expect("missing rider"),
                        )],
                    )
                })
                .collect();
            println!("{:?}", (res.next_stream_id.clone(), &claimed));
            // >>> ("0-0", [("1692632662819-0", [("rider", "Sam-Bodden")])])
        }

        seed_italy_info_state(&mut r);
        if let Ok(res) = r.xinfo_stream("race:italy") {
            let res: StreamInfoStreamReply = res;
            let view = (
                res.length,
                res.radix_tree_keys,
                res.groups,
                res.last_generated_id.clone(),
                res.first_entry.id.clone(),
                res.last_entry.id.clone(),
            );
            println!("{view:?}");
            // >>> (5, 1, 1, "1692632678249-0", "1692632639151-0", "1692632678249-0")
        }

        seed_italy_info_state(&mut r);
        if let Ok(res) = r.xinfo_groups("race:italy") {
            let res: StreamInfoGroupsReply = res;
            let view: Vec<_> = res
                .groups
                .iter()
                .map(|group| {
                    (
                        group.name.clone(),
                        group.consumers,
                        group.pending,
                        group.last_delivered_id.clone(),
                    )
                })
                .collect();
            println!("{view:?}");
            // >>> [("italy_riders", 3, 2, "1692632662819-0")]
        }

        seed_italy_info_state(&mut r);
        if let Ok(res) = r.xinfo_consumers("race:italy", "italy_riders") {
            let res: StreamInfoConsumersReply = res;
            let mut view: Vec<_> = res
                .consumers
                .iter()
                .map(|consumer| (consumer.name.clone(), consumer.pending, consumer.idle))
                .collect();
            view.sort_by(|a, b| a.0.cmp(&b.0));
            println!("{view:?}");
            // >>> [("Alice", 1, 5), ("Bob", 0, 5), ("Lora", 1, 5)]
        }

        delete_keys(&mut r, &["race:italy"]);
        let max1: Option<String> = r
            .xadd_maxlen("race:italy", StreamMaxlen::Equals(2), "1-0", &[("rider", "Jones")])
            .expect("maxlen add 1");
        let max1 = max1.expect("missing stream id");
        println!("{max1}"); // >>> 1-0
        let max2: Option<String> = r
            .xadd_maxlen("race:italy", StreamMaxlen::Equals(2), "2-0", &[("rider", "Wood")])
            .expect("maxlen add 2");
        let max2 = max2.expect("missing stream id");
        println!("{max2}"); // >>> 2-0
        let max3: Option<String> = r
            .xadd_maxlen("race:italy", StreamMaxlen::Equals(2), "3-0", &[("rider", "Henshaw")])
            .expect("maxlen add 3");
        let max3 = max3.expect("missing stream id");
        println!("{max3}"); // >>> 3-0

        if let Ok(res) = r.xlen("race:italy") {
            let res: usize = res;
            println!("{res}"); // >>> 2
        }

        if let Ok(res) = r.xrange_all("race:italy") {
            let res: StreamRangeReply = res;
            let view: Vec<_> = res
                .ids
                .iter()
                .map(|entry| {
                    (
                        entry.id.clone(),
                        vec![(
                            "rider".to_string(),
                            entry.get::<String>("rider").expect("missing rider"),
                        )],
                    )
                })
                .collect();
            println!("{view:?}");
            // >>> [("2-0", [("rider", "Wood")]), ("3-0", [("rider", "Henshaw")])]
        }

        delete_keys(&mut r, &["race:italy"]);
        let _: Option<String> = r.xadd("race:italy", "1-0", &[("rider", "Wood")]).expect("trim seed 1");
        let _: Option<String> = r.xadd("race:italy", "2-0", &[("rider", "Henshaw")]).expect("trim seed 2");
        if let Ok(res) = r.xtrim("race:italy", StreamMaxlen::Equals(10)) {
            let res: usize = res;
            println!("{res}"); // >>> 0
        }

        seed_trim_stream(&mut r);
        if let Ok(res) = r.xtrim_options(
            "mystream",
            &StreamTrimOptions::maxlen(StreamTrimmingMode::Approx, 10),
        ) {
            let res: usize = res;
            println!("{res}"); // >>> 0
        }

        delete_keys(&mut r, &["race:italy"]);
        let _: Option<String> = r.xadd("race:italy", "2-0", &[("rider", "Wood")]).expect("xdel seed 1");
        let _: Option<String> = r.xadd("race:italy", "3-0", &[("rider", "Henshaw")]).expect("xdel seed 2");
        if let Ok(res) = r.xrange_count("race:italy", "-", "+", 2) {
            let res: StreamRangeReply = res;
            let view: Vec<_> = res
                .ids
                .iter()
                .map(|entry| {
                    (
                        entry.id.clone(),
                        vec![(
                            "rider".to_string(),
                            entry.get::<String>("rider").expect("missing rider"),
                        )],
                    )
                })
                .collect();
            println!("{view:?}"); // >>> [("2-0", [("rider", "Wood")]), ("3-0", [("rider", "Henshaw")])]
        }

        if let Ok(res) = r.xdel("race:italy", &["3-0"]) {
            let res: usize = res;
            println!("{res}"); // >>> 1
        }

        if let Ok(res) = r.xrange_count("race:italy", "-", "+", 2) {
            let res: StreamRangeReply = res;
            let view: Vec<_> = res
                .ids
                .iter()
                .map(|entry| {
                    (
                        entry.id.clone(),
                        vec![(
                            "rider".to_string(),
                            entry.get::<String>("rider").expect("missing rider"),
                        )],
                    )
                })
                .collect();
            println!("{view:?}"); // >>> [("2-0", [("rider", "Wood")])]
        }
    }
}
mod tests {
    use redis::{
        streams::{
            StreamAutoClaimOptions, StreamInfoConsumersReply, StreamInfoGroupsReply,
            StreamInfoStreamReply, StreamMaxlen, StreamPendingCountReply, StreamPendingReply,
            StreamRangeReply, StreamReadOptions, StreamReadReply, StreamTrimmingMode, StreamTrimOptions,
        },
        AsyncCommands,
    };
    use tokio::time::{sleep, Duration};

    async fn delete_keys(r: &mut redis::aio::MultiplexedConnection, keys: &[&str]) {
        let _: usize = r.del(keys).await.unwrap_or(0);
    }

    async fn add_france_fixed(r: &mut redis::aio::MultiplexedConnection) {
        delete_keys(r, &["race:france"]).await;
        let _: Option<String> = r
            .xadd(
                "race:france",
                "1692632086370-0",
                &[
                    ("rider", "Castilla"),
                    ("speed", "30.2"),
                    ("position", "1"),
                    ("location_id", "1"),
                ],
            )
            .await
            .expect("add france 1");
        let _: Option<String> = r
            .xadd(
                "race:france",
                "1692632094485-0",
                &[
                    ("rider", "Norem"),
                    ("speed", "28.8"),
                    ("position", "3"),
                    ("location_id", "1"),
                ],
            )
            .await
            .expect("add france 2");
        let _: Option<String> = r
            .xadd(
                "race:france",
                "1692632102976-0",
                &[
                    ("rider", "Prickett"),
                    ("speed", "29.7"),
                    ("position", "2"),
                    ("location_id", "1"),
                ],
            )
            .await
            .expect("add france 3");
        let _: Option<String> = r
            .xadd(
                "race:france",
                "1692632147973-0",
                &[
                    ("rider", "Castilla"),
                    ("speed", "29.9"),
                    ("position", "1"),
                    ("location_id", "2"),
                ],
            )
            .await
            .expect("add france 4");
    }

    async fn seed_usa_fixed(r: &mut redis::aio::MultiplexedConnection) {
        delete_keys(r, &["race:usa"]).await;
        let _: Option<String> = r
            .xadd("race:usa", "0-1", &[("racer", "Castilla")])
            .await
            .expect("add usa 1");
        let _: Option<String> = r
            .xadd("race:usa", "0-2", &[("racer", "Norem")])
            .await
            .expect("add usa 2");
    }

    async fn seed_italy_group_base(r: &mut redis::aio::MultiplexedConnection) {
        delete_keys(r, &["race:italy"]).await;
        let _: () = r
            .xgroup_create_mkstream("race:italy", "italy_riders", "$")
            .await
            .expect("create italy group");
        let _: Option<String> = r
            .xadd("race:italy", "1692632639151-0", &[("rider", "Castilla")])
            .await
            .expect("add italy 1");
        let _: Option<String> = r
            .xadd("race:italy", "1692632647899-0", &[("rider", "Royce")])
            .await
            .expect("add italy 2");
        let _: Option<String> = r
            .xadd("race:italy", "1692632662819-0", &[("rider", "Sam-Bodden")])
            .await
            .expect("add italy 3");
        let _: Option<String> = r
            .xadd("race:italy", "1692632670501-0", &[("rider", "Prickett")])
            .await
            .expect("add italy 4");
        let _: Option<String> = r
            .xadd("race:italy", "1692632678249-0", &[("rider", "Norem")])
            .await
            .expect("add italy 5");
    }

    async fn seed_italy_alice_pending(r: &mut redis::aio::MultiplexedConnection) {
        seed_italy_group_base(r).await;
        let opts = StreamReadOptions::default().group("italy_riders", "Alice").count(1);
        let _: Option<StreamReadReply> = r
            .xread_options(&["race:italy"], &[">"], &opts)
            .await
            .expect("alice read pending");
    }

    async fn seed_italy_after_ack(r: &mut redis::aio::MultiplexedConnection) {
        seed_italy_alice_pending(r).await;
        let _: usize = r
            .xack("race:italy", "italy_riders", &["1692632639151-0"])
            .await
            .expect("ack first italy message");
    }

    async fn seed_italy_bob_pending(r: &mut redis::aio::MultiplexedConnection) {
        seed_italy_after_ack(r).await;
        let opts = StreamReadOptions::default().group("italy_riders", "Bob").count(2);
        let _: Option<StreamReadReply> = r
            .xread_options(&["race:italy"], &[">"], &opts)
            .await
            .expect("bob read pending");
    }

    async fn seed_italy_info_state(r: &mut redis::aio::MultiplexedConnection) {
        seed_italy_bob_pending(r).await;
        sleep(Duration::from_millis(5)).await;
        let _: redis::streams::StreamClaimReply = r
            .xclaim("race:italy", "italy_riders", "Alice", 1, &["1692632647899-0"])
            .await
            .expect("alice claim");
        sleep(Duration::from_millis(5)).await;
        let _: redis::streams::StreamClaimReply = r
            .xclaim("race:italy", "italy_riders", "Lora", 1, &["1692632662819-0"])
            .await
            .expect("lora claim");
    }

    async fn seed_trim_stream(r: &mut redis::aio::MultiplexedConnection) {
        delete_keys(r, &["mystream"]).await;
        for id in ["1-0", "2-0", "3-0", "4-0", "5-0", "6-0", "7-0", "8-0", "9-0", "10-0"] {
            let _: Option<String> = r
                .xadd("mystream", id, &[("field", "value")])
                .await
                .expect("seed mystream");
        }
    }

    async fn run() {
        let mut r = match redis::Client::open("redis://127.0.0.1") {
            Ok(client) => match client.get_multiplexed_async_connection().await {
                Ok(conn) => conn,
                Err(e) => {
                    println!("Failed to connect to Redis: {e}");
                    return;
                }
            },
            Err(e) => {
                println!("Failed to create Redis client: {e}");
                return;
            }
        };

        let res1: Option<String> = r
            .xadd(
                "race:france",
                "*",
                &[
                    ("rider", "Castilla"),
                    ("speed", "30.2"),
                    ("position", "1"),
                    ("location_id", "1"),
                ],
            )
            .await
            .expect("xadd 1");
        let res1 = res1.expect("missing stream id");
        println!("{res1}"); // >>> 1692632086370-0

        let res2: Option<String> = r
            .xadd(
                "race:france",
                "*",
                &[
                    ("rider", "Norem"),
                    ("speed", "28.8"),
                    ("position", "3"),
                    ("location_id", "1"),
                ],
            )
            .await
            .expect("xadd 2");
        let res2 = res2.expect("missing stream id");
        println!("{res2}"); // >>> 1692632094485-0

        let res3: Option<String> = r
            .xadd(
                "race:france",
                "*",
                &[
                    ("rider", "Prickett"),
                    ("speed", "29.7"),
                    ("position", "2"),
                    ("location_id", "1"),
                ],
            )
            .await
            .expect("xadd 3");
        let res3 = res3.expect("missing stream id");
        println!("{res3}"); // >>> 1692632102976-0

        add_france_fixed(&mut r).await;
        if let Ok(res) = r.xrange_count("race:france", "1692632086370-0", "+", 2).await {
            let res: StreamRangeReply = res;
            let view: Vec<_> = res
                .ids
                .iter()
                .map(|entry| {
                    (
                        entry.id.clone(),
                        vec![
                            ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                            ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                            ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                            (
                                "location_id".to_string(),
                                entry.get::<String>("location_id").expect("missing location_id"),
                            ),
                        ],
                    )
                })
                .collect();
            println!("{view:?}");
            // >>> [("1692632086370-0", [("rider", "Castilla"), ("speed", "30.2"), ("position", "1"), ("location_id", "1")]), ("1692632094485-0", [("rider", "Norem"), ("speed", "28.8"), ("position", "3"), ("location_id", "1")])]
        }

        add_france_fixed(&mut r).await;
        let opts = StreamReadOptions::default().count(100).block(300);
        if let Ok(res) = r.xread_options(&["race:france"], &["$"], &opts).await {
            let res: Option<StreamReadReply> = res;
            println!("{res:?}"); // >>> None
        }

        if let Ok(res) = r
            .xadd(
                "race:france",
                "*",
                &[
                    ("rider", "Castilla"),
                    ("speed", "29.9"),
                    ("position", "1"),
                    ("location_id", "2"),
                ],
            )
            .await
        {
            let res: Option<String> = res;
            let res = res.expect("missing stream id");
            println!("{res}"); // >>> 1692632147973-0
        }

        add_france_fixed(&mut r).await;
        if let Ok(res) = r.xlen("race:france").await {
            let res: usize = res;
            println!("{res}"); // >>> 4
        }

        delete_keys(&mut r, &["race:usa"]).await;
        if let Ok(res) = r.xadd("race:usa", "0-1", &[("racer", "Castilla")]).await {
            let res: Option<String> = res;
            let res = res.expect("missing stream id");
            println!("{res}"); // >>> 0-1
        }
        if let Ok(res) = r.xadd("race:usa", "0-2", &[("racer", "Norem")]).await {
            let res: Option<String> = res;
            let res = res.expect("missing stream id");
            println!("{res}"); // >>> 0-2
        }

        let res: redis::RedisResult<Option<String>> =
            r.xadd("race:usa", "0-1", &[("racer", "Prickett")]).await;
        match res {
            Ok(_) => {}
            Err(e) => {
                let msg = e.to_string();
                println!("{msg}");
                // >>> An error was signalled by the server - ResponseError: The ID specified in XADD is equal or smaller than the target stream top item
            }
        }

        seed_usa_fixed(&mut r).await;
        if let Ok(res) = r.xadd("race:usa", "0-*", &[("racer", "Prickett")]).await {
            let res: Option<String> = res;
            let res = res.expect("missing stream id");
            println!("{res}"); // >>> 0-3
        }

        add_france_fixed(&mut r).await;
        if let Ok(res) = r.xrange_all("race:france").await {
            let res: StreamRangeReply = res;
            let view: Vec<_> = res
                .ids
                .iter()
                .map(|entry| {
                    (
                        entry.id.clone(),
                        vec![
                            ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                            ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                            ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                            (
                                "location_id".to_string(),
                                entry.get::<String>("location_id").expect("missing location_id"),
                            ),
                        ],
                    )
                })
                .collect();
            println!("{view:?}");
            // >>> [("1692632086370-0", [("rider", "Castilla"), ("speed", "30.2"), ("position", "1"), ("location_id", "1")]), ("1692632094485-0", [("rider", "Norem"), ("speed", "28.8"), ("position", "3"), ("location_id", "1")]), ("1692632102976-0", [("rider", "Prickett"), ("speed", "29.7"), ("position", "2"), ("location_id", "1")]), ("1692632147973-0", [("rider", "Castilla"), ("speed", "29.9"), ("position", "1"), ("location_id", "2")])]
        }

        add_france_fixed(&mut r).await;
        if let Ok(res) = r.xrange("race:france", "1692632086369", "1692632086371").await {
            let res: StreamRangeReply = res;
            let view: Vec<_> = res
                .ids
                .iter()
                .map(|entry| {
                    (
                        entry.id.clone(),
                        vec![
                            ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                            ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                            ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                            (
                                "location_id".to_string(),
                                entry.get::<String>("location_id").expect("missing location_id"),
                            ),
                        ],
                    )
                })
                .collect();
            println!("{view:?}");
            // >>> [("1692632086370-0", [("rider", "Castilla"), ("speed", "30.2"), ("position", "1"), ("location_id", "1")])]
        }

        add_france_fixed(&mut r).await;
        if let Ok(res) = r.xrange_count("race:france", "-", "+", 2).await {
            let res: StreamRangeReply = res;
            let view: Vec<_> = res
                .ids
                .iter()
                .map(|entry| {
                    (
                        entry.id.clone(),
                        vec![
                            ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                            ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                            ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                            (
                                "location_id".to_string(),
                                entry.get::<String>("location_id").expect("missing location_id"),
                            ),
                        ],
                    )
                })
                .collect();
            println!("{view:?}");
            // >>> [("1692632086370-0", [("rider", "Castilla"), ("speed", "30.2"), ("position", "1"), ("location_id", "1")]), ("1692632094485-0", [("rider", "Norem"), ("speed", "28.8"), ("position", "3"), ("location_id", "1")])]
        }

        add_france_fixed(&mut r).await;
        if let Ok(res) = r.xrange_count("race:france", "(1692632094485-0", "+", 2).await {
            let res: StreamRangeReply = res;
            let view: Vec<_> = res
                .ids
                .iter()
                .map(|entry| {
                    (
                        entry.id.clone(),
                        vec![
                            ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                            ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                            ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                            (
                                "location_id".to_string(),
                                entry.get::<String>("location_id").expect("missing location_id"),
                            ),
                        ],
                    )
                })
                .collect();
            println!("{view:?}");
            // >>> [("1692632102976-0", [("rider", "Prickett"), ("speed", "29.7"), ("position", "2"), ("location_id", "1")]), ("1692632147973-0", [("rider", "Castilla"), ("speed", "29.9"), ("position", "1"), ("location_id", "2")])]
        }

        add_france_fixed(&mut r).await;
        if let Ok(res) = r.xrange_count("race:france", "(1692632147973-0", "+", 2).await {
            let res: StreamRangeReply = res;
            let view: Vec<_> = res
                .ids
                .iter()
                .map(|entry| {
                    (
                        entry.id.clone(),
                        vec![
                            ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                            ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                            ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                            (
                                "location_id".to_string(),
                                entry.get::<String>("location_id").expect("missing location_id"),
                            ),
                        ],
                    )
                })
                .collect();
            println!("{view:?}"); // >>> []
        }

        add_france_fixed(&mut r).await;
        if let Ok(res) = r.xrevrange_count("race:france", "+", "-", 1).await {
            let res: StreamRangeReply = res;
            let view: Vec<_> = res
                .ids
                .iter()
                .map(|entry| {
                    (
                        entry.id.clone(),
                        vec![
                            ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                            ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                            ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                            (
                                "location_id".to_string(),
                                entry.get::<String>("location_id").expect("missing location_id"),
                            ),
                        ],
                    )
                })
                .collect();
            println!("{view:?}");
            // >>> [("1692632147973-0", [("rider", "Castilla"), ("speed", "29.9"), ("position", "1"), ("location_id", "2")])]
        }

        add_france_fixed(&mut r).await;
        let opts = StreamReadOptions::default().count(2);
        if let Ok(res) = r.xread_options(&["race:france"], &["0"], &opts).await {
            let res: Option<StreamReadReply> = res;
            let view: Vec<_> = res
                .expect("xread should return data")
                .keys
                .iter()
                .map(|stream| {
                    (
                        stream.key.clone(),
                        stream
                            .ids
                            .iter()
                            .map(|entry| {
                                (
                                    entry.id.clone(),
                                    vec![
                                        ("rider".to_string(), entry.get::<String>("rider").expect("missing rider")),
                                        ("speed".to_string(), entry.get::<String>("speed").expect("missing speed")),
                                        ("position".to_string(), entry.get::<String>("position").expect("missing position")),
                                        (
                                            "location_id".to_string(),
                                            entry.get::<String>("location_id").expect("missing location_id"),
                                        ),
                                    ],
                                )
                            })
                            .collect::<Vec<_>>(),
                    )
                })
                .collect();
            println!("{view:?}");
            // >>> [("race:france", [("1692632086370-0", [("rider", "Castilla"), ("speed", "30.2"), ("position", "1"), ("location_id", "1")]), ("1692632094485-0", [("rider", "Norem"), ("speed", "28.8"), ("position", "3"), ("location_id", "1")])])]
        }

        add_france_fixed(&mut r).await;
        if let Ok(res) = r.xgroup_create("race:france", "france_riders", "$").await {
            let res: () = res;
            let _ = res;
            println!("OK"); // >>> OK
        }

        delete_keys(&mut r, &["race:italy"]).await;
        if let Ok(res) = r.xgroup_create_mkstream("race:italy", "italy_riders", "$").await {
            let res: () = res;
            let _ = res;
            println!("OK"); // >>> OK
        }

        delete_keys(&mut r, &["race:italy"]).await;
        let _: () = r
            .xgroup_create_mkstream("race:italy", "italy_riders", "$")
            .await
            .expect("create italy group");
        let italy_1: Option<String> = r
            .xadd("race:italy", "1692632639151-0", &[("rider", "Castilla")])
            .await
            .expect("italy1");
        let italy_1 = italy_1.expect("missing stream id");
        println!("{italy_1}"); // >>> 1692632639151-0
        let italy_2: Option<String> = r
            .xadd("race:italy", "1692632647899-0", &[("rider", "Royce")])
            .await
            .expect("italy2");
        let italy_2 = italy_2.expect("missing stream id");
        println!("{italy_2}"); // >>> 1692632647899-0
        let italy_3: Option<String> = r
            .xadd("race:italy", "1692632662819-0", &[("rider", "Sam-Bodden")])
            .await
            .expect("italy3");
        let italy_3 = italy_3.expect("missing stream id");
        println!("{italy_3}"); // >>> 1692632662819-0
        let italy_4: Option<String> = r
            .xadd("race:italy", "1692632670501-0", &[("rider", "Prickett")])
            .await
            .expect("italy4");
        let italy_4 = italy_4.expect("missing stream id");
        println!("{italy_4}"); // >>> 1692632670501-0
        let italy_5: Option<String> = r
            .xadd("race:italy", "1692632678249-0", &[("rider", "Norem")])
            .await
            .expect("italy5");
        let italy_5 = italy_5.expect("missing stream id");
        println!("{italy_5}"); // >>> 1692632678249-0
        let opts = StreamReadOptions::default().group("italy_riders", "Alice").count(1);
        if let Ok(res) = r.xread_options(&["race:italy"], &[">"], &opts).await {
            let res: Option<StreamReadReply> = res;
            let view: Vec<_> = res
                .expect("xgroup read should return data")
                .keys
                .iter()
                .map(|stream| {
                    (
                        stream.key.clone(),
                        stream
                            .ids
                            .iter()
                            .map(|entry| {
                                (
                                    entry.id.clone(),
                                    vec![(
                                        "rider".to_string(),
                                        entry.get::<String>("rider").expect("missing rider"),
                                    )],
                                )
                            })
                            .collect::<Vec<_>>(),
                    )
                })
                .collect();
            println!("{view:?}");
            // >>> [("race:italy", [("1692632639151-0", [("rider", "Castilla")])])]
        }

        seed_italy_alice_pending(&mut r).await;
        let opts = StreamReadOptions::default().group("italy_riders", "Alice");
        if let Ok(res) = r.xread_options(&["race:italy"], &["0"], &opts).await {
            let res: Option<StreamReadReply> = res;
            let view: Vec<_> = res
                .expect("xgroup history")
                .keys
                .iter()
                .map(|stream| {
                    (
                        stream.key.clone(),
                        stream
                            .ids
                            .iter()
                            .map(|entry| {
                                (
                                    entry.id.clone(),
                                    vec![(
                                        "rider".to_string(),
                                        entry.get::<String>("rider").expect("missing rider"),
                                    )],
                                )
                            })
                            .collect::<Vec<_>>(),
                    )
                })
                .collect();
            println!("{view:?}");
            // >>> [("race:italy", [("1692632639151-0", [("rider", "Castilla")])])]
        }

        seed_italy_alice_pending(&mut r).await;
        if let Ok(res) = r.xack("race:italy", "italy_riders", &["1692632639151-0"]).await {
            let res: usize = res;
            println!("{res}"); // >>> 1
        }
        let opts = StreamReadOptions::default().group("italy_riders", "Alice");
        if let Ok(res) = r.xread_options(&["race:italy"], &["0"], &opts).await {
            let res: Option<StreamReadReply> = res;
            let view: Vec<_> = res
                .expect("xgroup history")
                .keys
                .iter()
                .map(|stream| {
                    (
                        stream.key.clone(),
                        stream
                            .ids
                            .iter()
                            .map(|entry| {
                                (
                                    entry.id.clone(),
                                    vec![(
                                        "rider".to_string(),
                                        entry.get::<String>("rider").expect("missing rider"),
                                    )],
                                )
                            })
                            .collect::<Vec<_>>(),
                    )
                })
                .collect();
            println!("{view:?}"); // >>> [("race:italy", [])]
        }

        seed_italy_after_ack(&mut r).await;
        let opts = StreamReadOptions::default().group("italy_riders", "Bob").count(2);
        if let Ok(res) = r.xread_options(&["race:italy"], &[">"], &opts).await {
            let res: Option<StreamReadReply> = res;
            let view: Vec<_> = res
                .expect("bob should receive data")
                .keys
                .iter()
                .map(|stream| {
                    (
                        stream.key.clone(),
                        stream
                            .ids
                            .iter()
                            .map(|entry| {
                                (
                                    entry.id.clone(),
                                    vec![(
                                        "rider".to_string(),
                                        entry.get::<String>("rider").expect("missing rider"),
                                    )],
                                )
                            })
                            .collect::<Vec<_>>(),
                    )
                })
                .collect();
            println!("{view:?}");
            // >>> [("race:italy", [("1692632647899-0", [("rider", "Royce")]), ("1692632662819-0", [("rider", "Sam-Bodden")])])]
        }

        seed_italy_bob_pending(&mut r).await;
        if let Ok(res) = r.xpending("race:italy", "italy_riders").await {
            let res: StreamPendingReply = res;
            let view = match res {
                StreamPendingReply::Empty => None,
                StreamPendingReply::Data(data) => Some((
                    data.count,
                    data.start_id.clone(),
                    data.end_id.clone(),
                    data.consumers
                        .iter()
                        .map(|consumer| (consumer.name.clone(), consumer.pending))
                        .collect::<Vec<_>>(),
                )),
            }
            .expect("pending summary");
            println!("{view:?}");
            // >>> (2, "1692632647899-0", "1692632662819-0", [("Bob", 2)])
        }

        seed_italy_bob_pending(&mut r).await;
        sleep(Duration::from_millis(5)).await;
        if let Ok(res) = r.xpending_count("race:italy", "italy_riders", "-", "+", 10).await {
            let res: StreamPendingCountReply = res;
            let view: Vec<_> = res
                .ids
                .iter()
                .map(|entry| {
                    (
                        entry.id.clone(),
                        entry.consumer.clone(),
                        entry.last_delivered_ms,
                        entry.times_delivered,
                    )
                })
                .collect();
            println!("{view:?}");
            // >>> [("1692632647899-0", "Bob", 5, 1), ("1692632662819-0", "Bob", 5, 1)]
        }

        seed_italy_bob_pending(&mut r).await;
        if let Ok(res) = r.xrange("race:italy", "1692632647899-0", "1692632647899-0").await {
            let res: StreamRangeReply = res;
            let view: Vec<_> = res
                .ids
                .iter()
                .map(|entry| {
                    (
                        entry.id.clone(),
                        vec![(
                            "rider".to_string(),
                            entry.get::<String>("rider").expect("missing rider"),
                        )],
                    )
                })
                .collect();
            println!("{view:?}"); // >>> [("1692632647899-0", [("rider", "Royce")])]
        }

        seed_italy_bob_pending(&mut r).await;
        sleep(Duration::from_millis(5)).await;
        if let Ok(res) = r
            .xclaim("race:italy", "italy_riders", "Alice", 1, &["1692632647899-0"])
            .await
        {
            let res: redis::streams::StreamClaimReply = res;
            let view: Vec<_> = res
                .ids
                .iter()
                .map(|entry| {
                    (
                        entry.id.clone(),
                        vec![(
                            "rider".to_string(),
                            entry.get::<String>("rider").expect("missing rider"),
                        )],
                    )
                })
                .collect();
            println!("{view:?}"); // >>> [("1692632647899-0", [("rider", "Royce")])]
        }

        seed_italy_bob_pending(&mut r).await;
        sleep(Duration::from_millis(5)).await;
        let opts = StreamAutoClaimOptions::default().count(1);
        if let Ok(res) = r
            .xautoclaim_options("race:italy", "italy_riders", "Alice", 1, "0-0", opts)
            .await
        {
            let res: redis::streams::StreamAutoClaimReply = res;
            let claimed: Vec<_> = res
                .claimed
                .iter()
                .map(|entry| {
                    (
                        entry.id.clone(),
                        vec![(
                            "rider".to_string(),
                            entry.get::<String>("rider").expect("missing rider"),
                        )],
                    )
                })
                .collect();
            println!("{:?}", (res.next_stream_id.clone(), &claimed));
            // >>> ("1692632662819-0", [("1692632647899-0", [("rider", "Royce")])])
        }

        seed_italy_bob_pending(&mut r).await;
        sleep(Duration::from_millis(5)).await;
        let first_opts = StreamAutoClaimOptions::default().count(1);
        let _: redis::streams::StreamAutoClaimReply = r
            .xautoclaim_options("race:italy", "italy_riders", "Alice", 1, "0-0", first_opts)
            .await
            .expect("first autoclaim");
        let next_opts = StreamAutoClaimOptions::default().count(1);
        if let Ok(res) = r
            .xautoclaim_options(
                "race:italy",
                "italy_riders",
                "Lora",
                1,
                "(1692632647899-0",
                next_opts,
            )
            .await
        {
            let res: redis::streams::StreamAutoClaimReply = res;
            let claimed: Vec<_> = res
                .claimed
                .iter()
                .map(|entry| {
                    (
                        entry.id.clone(),
                        vec![(
                            "rider".to_string(),
                            entry.get::<String>("rider").expect("missing rider"),
                        )],
                    )
                })
                .collect();
            println!("{:?}", (res.next_stream_id.clone(), &claimed));
            // >>> ("0-0", [("1692632662819-0", [("rider", "Sam-Bodden")])])
        }

        seed_italy_info_state(&mut r).await;
        if let Ok(res) = r.xinfo_stream("race:italy").await {
            let res: StreamInfoStreamReply = res;
            let view = (
                res.length,
                res.radix_tree_keys,
                res.groups,
                res.last_generated_id.clone(),
                res.first_entry.id.clone(),
                res.last_entry.id.clone(),
            );
            println!("{view:?}");
            // >>> (5, 1, 1, "1692632678249-0", "1692632639151-0", "1692632678249-0")
        }

        seed_italy_info_state(&mut r).await;
        if let Ok(res) = r.xinfo_groups("race:italy").await {
            let res: StreamInfoGroupsReply = res;
            let view: Vec<_> = res
                .groups
                .iter()
                .map(|group| {
                    (
                        group.name.clone(),
                        group.consumers,
                        group.pending,
                        group.last_delivered_id.clone(),
                    )
                })
                .collect();
            println!("{view:?}");
            // >>> [("italy_riders", 3, 2, "1692632662819-0")]
        }

        seed_italy_info_state(&mut r).await;
        if let Ok(res) = r.xinfo_consumers("race:italy", "italy_riders").await {
            let res: StreamInfoConsumersReply = res;
            let mut view: Vec<_> = res
                .consumers
                .iter()
                .map(|consumer| (consumer.name.clone(), consumer.pending, consumer.idle))
                .collect();
            view.sort_by(|a, b| a.0.cmp(&b.0));
            println!("{view:?}");
            // >>> [("Alice", 1, 5), ("Bob", 0, 5), ("Lora", 1, 5)]
        }

        delete_keys(&mut r, &["race:italy"]).await;
        let max1: Option<String> = r
            .xadd_maxlen("race:italy", StreamMaxlen::Equals(2), "1-0", &[("rider", "Jones")])
            .await
            .expect("maxlen add 1");
        let max1 = max1.expect("missing stream id");
        println!("{max1}"); // >>> 1-0
        let max2: Option<String> = r
            .xadd_maxlen("race:italy", StreamMaxlen::Equals(2), "2-0", &[("rider", "Wood")])
            .await
            .expect("maxlen add 2");
        let max2 = max2.expect("missing stream id");
        println!("{max2}"); // >>> 2-0
        let max3: Option<String> = r
            .xadd_maxlen("race:italy", StreamMaxlen::Equals(2), "3-0", &[("rider", "Henshaw")])
            .await
            .expect("maxlen add 3");
        let max3 = max3.expect("missing stream id");
        println!("{max3}"); // >>> 3-0
        if let Ok(res) = r.xlen("race:italy").await {
            let res: usize = res;
            println!("{res}"); // >>> 2
        }
        if let Ok(res) = r.xrange_all("race:italy").await {
            let res: StreamRangeReply = res;
            let view: Vec<_> = res
                .ids
                .iter()
                .map(|entry| {
                    (
                        entry.id.clone(),
                        vec![(
                            "rider".to_string(),
                            entry.get::<String>("rider").expect("missing rider"),
                        )],
                    )
                })
                .collect();
            println!("{view:?}");
            // >>> [("2-0", [("rider", "Wood")]), ("3-0", [("rider", "Henshaw")])]
        }

        delete_keys(&mut r, &["race:italy"]).await;
        let _: Option<String> = r.xadd("race:italy", "1-0", &[("rider", "Wood")]).await.expect("trim seed 1");
        let _: Option<String> = r.xadd("race:italy", "2-0", &[("rider", "Henshaw")]).await.expect("trim seed 2");
        if let Ok(res) = r.xtrim("race:italy", StreamMaxlen::Equals(10)).await {
            let res: usize = res;
            println!("{res}"); // >>> 0
        }

        seed_trim_stream(&mut r).await;
        if let Ok(res) = r
            .xtrim_options(
                "mystream",
                &StreamTrimOptions::maxlen(StreamTrimmingMode::Approx, 10),
            )
            .await
        {
            let res: usize = res;
            println!("{res}"); // >>> 0
        }

        delete_keys(&mut r, &["race:italy"]).await;
        let _: Option<String> = r.xadd("race:italy", "2-0", &[("rider", "Wood")]).await.expect("xdel seed 1");
        let _: Option<String> = r.xadd("race:italy", "3-0", &[("rider", "Henshaw")]).await.expect("xdel seed 2");
        if let Ok(res) = r.xrange_count("race:italy", "-", "+", 2).await {
            let res: StreamRangeReply = res;
            let view: Vec<_> = res
                .ids
                .iter()
                .map(|entry| {
                    (
                        entry.id.clone(),
                        vec![(
                            "rider".to_string(),
                            entry.get::<String>("rider").expect("missing rider"),
                        )],
                    )
                })
                .collect();
            println!("{view:?}"); // >>> [("2-0", [("rider", "Wood")]), ("3-0", [("rider", "Henshaw")])]
        }
        if let Ok(res) = r.xdel("race:italy", &["3-0"]).await {
            let res: usize = res;
            println!("{res}"); // >>> 1
        }
        if let Ok(res) = r.xrange_count("race:italy", "-", "+", 2).await {
            let res: StreamRangeReply = res;
            let view: Vec<_> = res
                .ids
                .iter()
                .map(|entry| {
                    (
                        entry.id.clone(),
                        vec![(
                            "rider".to_string(),
                            entry.get::<String>("rider").expect("missing rider"),
                        )],
                    )
                })
                .collect();
            println!("{view:?}"); // >>> [("2-0", [("rider", "Wood")])]
        }
    }
}

The above call to the XADD command adds an entry rider: Castilla, speed: 29.9, position: 1, location_id: 2 to the stream at key race:france, using an auto-generated entry ID, which is the one returned by the command, specifically 1692632147973-0. It gets as its first argument the key name race:france, the second argument is the entry ID that identifies every entry inside a stream. However, in this case, we passed * because we want the server to generate a new ID for us. Every new ID will be monotonically increasing, so in more simple terms, every new entry added will have a higher ID compared to all the past entries. Auto-generation of IDs by the server is almost always what you want, and the reasons for specifying an ID explicitly are very rare. We'll talk more about this later. The fact that each Stream entry has an ID is another similarity with log files, where line numbers, or the byte offset inside the file, can be used in order to identify a given entry. Returning back at our XADD example, after the key name and ID, the next arguments are the field-value pairs composing our stream entry.

It is possible to get the number of items inside a Stream just using the XLEN command:

Foundational: Get the total number of entries in a stream using XLEN
> XLEN race:france
(integer) 4
"""
Code samples for Stream doc pages:
    https://redis.io/docs/latest/develop/data-types/streams/
"""

import redis

r = redis.Redis(decode_responses=True)

res1 = r.xadd(
    "race:france",
    {"rider": "Castilla", "speed": 30.2, "position": 1, "location_id": 1},
)
print(res1)  # >>> 1692629576966-0

res2 = r.xadd(
    "race:france",
    {"rider": "Norem", "speed": 28.8, "position": 3, "location_id": 1},
)
print(res2)  # >>> 1692629594113-0

res3 = r.xadd(
    "race:france",
    {"rider": "Prickett", "speed": 29.7, "position": 2, "location_id": 1},
)
print(res3)  # >>> 1692629613374-0


res4 = r.xrange("race:france", "1691765278160-0", "+", 2)
print(
    res4
)  # >>> [
#   ('1692629576966-0',
#       {'rider': 'Castilla', 'speed': '30.2', 'position': '1', 'location_id': '1'}
#   ),
#   ('1692629594113-0',
#       {'rider': 'Norem', 'speed': '28.8', 'position': '3', 'location_id': '1'}
#   )
# ]

res5 = r.xread(streams={"race:france": 0}, count=100, block=300)
print(
    res5
)
# >>> [
#   ['race:france',
#       [('1692629576966-0',
#           {'rider': 'Castilla', 'speed': '30.2', 'position': '1', 'location_id': '1'}
#       ),
#       ('1692629594113-0',
#           {'rider': 'Norem', 'speed': '28.8', 'position': '3', 'location_id': '1'}
#       ),
#       ('1692629613374-0',
#           {'rider': 'Prickett', 'speed': '29.7', 'position': '2', 'location_id': '1'}
#       )]
# ]
# ]

res6 = r.xadd(
    "race:france",
    {"rider": "Castilla", "speed": 29.9, "position": 1, "location_id": 2},
)
print(res6)  # >>> 1692629676124-0

res7 = r.xlen("race:france")
print(res7)  # >>> 4


res8 = r.xadd("race:usa", {"racer": "Castilla"}, id="0-1")
print(res8)  # >>> 0-1

res9 = r.xadd("race:usa", {"racer": "Norem"}, id="0-2")
print(res9)  # >>> 0-2

try:
    res10 = r.xadd("race:usa", {"racer": "Prickett"}, id="0-1")
    print(res10)  # >>> 0-1
except redis.exceptions.ResponseError as e:
    print(e)  # >>> WRONGID

# Not yet implemented

res11 = r.xrange("race:france", "-", "+")
print(
    res11
)
# >>> [
#   ('1692629576966-0',
#       {'rider': 'Castilla', 'speed': '30.2', 'position': '1', 'location_id': '1'}
#   ),
#   ('1692629594113-0',
#       {'rider': 'Norem', 'speed': '28.8', 'position': '3', 'location_id': '1'}
#   ),
#   ('1692629613374-0',
#       {'rider': 'Prickett', 'speed': '29.7', 'position': '2', 'location_id': '1'}
#   ),
#   ('1692629676124-0',
#       {'rider': 'Castilla', 'speed': '29.9', 'position': '1', 'location_id': '2'}
#   )
# ]

res12 = r.xrange("race:france", 1692629576965, 1692629576967)
print(
    res12
)
# >>> [
#       ('1692629576966-0',
#           {'rider': 'Castilla', 'speed': '30.2', 'position': '1', 'location_id': '1'}
#       )
# ]

res13 = r.xrange("race:france", "-", "+", 2)
print(
    res13
)
# >>> [
#   ('1692629576966-0',
#       {'rider': 'Castilla', 'speed': '30.2', 'position': '1', 'location_id': '1'}
#   ),
#   ('1692629594113-0',
#       {'rider': 'Norem', 'speed': '28.8', 'position': '3', 'location_id': '1'}
#   )
# ]

res14 = r.xrange("race:france", "(1692629594113-0", "+", 2)
print(
    res14
)
# >>> [
#   ('1692629613374-0',
#       {'rider': 'Prickett', 'speed': '29.7', 'position': '2', 'location_id': '1'}
#   ),
#   ('1692629676124-0',
#       {'rider': 'Castilla', 'speed': '29.9', 'position': '1', 'location_id': '2'}
#   )
# ]

res15 = r.xrange("race:france", "(1692629676124-0", "+", 2)
print(res15)  # >>> []

res16 = r.xrevrange("race:france", "+", "-", 1)
print(
    res16
)
# >>> [
#       ('1692629676124-0',
#           {'rider': 'Castilla', 'speed': '29.9', 'position': '1', 'location_id': '2'}
#       )
# ]

res17 = r.xread(streams={"race:france": 0}, count=2)
print(
    res17
)
# >>> [
#       ['race:france', [
#       ('1692629576966-0',
#           {'rider': 'Castilla', 'speed': '30.2', 'position': '1', 'location_id': '1'}
#       ),
#       ('1692629594113-0',
#           {'rider': 'Norem', 'speed': '28.8', 'position': '3', 'location_id': '1'}
#       )
#       ]
#       ]
#   ]

res18 = r.xgroup_create("race:france", "france_riders", "$")
print(res18)  # >>> True

res19 = r.xgroup_create("race:italy", "italy_riders", "$", mkstream=True)
print(res19)  # >>> True

r.xadd("race:italy", {"rider": "Castilla"})
r.xadd("race:italy", {"rider": "Royce"})
r.xadd("race:italy", {"rider": "Sam-Bodden"})
r.xadd("race:italy", {"rider": "Prickett"})
r.xadd("race:italy", {"rider": "Norem"})

res20 = r.xreadgroup(
    streams={"race:italy": ">"},
    consumername="Alice",
    groupname="italy_riders",
    count=1,
)
print(res20)  # >>> [['race:italy', [('1692629925771-0', {'rider': 'Castilla'})]]]

res21 = r.xreadgroup(
    streams={"race:italy": 0},
    consumername="Alice",
    groupname="italy_riders",
    count=1,
)
print(res21)  # >>> [['race:italy', [('1692629925771-0', {'rider': 'Castilla'})]]]

res22 = r.xack("race:italy", "italy_riders", "1692629925771-0")
print(res22)  # >>> 1

res23 = r.xreadgroup(
    streams={"race:italy": 0},
    consumername="Alice",
    groupname="italy_riders",
    count=1,
)
print(res23)  # >>> [['race:italy', []]]

res24 = r.xreadgroup(
    streams={"race:italy": ">"},
    consumername="Bob",
    groupname="italy_riders",
    count=2,
)
print(
    res24
)
# >>> [
#       ['race:italy', [
#           ('1692629925789-0',
#               {'rider': 'Royce'}
#           ),
#           ('1692629925790-0',
#               {'rider': 'Sam-Bodden'}
#           )
#       ]
#       ]
# ]

res25 = r.xpending("race:italy", "italy_riders")
print(
    res25
)
# >>> {
#       'pending': 2, 'min': '1692629925789-0', 'max': '1692629925790-0',
#       'consumers': [{'name': 'Bob', 'pending': 2}]
# }

res26 = r.xpending_range("race:italy", "italy_riders", "-", "+", 10)
print(
    res26
)
# >>> [
#       {
#           'message_id': '1692629925789-0', 'consumer': 'Bob',
#           'time_since_delivered': 31084, 'times_delivered': 1
#       },
#       {
#           'message_id': '1692629925790-0', 'consumer': 'Bob',
#           'time_since_delivered': 31084, 'times_delivered': 1
#       }
# ]

res27 = r.xrange("race:italy", "1692629925789-0", "1692629925789-0")
print(res27)  # >>> [('1692629925789-0', {'rider': 'Royce'})]

res28 = r.xclaim("race:italy", "italy_riders", "Alice", 60000, ["1692629925789-0"])
print(res28)  # >>> [('1692629925789-0', {'rider': 'Royce'})]

res29 = r.xautoclaim("race:italy", "italy_riders", "Alice", 1, "0-0", 1)
print(res29)  # >>> ['1692629925790-0', [('1692629925789-0', {'rider': 'Royce'})]]

res30 = r.xautoclaim("race:italy", "italy_riders", "Alice", 1, "(1692629925789-0", 1)
print(res30)  # >>> ['0-0', [('1692629925790-0', {'rider': 'Sam-Bodden'})]]

res31 = r.xinfo_stream("race:italy")
print(
    res31
)
# >>> {
#       'length': 5, 'radix-tree-keys': 1, 'radix-tree-nodes': 2,
#       'last-generated-id': '1692629926436-0', 'groups': 1,
#       'first-entry': ('1692629925771-0', {'rider': 'Castilla'}),
#       'last-entry': ('1692629926436-0', {'rider': 'Norem'})
# }

res32 = r.xinfo_groups("race:italy")
print(
    res32
)
# >>> [
#       {
#           'name': 'italy_riders', 'consumers': 2, 'pending': 2,
#           'last-delivered-id': '1692629925790-0'
#       }
# ]

res33 = r.xinfo_consumers("race:italy", "italy_riders")
print(
    res33
)
# >>> [
#       {'name': 'Alice', 'pending': 2, 'idle': 199332},
#       {'name': 'Bob', 'pending': 0, 'idle': 489170}
# ]

r.xadd("race:italy", {"rider": "Jones"}, maxlen=2)
r.xadd("race:italy", {"rider": "Wood"}, maxlen=2)
r.xadd("race:italy", {"rider": "Henshaw"}, maxlen=2)

res34 = r.xlen("race:italy")
print(res34)  # >>> 8

res35 = r.xrange("race:italy", "-", "+")
print(
    res35
)
# >>> [
#       ('1692629925771-0', {'rider': 'Castilla'}),
#       ('1692629925789-0', {'rider': 'Royce'}),
#       ('1692629925790-0', {'rider': 'Sam-Bodden'}),
#       ('1692629925791-0', {'rider': 'Prickett'}),
#       ('1692629926436-0', {'rider': 'Norem'}),
#       ('1692630612602-0', {'rider': 'Jones'}),
#       ('1692630641947-0', {'rider': 'Wood'}),
#       ('1692630648281-0', {'rider': 'Henshaw'})
# ]

r.xadd("race:italy", {"rider": "Smith"}, maxlen=2, approximate=False)

res36 = r.xrange("race:italy", "-", "+")
print(
    res36
)
# >>> [
#       ('1692630648281-0', {'rider': 'Henshaw'}),
#       ('1692631018238-0', {'rider': 'Smith'})
# ]

res37 = r.xtrim("race:italy", maxlen=10, approximate=False)
print(res37)  # >>> 0

res38 = r.xtrim("race:italy", maxlen=10)
print(res38)  # >>> 0

res39 = r.xrange("race:italy", "-", "+")
print(
    res39
)
# >>> [
#       ('1692630648281-0', {'rider': 'Henshaw'}),
#       ('1692631018238-0', {'rider': 'Smith'})
# ]

res40 = r.xdel("race:italy", "1692631018238-0")
print(res40)  # >>> 1

res41 = r.xrange("race:italy", "-", "+")
print(res41)  # >>> [('1692630648281-0', {'rider': 'Henshaw'})]
import assert from 'assert';
import {
  createClient
} from 'redis';

const client = await createClient();
await client.connect();

const res1 = await client.xAdd(
  'race:france', '*', {
    'rider': 'Castilla',
    'speed': '30.2',
    'position': '1',
    'location_id': '1'
  }
);
console.log(res1); // >>> 1700073067968-0 N.B. actual values will differ from these examples

const res2 = await client.xAdd(
  'race:france', '*', {
    'rider': 'Norem',
    'speed': '28.8',
    'position': '3',
    'location_id': '1'
  },
);
console.log(res2); // >>> 1692629594113-0

const res3 = await client.xAdd(
  'race:france', '*', {
    'rider': 'Prickett',
    'speed': '29.7',
    'position': '2',
    'location_id': '1'
  },
);
console.log(res3); // >>> 1692629613374-0


const res4 = await client.xRange('race:france', '1691765278160-0', '+', {COUNT: 2});
console.log(res4); // >>> [{ id: '1692629576966-0', message: { rider: 'Castilla', speed: '30.2', position: '1', location_id: '1' } }, { id: '1692629594113-0', message: { rider: 'Norem', speed: '28.8', position: '3', location_id: '1' } }]

const res5 = await client.xRead({
  key: 'race:france',
  id: '0-0'
}, {
  COUNT: 100,
  BLOCK: 300
});
console.log(res5); // >>> [{ name: 'race:france', messages: [{ id: '1692629576966-0', message: { rider: 'Castilla', speed: '30.2', position: '1', location_id: '1' } }, { id: '1692629594113-0', message: { rider: 'Norem', speed: '28.8', position: '3', location_id: '1' } }, { id: '1692629613374-0', message: { rider: 'Prickett', speed: '29.7', position: '2', location_id: '1' } }] }]

const res6 = await client.xAdd(
  'race:france', '*', {
    'rider': 'Castilla',
    'speed': '29.9',
    'position': '1',
    'location_id': '2'
  }
);
console.log(res6); // >>> 1692629676124-0

const res7 = await client.xLen('race:france');
console.log(res7); // >>> 4


const res8 = await client.xAdd('race:usa', '0-1', {
  'racer': 'Castilla'
});
console.log(res8); // >>> 0-1

const res9 = await client.xAdd('race:usa', '0-2', {
  'racer': 'Norem'
});
console.log(res9); // >>> 0-2

try {
  const res10 = await client.xAdd('race:usa', '0-1', {
    'racer': 'Prickett'
  });
  console.log(res10); // >>> 0-1
} catch (error) {
  console.error(error); // >>> [SimpleError: ERR The ID specified in XADD is equal or smaller than the target stream top item]
}

const res11a = await client.xAdd('race:usa', '0-*', { racer: 'Norem' });
console.log(res11a); // >>> 0-3

const res11 = await client.xRange('race:france', '-', '+');
console.log(res11); // >>> [{ id: '1692629576966-0', message: { rider: 'Castilla', speed: '30.2', position: '1', location_id: '1' } }, { id: '1692629594113-0', message: { rider: 'Norem', speed: '28.8', position: '3', location_id: '1' } }, { id: '1692629613374-0', message: { rider: 'Prickett', speed: '29.7', position: '2', location_id: '1' } }, { id: '1692629676124-0', message: { rider: 'Castilla', speed: '29.9', position: '1', location_id: '2' } }]

const res12 = await client.xRange('race:france', '1692629576965', '1692629576967');
console.log(res12); // >>> [{ id: '1692629576966-0', message: { rider: 'Castilla', speed: '30.2', position: '1', location_id: '1' } }]

const res13 = await client.xRange('race:france', '-', '+', {COUNT: 2});
console.log(res13); // >>> [{ id: '1692629576966-0', message: { rider: 'Castilla', speed: '30.2', position: '1', location_id: '1' } }, { id: '1692629594113-0', message: { rider: 'Norem', speed: '28.8', position: '3', location_id: '1' } }]

const res14 = await client.xRange('race:france', '(1692629594113-0', '+', {COUNT: 2});
console.log(res14); // >>> [{ id: '1692629613374-0', message: { rider: 'Prickett', speed: '29.7', position: '2', location_id: '1' } }, { id: '1692629676124-0', message: { rider: 'Castilla', speed: '29.9', position: '1', location_id: '2' } }]

const res15 = await client.xRange('race:france', '(1692629676124-0', '+', {COUNT: 2});
console.log(res15); // >>> []

const res16 = await client.xRevRange('race:france', '+', '-', {COUNT: 1});
console.log(
  res16
); // >>> [{ id: '1692629676124-0', message: { rider: 'Castilla', speed: '29.9', position: '1', location_id: '2' } }]

const res17 = await client.xRead({
  key: 'race:france',
  id: '0-0'
}, {
  COUNT: 2
});
console.log(res17); // >>> [{ name: 'race:france', messages: [{ id: '1692629576966-0', message: { rider: 'Castilla', speed: '30.2', position: '1', location_id: '1' } }, { id: '1692629594113-0', message: { rider: 'Norem', speed: '28.8', position: '3', location_id: '1' } }] }]

const res18 = await client.xGroupCreate('race:france', 'france_riders', '$');
console.log(res18); // >>> OK

const res19 = await client.xGroupCreate('race:italy', 'italy_riders', '$', {
  MKSTREAM: true
});
console.log(res19); // >>> OK

await client.xAdd('race:italy', '*', {
  'rider': 'Castilla'
});
await client.xAdd('race:italy', '*', {
  'rider': 'Royce'
});
await client.xAdd('race:italy', '*', {
  'rider': 'Sam-Bodden'
});
await client.xAdd('race:italy', '*', {
  'rider': 'Prickett'
});
await client.xAdd('race:italy', '*', {
  'rider': 'Norem'
});

const res20 = await client.xReadGroup(
  'italy_riders',
  'Alice', {
    key: 'race:italy',
    id: '>'
  }, {
    COUNT: 1
  }
);
console.log(res20); // >>> [{ name: 'race:italy', messages: [{ id: '1692629925771-0', message: { rider: 'Castilla' } }] }]

const res21 = await client.xReadGroup(
  'italy_riders',
  'Alice', {
    key: 'race:italy',
    id: '0'
  }, {
    COUNT: 1
  }
);
console.log(res21); // >>> [{ name: 'race:italy', messages: [{ id: '1692629925771-0', message: { rider: 'Castilla' } }] }]

const res22 = await client.xAck('race:italy', 'italy_riders', '1692629925771-0')
console.log(res22); // >>> 1

const res23 = await client.xReadGroup(
  'italy_riders',
  'Alice', {
    key: 'race:italy',
    id: '0'
  }, {
    COUNT: 1
  }
);
console.log(res23); // >>> [{ name: 'race:italy', messages: [] }]

const res24 = await client.xReadGroup(
  'italy_riders',
  'Bob', {
    key: 'race:italy',
    id: '>'
  }, {
    COUNT: 2
  }
);
console.log(res24); // >>> [{ name: 'race:italy', messages: [{ id: '1692629925789-0', message: { rider: 'Royce' } }, { id: '1692629925790-0', message: { rider: 'Sam-Bodden' } }] }]

const res25 = await client.xPending('race:italy', 'italy_riders');
console.log(res25); // >>> {'pending': 2, 'firstId': '1692629925789-0', 'lastId': '1692629925790-0', 'consumers': [{'name': 'Bob', 'deliveriesCounter': 2}]}

const res26 = await client.xPendingRange('race:italy', 'italy_riders', '-', '+', 10);
console.log(res26); // >>> [{'id': '1692629925789-0', 'consumer': 'Bob', 'millisecondsSinceLastDelivery': 31084, 'deliveriesCounter:': 1}, {'id': '1692629925790-0', 'consumer': 'Bob', 'millisecondsSinceLastDelivery': 31084, 'deliveriesCounter': 1}]

const res27 = await client.xRange('race:italy', '1692629925789-0', '1692629925789-0');
console.log(res27); // >>> [{ id: '1692629925789-0', message: { rider: 'Royce' } }]

const res28 = await client.xClaim(
  'race:italy', 'italy_riders', 'Alice', 60000, ['1692629925789-0']
);
console.log(res28); // >>> [{ id: '1692629925789-0', message: { rider: 'Royce' } }]

const res29 = await client.xAutoClaim('race:italy', 'italy_riders', 'Alice', 1, '0-0', {
  COUNT: 1
});
console.log(res29); // >>> { nextId: '1692629925790-0', messages: [{ id: '1692629925789-0', message: { rider: 'Royce' } }], deletedMessages: [] }

const res30 = await client.xAutoClaim(
  'race:italy', 'italy_riders', 'Alice', 1, '(1692629925789-0',
  {
    COUNT: 1
  }
);
console.log(res30); // >>> { nextId: '0-0', messages: [{ id: '1692629925790-0', message: { rider: 'Sam-Bodden' } }], deletedMessages: [] }

const res31 = await client.xInfoStream('race:italy');
console.log(res31); // >>> { length: 5, 'radix-tree-keys': 1, 'radix-tree-nodes': 2, 'last-generated-id': '1692629926436-0', 'max-deleted-entry-id': '0-0', 'entries-added': 5, 'recorded-first-entry-id': '1692629925771-0', groups: 1, 'first-entry': { id: '1692629925771-0', message: { rider: 'Castilla' } }, 'last-entry': { id: '1692629926436-0', message: { rider: 'Norem' } } }

const res32 = await client.xInfoGroups('race:italy');
console.log(res32); // >>> [{ name: 'italy_riders', consumers: 2, pending: 3, 'last-delivered-id': '1692629925790-0', 'entries-read': 3, lag: 2 }]

const res33 = await client.xInfoConsumers('race:italy', 'italy_riders');
console.log(res33); // >>> [{ name: 'Alice', pending: 3, idle: 170582, inactive: 170582 }, { name: 'Bob', pending: 0, idle: 489404, inactive: 489404 }]

await client.