diff --git a/docs.json b/docs.json index 8020cce5..795d9dfd 100644 --- a/docs.json +++ b/docs.json @@ -155,6 +155,12 @@ "redis/sdks/ts/commands/auth/ping" ] }, + { + "group": "Connection", + "pages": [ + "redis/sdks/ts/commands/connection/client_setinfo" + ] + }, { "group": "Bitmap", "pages": [ @@ -209,6 +215,8 @@ "redis/sdks/ts/commands/hash/hexpiretime", "redis/sdks/ts/commands/hash/hget", "redis/sdks/ts/commands/hash/hgetall", + "redis/sdks/ts/commands/hash/hgetdel", + "redis/sdks/ts/commands/hash/hgetex", "redis/sdks/ts/commands/hash/hincrby", "redis/sdks/ts/commands/hash/hincrbyfloat", "redis/sdks/ts/commands/hash/hkeys", @@ -217,6 +225,7 @@ "redis/sdks/ts/commands/hash/hrandfield", "redis/sdks/ts/commands/hash/hscan", "redis/sdks/ts/commands/hash/hset", + "redis/sdks/ts/commands/hash/hsetex", "redis/sdks/ts/commands/hash/hpersist", "redis/sdks/ts/commands/hash/hpexpire", "redis/sdks/ts/commands/hash/hpexpireat", @@ -353,10 +362,12 @@ "group": "Stream", "pages": [ "redis/sdks/ts/commands/stream/xack", + "redis/sdks/ts/commands/stream/xackdel", "redis/sdks/ts/commands/stream/xadd", "redis/sdks/ts/commands/stream/xautoclaim", "redis/sdks/ts/commands/stream/xclaim", "redis/sdks/ts/commands/stream/xdel", + "redis/sdks/ts/commands/stream/xdelex", "redis/sdks/ts/commands/stream/xgroup", "redis/sdks/ts/commands/stream/xinfo", "redis/sdks/ts/commands/stream/xlen", @@ -423,6 +434,12 @@ "redis/sdks/py/commands/auth/ping" ] }, + { + "group": "Connection", + "pages": [ + "redis/sdks/py/commands/connection/client_setinfo" + ] + }, { "group": "Bitmap", "pages": [ @@ -465,6 +482,8 @@ "redis/sdks/py/commands/hash/hexpiretime", "redis/sdks/py/commands/hash/hget", "redis/sdks/py/commands/hash/hgetall", + "redis/sdks/py/commands/hash/hgetdel", + "redis/sdks/py/commands/hash/hgetex", "redis/sdks/py/commands/hash/hincrby", "redis/sdks/py/commands/hash/hincrbyfloat", "redis/sdks/py/commands/hash/hkeys", @@ -479,6 +498,7 @@ "redis/sdks/py/commands/hash/hpexpiretime", "redis/sdks/py/commands/hash/hpttl", "redis/sdks/py/commands/hash/hmset", + "redis/sdks/py/commands/hash/hsetex", "redis/sdks/py/commands/hash/hsetnx", "redis/sdks/py/commands/hash/hstrlen", "redis/sdks/py/commands/hash/httl", @@ -611,10 +631,12 @@ "group": "Stream", "pages": [ "redis/sdks/py/commands/stream/xack", + "redis/sdks/py/commands/stream/xackdel", "redis/sdks/py/commands/stream/xadd", "redis/sdks/py/commands/stream/xautoclaim", "redis/sdks/py/commands/stream/xclaim", "redis/sdks/py/commands/stream/xdel", + "redis/sdks/py/commands/stream/xdelex", "redis/sdks/py/commands/stream/xgroup_create", "redis/sdks/py/commands/stream/xgroup_createconsumer", "redis/sdks/py/commands/stream/xgroup_delconsumer", diff --git a/redis/sdks/py/commands/bitmap/bitop.mdx b/redis/sdks/py/commands/bitmap/bitop.mdx index 27913418..c71f199c 100644 --- a/redis/sdks/py/commands/bitmap/bitop.mdx +++ b/redis/sdks/py/commands/bitmap/bitop.mdx @@ -3,22 +3,34 @@ title: BITOP description: Perform bitwise operations between strings. --- -The `BITOP` command in Redis is used to perform bitwise operations on multiple keys (or Redis strings) and store the result in a destination key. It is primarily used for performing logical AND, OR, XOR, and NOT operations on binary data stored in Redis. +The `BITOP` command in Redis is used to perform bitwise operations on multiple keys (or Redis strings) and store the result in a destination key. It supports standard logical operations (AND, OR, XOR, NOT) and advanced operations (DIFF, DIFF1, ANDOR, ONE) for complex bit manipulation. + ## Arguments - - Specifies the type of bitwise operation to perform, which can be one of the - following: `AND`, `OR`, `XOR`, or `NOT`. + + Specifies the type of bitwise operation to perform (case-insensitive): + + **Standard Operations:** + - `AND` or `and`: Bitwise AND + - `OR` or `or`: Bitwise OR + - `XOR` or `xor`: Bitwise XOR + - `NOT` or `not`: Bitwise NOT + + **Advanced Operations (Redis 8.2+):** + - `DIFF` or `diff`: A bit is set only if it's set in all source bitmaps + - `DIFF1` or `diff1`: A bit is set if it's set in the first key but not in any of the other keys + - `ANDOR` or `andor`: A bit is set if it's set in X and also in one or more of Y1, Y2, ... + - `ONE` or `one`: A bit is set if it's set in exactly one source key - The key to store the result of the operation in. + The key to store the result of the operation in. - - One or more keys to perform the operation on. - + + One or more keys to perform the operation on. + ## Response @@ -27,17 +39,108 @@ The `BITOP` command in Redis is used to perform bitwise operations on multiple k - ```py Example - # key1 = 00000001 - # key2 = 00000010 - redis.setbit("key1", 0, 1) - redis.setbit("key2", 0, 0) - redis.setbit("key2", 1, 1) - - assert redis.bitop("AND", "dest", "key1", "key2") == 1 - - # result = 00000000 - assert redis.getbit("dest", 0) == 0 - assert redis.getbit("dest", 1) == 0 - ``` +```py Standard Operations +# AND operation +redis.setbit("key1", 0, 1) +redis.setbit("key2", 0, 0) +redis.setbit("key2", 1, 1) + +assert redis.bitop("AND", "dest", "key1", "key2") == 1 + +# OR operation +redis.bitop("OR", "dest", "key1", "key2") + +# XOR operation +redis.bitop("XOR", "dest", "key1", "key2") + +# NOT operation (only accepts one source key) +redis.bitop("NOT", "dest", "key1") +``` + +```py DIFF Operation (Redis 8.2+) +# Set bits in multiple bitmaps +redis.setbit("bitmap1", 0, 1) +redis.setbit("bitmap1", 1, 1) +redis.setbit("bitmap2", 1, 1) +redis.setbit("bitmap2", 2, 1) + +# DIFF: bits set in all sources +result = redis.bitop("DIFF", "result", "bitmap1", "bitmap2") +# Result: bit 1 is set (only bit present in both) +``` + +```py DIFF1 Operation (Redis 8.2+) +# Set bits in bitmaps +redis.setbit("exclude", 0, 1) +redis.setbit("exclude", 1, 1) +redis.setbit("include1", 1, 1) +redis.setbit("include2", 2, 1) + +# DIFF1: bits in first key but not in others +result = redis.bitop("DIFF1", "result", "exclude", "include1", "include2") +# Result: bit 0 is set (only in exclude, not in include1 or include2) +``` + +```py ANDOR Operation (Redis 8.2+) +# Set bits in bitmaps +redis.setbit("required", 0, 1) +redis.setbit("required", 1, 1) +redis.setbit("option1", 1, 1) +redis.setbit("option2", 2, 1) + +# ANDOR: bits in required AND in at least one option +result = redis.bitop("ANDOR", "result", "required", "option1", "option2") +# Result: bit 1 is set (in required AND in option1) +``` + +```py ONE Operation (Redis 8.2+) +# Set bits in multiple sources +redis.setbit("source1", 0, 1) +redis.setbit("source1", 1, 1) +redis.setbit("source2", 1, 1) +redis.setbit("source2", 2, 1) +redis.setbit("source3", 2, 1) + +# ONE: bits set in exactly one source +result = redis.bitop("ONE", "result", "source1", "source2", "source3") +# Result: bit 0 is set (only in source1) +``` + +## Advanced Operations Details (Redis 8.2+) + +### DIFF +A bit in the destination is set if it is set in **all** source bitmaps. This is equivalent to the intersection of all source bitmaps. + +**Use Cases:** +- Finding common features across multiple datasets +- Identifying users present in all segments +- Intersection of multiple filters + +### DIFF1 +A bit in the destination is set if it is set in the **first key** but **not in any of the other keys**. This finds bits unique to the first bitmap. + +**Use Cases:** +- Finding exclusive features +- Identifying users in one segment but not in others +- Exclusion filtering + +### ANDOR +A bit in the destination is set if it is set in **X and also in one or more of Y1, Y2, ...**. This implements "X AND (Y1 OR Y2 OR ...)" logic. + +**Use Cases:** +- Required condition with optional alternatives +- Users with a required attribute and at least one optional attribute +- Complex filtering with mandatory and optional criteria + +### ONE +A bit in the destination is set if it is set in **exactly one** of the source keys. This finds bits that are unique to a single source. + +**Use Cases:** +- Finding unique occurrences +- Identifying exclusive memberships +- Detecting singular conditions across multiple sets + + +The advanced operations (DIFF, DIFF1, ANDOR, ONE) are available in Redis 8.2.0 and later. + diff --git a/redis/sdks/py/commands/connection/client_setinfo.mdx b/redis/sdks/py/commands/connection/client_setinfo.mdx new file mode 100644 index 00000000..780db561 --- /dev/null +++ b/redis/sdks/py/commands/connection/client_setinfo.mdx @@ -0,0 +1,91 @@ +--- +title: CLIENT SETINFO +description: Set client library name and version information. +--- + +The `CLIENT SETINFO` command sets client library name and version information that will be shown in CLIENT LIST and CLIENT INFO commands. This helps identify which client library is being used and is useful for debugging and monitoring. + +## Arguments + + + The attribute to set (case-insensitive): + - `"LIB-NAME"` or `"lib-name"`: Library name + - `"LIB-VER"` or `"lib-ver"`: Library version + + + + The value to set for the attribute. + + +## Response + + + Returns `"OK"` if the attribute was successfully set. + + + +```py Set Library Name +# Set the library name +result = redis.client_setinfo("LIB-NAME", "redis-py") +assert result == "OK" +``` + +```py Set Library Version +# Set the library version +result = redis.client_setinfo("LIB-VER", "1.0.0") +assert result == "OK" +``` + +```py Lowercase Attributes +# Attributes are case-insensitive +redis.client_setinfo("lib-name", "redis-py") +redis.client_setinfo("lib-ver", "2.0.0") +``` + +```py With Custom Suffix +# Common pattern: include wrapper or framework info +redis.client_setinfo("LIB-NAME", "redis-py(upstash_v1.0.0)") +redis.client_setinfo("LIB-VER", "1.0.0") +``` + +```py Complete Setup +# Set both library name and version +redis.client_setinfo("LIB-NAME", "my-redis-wrapper") +redis.client_setinfo("LIB-VER", "3.2.1") + +# Now this information will appear in CLIENT LIST output +``` + + +## Use Cases + +- **Debugging**: Identify which client library version is being used +- **Monitoring**: Track different client libraries connecting to Redis +- **Analytics**: Understand client library distribution across connections +- **Support**: Quickly identify client versions when troubleshooting issues + +## Viewing Client Information + +After setting client info, you can view it using the CLIENT LIST command: + +```py +# Set client information +redis.client_setinfo("LIB-NAME", "redis-py") +redis.client_setinfo("LIB-VER", "1.0.0") + +# View all clients (if you have access to CLIENT LIST) +# The output will include lib-name and lib-ver fields +``` + +## Best Practices + +1. **Set on Connection**: Set client info immediately after establishing a connection +2. **Include Version**: Always set both library name and version +3. **Use Descriptive Names**: Make library names easily identifiable +4. **Version Format**: Use semantic versioning (e.g., "1.2.3") +5. **Include Context**: Add wrapper or framework info if applicable + + +This command is available in Redis 7.2.0 and later. The attribute names are case-insensitive and will be normalized to uppercase. + + diff --git a/redis/sdks/py/commands/hash/hgetdel.mdx b/redis/sdks/py/commands/hash/hgetdel.mdx new file mode 100644 index 00000000..da5fd7ca --- /dev/null +++ b/redis/sdks/py/commands/hash/hgetdel.mdx @@ -0,0 +1,75 @@ +--- +title: HGETDEL +description: Get and delete hash fields atomically. +--- + +The `HGETDEL` command returns the values of the specified fields and then atomically deletes them from the hash. This is useful when you need to retrieve and remove data in a single atomic operation. + +## Arguments + + + The key of the hash. + + + + One or more field names to get and delete. + + +## Response + + + A list of values corresponding to the requested fields, in the same order as the field arguments. For fields that do not exist, `None` is returned in that position. If the hash doesn't exist, `None` is returned. + + + +```py Basic Example +# Set some hash fields +redis.hset("user:123", values={ + "name": "John", + "age": "30", + "email": "john@example.com" +}) + +# Get and delete specific fields +result = redis.hgetdel("user:123", "name", "email") +assert result == ["John", "john@example.com"] + +# Verify fields were deleted +assert redis.hget("user:123", "name") is None +``` + +```py Multiple Fields +redis.hset("session:abc", values={ + "token": "xyz123", + "user": "john", + "expires": "2024-12-31" +}) + +# Get and delete all fields +session = redis.hgetdel("session:abc", "token", "user", "expires") +assert session == ["xyz123", "john", "2024-12-31"] +``` + +```py Non-existent Fields +redis.hset("user:456", "name", "Jane") + +# Get and delete with non-existent field +result = redis.hgetdel("user:456", "name", "email") +assert result == ["Jane", None] +``` + +```py Non-existent Hash +# Returns None if hash doesn't exist +result = redis.hgetdel("nonexistent", "field1") +assert result is None +``` + + +## Use Cases + +- **Session Management**: Retrieve and invalidate session data atomically +- **Cache Cleanup**: Get cached data while removing it from storage +- **Queue Processing**: Fetch and remove job data in a single operation +- **Temporary Data**: Retrieve one-time use tokens or codes while deleting them + + diff --git a/redis/sdks/py/commands/hash/hgetex.mdx b/redis/sdks/py/commands/hash/hgetex.mdx new file mode 100644 index 00000000..f2197e25 --- /dev/null +++ b/redis/sdks/py/commands/hash/hgetex.mdx @@ -0,0 +1,120 @@ +--- +title: HGETEX +description: Get hash fields with expiration support. +--- + +The `HGETEX` command returns the values of the specified fields and optionally sets their expiration time or TTL. This allows you to retrieve hash data while managing its lifetime in a single operation. + +## Arguments + + + The key of the hash. + + + + One or more field names to get. + + + + Set expiration time in seconds. + + + + Set expiration time in milliseconds. + + + + Set expiration as Unix timestamp in seconds. + + + + Set expiration as Unix timestamp in milliseconds. + + + + Remove expiration from the hash. + + +## Response + + + A list of values corresponding to the requested fields in the same order. Returns `None` for fields that do not exist. If the hash doesn't exist, `None` is returned. + + + +```py With EX (seconds) +redis.hset("user:123", values={"name": "John", "email": "john@example.com"}) + +# Get fields and set expiration to 60 seconds +result = redis.hgetex("user:123", "name", "email", ex=60) +assert result == ["John", "john@example.com"] +``` + +```py With PX (milliseconds) +redis.hset("session:abc", values={"token": "xyz123", "user": "john"}) + +# Get fields and set expiration to 30000 milliseconds (30 seconds) +result = redis.hgetex("session:abc", "token", "user", px=30000) +assert result == ["xyz123", "john"] +``` + +```py With EXAT (Unix timestamp in seconds) +import time + +redis.hset("cache:data", "value", "cached") + +# Set expiration to specific timestamp (1 hour from now) +future_time = int(time.time()) + 3600 +result = redis.hgetex("cache:data", "value", exat=future_time) +assert result == ["cached"] +``` + +```py With PXAT (Unix timestamp in milliseconds) +import time + +redis.hset("temp:data", "info", "temporary") + +# Set expiration to specific timestamp in milliseconds +future_time = int(time.time() * 1000) + 60000 # 1 minute from now +result = redis.hgetex("temp:data", "info", pxat=future_time) +assert result == ["temporary"] +``` + +```py With PERSIST +redis.hset("user:456", "name", "Jane") +redis.expire("user:456", 300) # Set 5 minute expiration + +# Get fields and remove expiration +result = redis.hgetex("user:456", "name", persist=True) +assert result == ["Jane"] + +# Verify expiration was removed +ttl = redis.ttl("user:456") +assert ttl == -1 # No expiration +``` + +```py Without Options +redis.hset("data:xyz", values={"field1": "value1", "field2": "value2"}) + +# Just get fields without changing expiration +result = redis.hgetex("data:xyz", "field1", "field2") +assert result == ["value1", "value2"] +``` + +```py Non-existent Fields +redis.hset("user:789", "name", "Bob") + +# Get fields including one that doesn't exist +result = redis.hgetex("user:789", "name", "email") +assert result == ["Bob", None] +``` + + +## Use Cases + +- **Session Management**: Retrieve session data and extend its lifetime +- **Cache Refresh**: Get cached data and update its TTL +- **Temporary Data**: Access temporary data while managing its expiration +- **Rate Limiting**: Fetch rate limit data and reset expiration + + diff --git a/redis/sdks/py/commands/hash/hsetex.mdx b/redis/sdks/py/commands/hash/hsetex.mdx new file mode 100644 index 00000000..14d2f033 --- /dev/null +++ b/redis/sdks/py/commands/hash/hsetex.mdx @@ -0,0 +1,202 @@ +--- +title: HSETEX +description: Set hash fields with expiration support. +--- + +The `HSETEX` command sets the specified fields with their values and optionally sets their expiration time or TTL. It supports conditional operations to control when fields should be set. + +## Arguments + + + The key of the hash. + + + + A single field name to set. Use with `value` parameter. + + + + A single value to set. Use with `field` parameter. + + + + A dictionary of fields and their values to set. Either use `field`/`value` or `values`, but not both. + + + + Only set fields if the hash does not exist. + + + + Only set fields if the hash already exists. + + + + Set expiration time in seconds. + + + + Set expiration time in milliseconds. + + + + Set expiration as Unix timestamp in seconds. + + + + Set expiration as Unix timestamp in milliseconds. + + + + Retain the existing time to live (TTL) associated with the hash key when setting fields. If the hash has an expiration, it will be preserved. + + +## Response + + + 0 if no fields were set, 1 if all the fields were set. + + + +```py Single Field +# Set a single field with expiration +result = redis.hsetex("myhash", "field1", "Hello", ex=60) +assert result == 1 +``` + +```py Multiple Fields +# Set fields with 1 hour expiration +result = redis.hsetex( + "user:123", + values={"name": "John", "email": "john@example.com"}, + ex=3600 +) +assert result == 2 +``` + +```py With FNX (only if hash doesn't exist) +# Set fields only if the hash doesn't exist +result = redis.hsetex( + "user:456", + values={"name": "Jane", "age": "25"}, + fnx=True +) +assert result == 2 + +# Try again - will return 0 since hash now exists +result = redis.hsetex( + "user:456", + values={"email": "jane@example.com"}, + fnx=True +) +assert result == 0 +``` + +```py With FXX (only if hash exists) +# First create the hash +redis.hset("session:abc", "token", "xyz") + +# Update only if hash exists +result = redis.hsetex( + "session:abc", + values={"user": "john"}, + fxx=True +) +assert result == 1 # Hash exists, field added + +# Try on non-existent hash +result = redis.hsetex( + "session:nonexistent", + values={"user": "jane"}, + fxx=True +) +assert result == 0 # Hash doesn't exist +``` + +```py With PX (milliseconds) +import time + +# Set fields with 30 second expiration +result = redis.hsetex( + "cache:data", + values={"value": "cached data", "timestamp": str(int(time.time()))}, + px=30000 +) +assert result == 2 +``` + +```py With EXAT (Unix timestamp in seconds) +import time + +# Set expiration to specific timestamp +future_time = int(time.time()) + 7200 # 2 hours from now +result = redis.hsetex( + "temp:data", + values={"info": "temporary information"}, + exat=future_time +) +assert result == 1 +``` + +```py With PXAT (Unix timestamp in milliseconds) +import time + +# Set expiration to specific timestamp in milliseconds +future_time = int(time.time() * 1000) + 300000 # 5 minutes from now +result = redis.hsetex( + "session:xyz", + values={"token": "abc123", "user": "john"}, + pxat=future_time +) +assert result == 2 +``` + +```py Combined: Conditional + Expiration +import time + +# Set fields only if hash doesn't exist, with 1 hour expiration +result = redis.hsetex( + "user:789", + values={ + "name": "Alice", + "email": "alice@example.com", + "created": str(int(time.time())) + }, + fnx=True, + ex=3600 +) +assert result == 3 +``` + +```py With KEEPTTL +# First set fields with expiration +redis.hsetex("cache:data", values={"value": "cached"}, ex=300) + +# Later update fields while retaining the existing TTL +result = redis.hsetex("cache:data", values={"updated": "yes"}, keepttl=True) +assert result == 1 + +# Verify TTL is still 300 seconds (or less if time passed) +ttl = redis.ttl("cache:data") +assert ttl > 0 and ttl <= 300 # TTL was retained +``` + +```py Without Options +# Just set fields without expiration or conditions +result = redis.hsetex( + "data:simple", + values={"field1": "value1", "field2": "value2"} +) +assert result == 2 +``` + + +## Use Cases + +- **Session Management**: Create sessions with automatic expiration +- **Cache with TTL**: Store cached data that expires automatically +- **Temporary Data**: Create temporary records with built-in cleanup +- **Rate Limiting**: Store rate limit counters with automatic reset +- **Conditional Updates**: Ensure data consistency with FNX/FXX options + + diff --git a/redis/sdks/py/commands/overview.mdx b/redis/sdks/py/commands/overview.mdx index c8b45a6f..4707fd53 100644 --- a/redis/sdks/py/commands/overview.mdx +++ b/redis/sdks/py/commands/overview.mdx @@ -15,6 +15,15 @@ description: Available Commands in upstash-redis Ping the server. + + + + + + + Set client library name and version information. + + @@ -27,7 +36,7 @@ description: Available Commands in upstash-redis Perform bitwise operations between strings. - Perform bitwise operations between strings. + Perform bitwise operations between strings (includes DIFF, DIFF1, ANDOR, ONE). Find first bit set or clear in a string. @@ -124,6 +133,12 @@ description: Available Commands in upstash-redis + + + Get and delete hash fields atomically. + + + Get hash fields with expiration support. @@ -166,6 +181,9 @@ description: Available Commands in upstash-redis + + + Set hash fields with expiration support. @@ -470,8 +488,11 @@ Publish messages to many clients Acknowledge one or multiple messages as processed for a consumer group. + + Acknowledge and delete stream entries atomically. + - Append a new entry to a stream. + Append a new entry to a stream (supports auto sequence numbers). Transfer ownership of pending messages to another consumer automatically. @@ -482,6 +503,9 @@ Publish messages to many clients Remove one or multiple entries from a stream. + + Extended delete for streams with reference control. + Create a new consumer group for a stream. diff --git a/redis/sdks/py/commands/stream/xackdel.mdx b/redis/sdks/py/commands/stream/xackdel.mdx new file mode 100644 index 00000000..0a585f31 --- /dev/null +++ b/redis/sdks/py/commands/stream/xackdel.mdx @@ -0,0 +1,198 @@ +--- +title: XACKDEL +description: Acknowledge and delete stream entries atomically. +--- + +The `XACKDEL` command acknowledges and deletes stream entries atomically in a single operation. This is useful for consumer groups where you want to acknowledge message processing and remove the entries from the stream simultaneously. + +## Arguments + + + The key of the stream. + + + + The consumer group name. + + + + One or more stream entry IDs to acknowledge and delete. + + + + Optional deletion behavior (case-insensitive): + - `KEEPREF` or `keepref`: Keep consumer group references + - `DELREF` or `delref`: Delete consumer group references + - `ACKED` or `acked`: Only acknowledge messages (don't delete) + + +## Response + + + A list of integers indicating the result for each ID in the same order as provided. + + + +```py Single Entry +# Acknowledge and delete a single entry +result = redis.xackdel("mystream", "mygroup", "1638360173533-0") +print(result) # List of results for each ID +``` + +```py Multiple Entries +# Acknowledge and delete multiple entries +result = redis.xackdel( + "mystream", + "mygroup", + "1638360173533-0", + "1638360173533-1", + "1638360173533-2" +) +print(result) # List of results for each ID +``` + +```py With KEEPREF Option +# Keep consumer group references when deleting entries +# Useful when you want to delete entries but maintain group tracking +result = redis.xackdel( + "mystream", + "mygroup", + "1638360173533-0", + "1638360173533-1", + option="KEEPREF" +) +print(result) # List of results for each ID +``` + +```py With DELREF Option +# Delete consumer group references along with entries +# Useful for complete cleanup of processed messages +result = redis.xackdel( + "mystream", + "mygroup", + "1638360173533-0", + "1638360173533-1", + option="DELREF" +) +print(result) # List of results for each ID +``` + +```py With ACKED Option +# Only acknowledge messages without deleting them +# Useful when you want to mark as processed but keep for audit/debugging +result = redis.xackdel( + "mystream", + "mygroup", + "1638360173533-0", + option="ACKED" +) +print(result) # List of results for each ID +# Entry remains in stream but is acknowledged +``` + +```py Consumer Group Processing +# Create a consumer group +redis.xgroup_create("orders", "processors", "0", mkstream=True) + +# Add some orders to the stream +redis.xadd("orders", "*", {"order_id": "123", "status": "pending"}) +redis.xadd("orders", "*", {"order_id": "124", "status": "pending"}) + +# Read messages as a consumer +messages = redis.xreadgroup( + "processors", "consumer1", {"orders": ">"}, + count=2 +) + +# Process messages and acknowledge + delete them +for stream_name, entries in messages: + for entry_id, fields in entries: + # Process the order... + print(f"Processing order {fields['order_id']}") + + # Acknowledge and delete the entry (default behavior) + redis.xackdel("orders", "processors", entry_id) + + # Or use DELREF for complete cleanup + # redis.xackdel("orders", "processors", entry_id, option="DELREF") +``` + +```py Batch Processing +# Read pending messages +pending = redis.xreadgroup( + "tasks", "worker1", {"task-queue": ">"}, + count=10 +) + +if pending: + stream_name, entries = pending[0] + processed_ids = [] + + # Process each task + for entry_id, fields in entries: + try: + # Process task... + print(f"Processing task {entry_id}") + processed_ids.append(entry_id) + except Exception as error: + print(f"Failed to process {entry_id}: {error}") + + # Acknowledge and delete all successfully processed tasks + # Using DELREF to completely remove references + if processed_ids: + result = redis.xackdel("task-queue", "tasks", *processed_ids, option="DELREF") + print(f"Results: {result}") # List of results for each ID +``` + +```py Audit Mode with ACKED +# Process messages but keep them for audit/debugging purposes +messages = redis.xreadgroup( + "audit", "auditor1", {"events": ">"}, + count=5 +) + +for stream_name, entries in messages: + entry_ids = [entry_id for entry_id, _ in entries] + + # Process and acknowledge but don't delete + for entry_id, fields in entries: + # Audit the event... + print(f"Auditing event {fields}") + + # Acknowledge without deleting (keep for audit trail) + result = redis.xackdel("events", "audit", *entry_ids, option="ACKED") + print(f"Acknowledged {len(result)} events (kept in stream)") +``` + + +## Use Cases + +- **Message Queue Cleanup**: Process messages and remove them from the stream in one operation +- **Event Processing**: Acknowledge event handling and clean up the stream +- **Task Queue Management**: Complete tasks and remove them atomically +- **Memory Optimization**: Reduce stream memory usage by removing processed entries + +## Comparison with XACK + XDEL + +Traditional approach (two operations): +```py +# Acknowledge the message +redis.xack("mystream", "mygroup", "123-0") +# Then delete it +redis.xdel("mystream", "123-0") +``` + +With XACKDEL (single atomic operation): +```py +# Acknowledge and delete in one operation (default) +redis.xackdel("mystream", "mygroup", "123-0") + +# With options for fine-grained control +redis.xackdel("mystream", "mygroup", "123-0", option="DELREF") # Complete cleanup +redis.xackdel("mystream", "mygroup", "123-0", option="ACKED") # Acknowledge only +``` + + +This command is available in Redis 8.2.0 and later. It combines XACK and XDEL into a single atomic operation, which is more efficient and ensures consistency. + + diff --git a/redis/sdks/py/commands/stream/xadd.mdx b/redis/sdks/py/commands/stream/xadd.mdx index cb7614e4..a1bcfb2c 100644 --- a/redis/sdks/py/commands/stream/xadd.mdx +++ b/redis/sdks/py/commands/stream/xadd.mdx @@ -10,8 +10,10 @@ description: Appends one or more new entries to a stream. - The stream entry ID. If `*` is passed, a new ID will be generated - automatically. + The stream entry ID. Supports multiple formats: + - `*`: Fully automatic ID generation (both timestamp and sequence) + - `-`: Explicit ID (e.g., "1526919030474-55") + - `-*`: Auto-generate sequence number for the given millisecond timestamp (Redis 8+) @@ -54,5 +56,46 @@ redis.xadd("mystream", "1634567890123-0", {"temperature": 25.5, "humidity": 60}) ```py Approximate trim with maxlen redis.xadd("mystream", "*", {"log_level": "error", "message": "Database connection failed"}, maxlen=100) ``` + +```py Auto Sequence Number (Redis 8+) +import time + +# Specify millisecond timestamp, let Redis generate sequence number +ms = int(time.time() * 1000) +id1 = redis.xadd("mystream", f"{ms}-*", {"event": "login", "user": "john"}) +print(id1) # e.g., "1769347235123-0" + +# Multiple entries with same ms but different sequence numbers +id2 = redis.xadd("mystream", f"{ms}-*", {"event": "logout", "user": "john"}) +print(id2) # e.g., "1769347235123-1" +``` + +```py Precise Timestamp Control +# Use auto sequence for precise timestamp control +timestamp = 1634567890123 +result = redis.xadd( + "events", + f"{timestamp}-*", + {"type": "payment", "amount": 100.00, "currency": "USD"} +) +# Redis generates the sequence number automatically +``` +## ID Format Details + +### Automatic ID (`*`) +Fully automatic - Redis generates both the millisecond timestamp and sequence number. + +### Explicit ID (`-`) +You provide both the millisecond timestamp and sequence number. Example: `"1526919030474-55"` + +### Auto Sequence (`-*`) - Redis 8+ +You provide the millisecond timestamp, Redis automatically generates the sequence number. This is useful when you need precise control over the timestamp but want Redis to handle sequence numbering. + +**Benefits of Auto Sequence:** +- Precise timestamp control for time-sensitive data +- Automatic sequence management prevents conflicts +- Multiple entries can share the same millisecond with different sequences +- Ideal for batch operations with consistent timestamps + diff --git a/redis/sdks/py/commands/stream/xdelex.mdx b/redis/sdks/py/commands/stream/xdelex.mdx new file mode 100644 index 00000000..4657b97a --- /dev/null +++ b/redis/sdks/py/commands/stream/xdelex.mdx @@ -0,0 +1,116 @@ +--- +title: XDELEX +description: Extended delete for streams with reference control. +--- + +The `XDELEX` command provides extended deletion capabilities for stream entries with additional control options. This is an enhanced version of XDEL with more advanced features for managing stream data. + +## Arguments + + + The key of the stream. + + + + One or more stream entry IDs to delete. + + + + Optional deletion behavior (case-insensitive): + - `KEEPREF` or `keepref`: Keep consumer group references + - `DELREF` or `delref`: Delete consumer group references + - `ACKED` or `acked`: Only acknowledge messages (don't delete) + + +## Response + + + A list of integers indicating the result for each ID in the same order as provided. + + + +```py Single Entry +# Delete a single stream entry +result = redis.xdelex("mystream", "1638360173533-0") +print(result) # List of results for each ID +``` + +```py Multiple Entries +# Delete multiple stream entries +result = redis.xdelex( + "mystream", + "1638360173533-0", + "1638360173533-1", + "1638360173533-2" +) +print(result) # List of results for each ID +``` + +```py With Option +# Use KEEPREF option to keep consumer group references +result = redis.xdelex( + "mystream", + "1638360173533-0", + "1638360173533-1", + option="KEEPREF" +) +print(result) # List of results for each ID + +# Use DELREF option to delete consumer group references +result = redis.xdelex( + "mystream", + "1638360173533-0", + option="DELREF" +) +print(result) # List of results for each ID + +# Use ACKED option to only acknowledge (don't delete) +result = redis.xdelex( + "mystream", + "1638360173533-0", + option="ACKED" +) +print(result) # List of results for each ID +``` + +```py With Stream Processing +# Add entries to stream +redis.xadd("events", "*", {"type": "login", "user": "john"}) +redis.xadd("events", "*", {"type": "logout", "user": "john"}) + +# Read entries +entries = redis.xrange("events", "-", "+") +print(entries) + +# Delete specific entries +deleted = redis.xdelex( + "events", + entries[0][0], # First entry ID + entries[1][0] # Second entry ID +) +print(f"Results: {deleted}") # List of results for each ID +``` + +```py Cleanup Old Entries +# Get entries older than a certain timestamp +old_entries = redis.xrange("logs", "-", "1638360000000-0") + +# Delete old entries +if old_entries: + ids = [entry[0] for entry in old_entries] + deleted = redis.xdelex("logs", *ids) + print(f"Results: {deleted}") # List of results for each ID +``` + + +## Use Cases + +- **Stream Cleanup**: Remove processed or expired stream entries +- **Data Retention**: Implement custom retention policies +- **Error Handling**: Delete corrupted or invalid stream entries +- **Memory Management**: Clean up streams to free memory + + +This command is available in Redis 8.2.0 and later. It provides extended functionality compared to the standard XDEL command. + + diff --git a/redis/sdks/ts/commands/bitmap/bitop.mdx b/redis/sdks/ts/commands/bitmap/bitop.mdx index 2c7aecf8..3de3d8f6 100644 --- a/redis/sdks/ts/commands/bitmap/bitop.mdx +++ b/redis/sdks/ts/commands/bitmap/bitop.mdx @@ -3,13 +3,25 @@ title: BITOP description: Perform bitwise operations between strings. --- -The `BITOP` command in Redis is used to perform bitwise operations on multiple keys (or Redis strings) and store the result in a destination key. It is primarily used for performing logical AND, OR, XOR, and NOT operations on binary data stored in Redis. +The `BITOP` command in Redis is used to perform bitwise operations on multiple keys (or Redis strings) and store the result in a destination key. It supports standard logical operations (AND, OR, XOR, NOT) and advanced operations (DIFF, DIFF1, ANDOR, ONE) for complex bit manipulation. ## Arguments - - Specifies the type of bitwise operation to perform, which can be one of the following: `AND`, `OR`, `XOR`, or `NOT`. + + Specifies the type of bitwise operation to perform: + + **Standard Operations:** + - `and`: Bitwise AND + - `or`: Bitwise OR + - `xor`: Bitwise XOR + - `not`: Bitwise NOT (only accepts one source key) + + **Advanced Operations (Redis 8.2+):** + - `diff`: A bit is set only if it's set in all source bitmaps + - `diff1`: A bit is set if it's set in the first key but not in any of the other keys + - `andor`: A bit is set if it's set in X and also in one or more of Y1, Y2, ... + - `one`: A bit is set if it's set in exactly one source key @@ -27,7 +39,104 @@ The `BITOP` command in Redis is used to perform bitwise operations on multiple k -```ts Example - await redis.bitop("AND", "destKey", "sourceKey1", "sourceKey2"); +```ts Standard Operations +// AND operation +await redis.bitop("and", "destKey", "sourceKey1", "sourceKey2"); + +// OR operation +await redis.bitop("or", "destKey", "sourceKey1", "sourceKey2"); + +// XOR operation +await redis.bitop("xor", "destKey", "sourceKey1", "sourceKey2"); + +// NOT operation (only accepts one source key) +await redis.bitop("not", "destKey", "sourceKey"); +``` + +```ts DIFF Operation (Redis 8.2+) +// Set bits in multiple bitmaps +await redis.setbit("bitmap1", 0, 1); +await redis.setbit("bitmap1", 1, 1); +await redis.setbit("bitmap2", 1, 1); +await redis.setbit("bitmap2", 2, 1); + +// DIFF: bits set in all sources +await redis.bitop("diff", "result", "bitmap1", "bitmap2"); +// Result: bit 1 is set (only bit present in both) +``` + +```ts DIFF1 Operation (Redis 8.2+) +// Set bits in bitmaps +await redis.setbit("exclude", 0, 1); +await redis.setbit("exclude", 1, 1); +await redis.setbit("include1", 1, 1); +await redis.setbit("include2", 2, 1); + +// DIFF1: bits in first key but not in others +await redis.bitop("diff1", "result", "exclude", "include1", "include2"); +// Result: bit 0 is set (only in exclude, not in include1 or include2) +``` + +```ts ANDOR Operation (Redis 8.2+) +// Set bits in bitmaps +await redis.setbit("required", 0, 1); +await redis.setbit("required", 1, 1); +await redis.setbit("option1", 1, 1); +await redis.setbit("option2", 2, 1); + +// ANDOR: bits in required AND in at least one option +await redis.bitop("andor", "result", "required", "option1", "option2"); +// Result: bit 1 is set (in required AND in option1) +``` + +```ts ONE Operation (Redis 8.2+) +// Set bits in multiple sources +await redis.setbit("source1", 0, 1); +await redis.setbit("source1", 1, 1); +await redis.setbit("source2", 1, 1); +await redis.setbit("source2", 2, 1); +await redis.setbit("source3", 2, 1); + +// ONE: bits set in exactly one source +await redis.bitop("one", "result", "source1", "source2", "source3"); +// Result: bit 0 is set (only in source1) ``` + +## Advanced Operations Details (Redis 8.2+) + +### DIFF +A bit in the destination is set if it is set in **all** source bitmaps. This is equivalent to the intersection of all source bitmaps. + +**Use Cases:** +- Finding common features across multiple datasets +- Identifying users present in all segments +- Intersection of multiple filters + +### DIFF1 +A bit in the destination is set if it is set in the **first key** but **not in any of the other keys**. This finds bits unique to the first bitmap. + +**Use Cases:** +- Finding exclusive features +- Identifying users in one segment but not in others +- Exclusion filtering + +### ANDOR +A bit in the destination is set if it is set in **X and also in one or more of Y1, Y2, ...**. This implements "X AND (Y1 OR Y2 OR ...)" logic. + +**Use Cases:** +- Required condition with optional alternatives +- Users with a required attribute and at least one optional attribute +- Complex filtering with mandatory and optional criteria + +### ONE +A bit in the destination is set if it is set in **exactly one** of the source keys. This finds bits that are unique to a single source. + +**Use Cases:** +- Finding unique occurrences +- Identifying exclusive memberships +- Detecting singular conditions across multiple sets + + +The advanced operations (DIFF, DIFF1, ANDOR, ONE) are available in Redis 8.2.0 and later. + diff --git a/redis/sdks/ts/commands/connection/client_setinfo.mdx b/redis/sdks/ts/commands/connection/client_setinfo.mdx new file mode 100644 index 00000000..811972e5 --- /dev/null +++ b/redis/sdks/ts/commands/connection/client_setinfo.mdx @@ -0,0 +1,91 @@ +--- +title: CLIENT SETINFO +description: Set client library name and version information. +--- + +The `CLIENT SETINFO` command sets client library name and version information that will be shown in CLIENT LIST and CLIENT INFO commands. This helps identify which client library is being used and is useful for debugging and monitoring. + +## Arguments + + + The attribute to set: + - `LIB-NAME` or `lib-name`: Library name + - `LIB-VER` or `lib-ver`: Library version + + + + The value to set for the attribute. + + +## Response + + + Returns `OK` if the attribute was successfully set. + + + +```ts Set Library Name +// Set the library name +await redis.clientSetInfo("LIB-NAME", "redis-js"); +// Returns: "OK" +``` + +```ts Set Library Version +// Set the library version +await redis.clientSetInfo("LIB-VER", "1.0.0"); +// Returns: "OK" +``` + +```ts Lowercase Attributes +// Attributes are case-insensitive +await redis.clientSetInfo("lib-name", "redis-js"); +await redis.clientSetInfo("lib-ver", "2.0.0"); +``` + +```ts With Custom Suffix +// Common pattern: include wrapper or framework info +await redis.clientSetInfo("LIB-NAME", "redis-js(upstash_v1.0.0)"); +await redis.clientSetInfo("LIB-VER", "1.0.0"); +``` + +```ts Complete Setup +// Set both library name and version +await redis.clientSetInfo("LIB-NAME", "my-redis-wrapper"); +await redis.clientSetInfo("LIB-VER", "3.2.1"); + +// Now this information will appear in CLIENT LIST output +``` + + +## Use Cases + +- **Debugging**: Identify which client library version is being used +- **Monitoring**: Track different client libraries connecting to Redis +- **Analytics**: Understand client library distribution across connections +- **Support**: Quickly identify client versions when troubleshooting issues + +## Viewing Client Information + +After setting client info, you can view it using the CLIENT LIST command: + +```ts +// Set client information +await redis.clientSetInfo("LIB-NAME", "redis-js"); +await redis.clientSetInfo("LIB-VER", "1.0.0"); + +// View all clients (if you have access to CLIENT LIST) +// The output will include lib-name and lib-ver fields +``` + +## Best Practices + +1. **Set on Connection**: Set client info immediately after establishing a connection +2. **Include Version**: Always set both library name and version +3. **Use Descriptive Names**: Make library names easily identifiable +4. **Version Format**: Use semantic versioning (e.g., "1.2.3") +5. **Include Context**: Add wrapper or framework info if applicable + + +This command is available in Redis 7.2.0 and later. The attribute names are case-insensitive and will be normalized to uppercase. + + diff --git a/redis/sdks/ts/commands/hash/hgetdel.mdx b/redis/sdks/ts/commands/hash/hgetdel.mdx new file mode 100644 index 00000000..d4af40f7 --- /dev/null +++ b/redis/sdks/ts/commands/hash/hgetdel.mdx @@ -0,0 +1,67 @@ +--- +title: HGETDEL +description: Get and delete hash fields atomically. +--- + +The `HGETDEL` command returns the values of the specified fields and then atomically deletes them from the hash. This is useful when you need to retrieve and remove data in a single atomic operation. + +## Arguments + + + The key of the hash. + + + + One or more field names to get and delete. + + +## Response + + + An object containing the field names and their values in the format `{[fieldName: string]: TValue | null}`. Returns `null` for fields that do not exist. If the hash doesn't exist or all fields are non-existent, `null` is returned. + + + +```ts Basic Example +// Set some hash fields +await redis.hset("user:123", { name: "John", age: "30", email: "john@example.com" }); + +// Get and delete specific fields +const result = await redis.hgetdel("user:123", "name", "email"); +console.log(result); // { name: "John", email: "john@example.com" } + +// Verify fields were deleted +const name = await redis.hget("user:123", "name"); +console.log(name); // null +``` + +```ts Multiple Fields +await redis.hset("session:abc", { + token: "xyz123", + user: "john", + expires: "2024-12-31" +}); + +// Get and delete all fields +const session = await redis.hgetdel("session:abc", "token", "user", "expires"); +console.log(session); +// { token: "xyz123", user: "john", expires: "2024-12-31" } +``` + +```ts Non-existent Fields +await redis.hset("user:456", { name: "Jane" }); + +// Get and delete with non-existent field +const result = await redis.hgetdel("user:456", "name", "email"); +console.log(result); // { name: "Jane", email: null } +``` + + +## Use Cases + +- **Session Management**: Retrieve and invalidate session data atomically +- **Cache Cleanup**: Get cached data while removing it from storage +- **Queue Processing**: Fetch and remove job data in a single operation +- **Temporary Data**: Retrieve one-time use tokens or codes while deleting them + + diff --git a/redis/sdks/ts/commands/hash/hgetex.mdx b/redis/sdks/ts/commands/hash/hgetex.mdx new file mode 100644 index 00000000..ba773410 --- /dev/null +++ b/redis/sdks/ts/commands/hash/hgetex.mdx @@ -0,0 +1,123 @@ +--- +title: HGETEX +description: Get hash fields with expiration support. +--- + +The `HGETEX` command returns the values of the specified fields and optionally sets their expiration time or TTL. This allows you to retrieve hash data while managing its lifetime in a single operation. + +## Arguments + + + The key of the hash. + + + + Expiration options for the hash fields. The options are mutually exclusive - you can only use one at a time. + + + + Set expiration time in seconds. Cannot be used with other expiration options. + + + + Set expiration time in milliseconds. Cannot be used with other expiration options. + + + + Set expiration as Unix timestamp in seconds. Cannot be used with other expiration options. + + + + Set expiration as Unix timestamp in milliseconds. Cannot be used with other expiration options. + + + + Remove expiration from the hash. Must be set to `true`. Cannot be used with other expiration options. + + + + + + One or more field names to get. + + +## Response + + + An object containing the field names and their values in the format `{[fieldName: string]: TValue | null}`. Returns `null` for fields that do not exist. If the hash doesn't exist or all fields are non-existent, `null` is returned. + + + +```ts With EX (seconds) +await redis.hset("user:123", { name: "John", email: "john@example.com" }); + +// Get fields and set expiration to 60 seconds +const result = await redis.hgetex("user:123", { ex: 60 }, "name", "email"); +console.log(result); // { name: "John", email: "john@example.com" } +``` + +```ts With PX (milliseconds) +await redis.hset("session:abc", { token: "xyz123", user: "john" }); + +// Get fields and set expiration to 30000 milliseconds (30 seconds) +const result = await redis.hgetex("session:abc", { px: 30000 }, "token", "user"); +console.log(result); // { token: "xyz123", user: "john" } +``` + +```ts With EXAT (Unix timestamp in seconds) +await redis.hset("cache:data", { value: "cached" }); + +// Set expiration to specific timestamp (1 hour from now) +const futureTime = Math.floor(Date.now() / 1000) + 3600; +const result = await redis.hgetex("cache:data", { exat: futureTime }, "value"); +console.log(result); // { value: "cached" } +``` + +```ts With PXAT (Unix timestamp in milliseconds) +await redis.hset("temp:data", { info: "temporary" }); + +// Set expiration to specific timestamp in milliseconds +const futureTime = Date.now() + 60000; // 1 minute from now +const result = await redis.hgetex("temp:data", { pxat: futureTime }, "info"); +console.log(result); // { info: "temporary" } +``` + +```ts With PERSIST +await redis.hset("user:456", { name: "Jane" }); +await redis.expire("user:456", 300); // Set 5 minute expiration + +// Get fields and remove expiration +const result = await redis.hgetex("user:456", { persist: true }, "name"); +console.log(result); // { name: "Jane" } + +// Verify expiration was removed +const ttl = await redis.ttl("user:456"); +console.log(ttl); // -1 (no expiration) +``` + +```ts Without Expiration Options +await redis.hset("data:xyz", { field1: "value1", field2: "value2" }); + +// Just get fields without changing expiration +// Pass an empty object or an object with no expiration properties +const result = await redis.hgetex("data:xyz", {}, "field1", "field2"); +console.log(result); // { field1: "value1", field2: "value2" } +``` + +```ts Non-existent Fields +await redis.hset("user:789", { name: "Bob" }); + +// Get fields including one that doesn't exist +const result = await redis.hgetex("user:789", {}, "name", "email"); +console.log(result); // { name: "Bob", email: null } +``` + + +## Use Cases + +- **Session Management**: Retrieve session data and extend its lifetime +- **Cache Refresh**: Get cached data and update its TTL +- **Temporary Data**: Access temporary data while managing its expiration +- **Rate Limiting**: Fetch rate limit data and reset expiration + + diff --git a/redis/sdks/ts/commands/hash/hsetex.mdx b/redis/sdks/ts/commands/hash/hsetex.mdx new file mode 100644 index 00000000..cf17d401 --- /dev/null +++ b/redis/sdks/ts/commands/hash/hsetex.mdx @@ -0,0 +1,186 @@ +--- +title: HSETEX +description: Set hash fields with expiration support. +--- + +The `HSETEX` command sets the specified fields with their values and optionally sets their expiration time or TTL. It supports conditional operations to control when fields should be set. + +## Arguments + + + The key of the hash. + + + + Options for conditional setting and expiration. + + + + Conditional setting options (case-insensitive): + - `FNX`: Only set fields if the hash does not exist + - `FXX`: Only set fields if the hash already exists + + + + Expiration options for the hash. + + + + Set expiration time in seconds. + + + + Set expiration time in milliseconds. + + + + Set expiration as Unix timestamp in seconds. + + + + Set expiration as Unix timestamp in milliseconds. + + + + Retain the existing time to live (TTL) associated with the hash key when setting fields. If the hash has an expiration, it will be preserved. Set to `true` to enable. + + + + + + + + An object of fields and their values to set. + + +## Response + + + The number of fields that were set. Returns 0 if conditions are not met (e.g., `conditional: "FNX"` but hash exists, or `conditional: "FXX"` but hash doesn't exist). + + + +```ts Basic Example +// Set fields with 1 hour expiration +await redis.hsetex("user:123", { expiration: { ex: 3600 } }, { + name: "John", + email: "john@example.com" +}); +``` + +```ts With FNX (only if hash doesn't exist) +// Set fields only if the hash doesn't exist +const result = await redis.hsetex( + "user:456", + { conditional: "FNX" }, + { name: "Jane", age: "25" } +); +console.log(result); // 2 (if hash didn't exist) + +// Try again - will return 0 since hash now exists +const result2 = await redis.hsetex( + "user:456", + { conditional: "FNX" }, + { email: "jane@example.com" } +); +console.log(result2); // 0 +``` + +```ts With FXX (only if hash exists) +// First create the hash +await redis.hset("session:abc", { token: "xyz" }); + +// Update only if hash exists +const result = await redis.hsetex( + "session:abc", + { conditional: "FXX" }, + { user: "john" } +); +console.log(result); // 1 (hash exists, field added) + +// Try on non-existent hash +const result2 = await redis.hsetex( + "session:nonexistent", + { conditional: "FXX" }, + { user: "jane" } +); +console.log(result2); // 0 (hash doesn't exist) +``` + +```ts With PX (milliseconds) +// Set fields with 30 second expiration +await redis.hsetex("cache:data", { expiration: { px: 30000 } }, { + value: "cached data", + timestamp: Date.now().toString() +}); +``` + +```ts With EXAT (Unix timestamp in seconds) +// Set expiration to specific timestamp +const futureTime = Math.floor(Date.now() / 1000) + 7200; // 2 hours from now +await redis.hsetex("temp:data", { expiration: { exat: futureTime } }, { + info: "temporary information" +}); +``` + +```ts With PXAT (Unix timestamp in milliseconds) +// Set expiration to specific timestamp in milliseconds +const futureTime = Date.now() + 300000; // 5 minutes from now +await redis.hsetex("session:xyz", { expiration: { pxat: futureTime } }, { + token: "abc123", + user: "john" +}); +``` + +```ts Combined: Conditional + Expiration +// Set fields only if hash doesn't exist, with 1 hour expiration +await redis.hsetex( + "user:789", + { + conditional: "FNX", + expiration: { ex: 3600 } + }, + { + name: "Alice", + email: "alice@example.com", + created: Date.now().toString() + } +); +``` + +```ts With KEEPTTL +// First set fields with expiration +await redis.hsetex("cache:data", { expiration: { ex: 300 } }, { + value: "cached" +}); + +// Later update fields while retaining the existing TTL +const result = await redis.hsetex( + "cache:data", + { expiration: { keepttl: true } }, + { updated: "yes" } +); +console.log(result); // 1 + +// Verify TTL is still 300 seconds (or less if time passed) +const ttl = await redis.ttl("cache:data"); +console.log(ttl); // Should be > 0 and <= 300 (TTL was retained) +``` + +```ts Without Options +// Just set fields without expiration or conditions +await redis.hsetex("data:simple", undefined, { + field1: "value1", + field2: "value2" +}); +``` + + +## Use Cases + +- **Session Management**: Create sessions with automatic expiration +- **Cache with TTL**: Store cached data that expires automatically +- **Temporary Data**: Create temporary records with built-in cleanup +- **Rate Limiting**: Store rate limit counters with automatic reset +- **Conditional Updates**: Ensure data consistency with FNX/FXX options + diff --git a/redis/sdks/ts/commands/overview.mdx b/redis/sdks/ts/commands/overview.mdx index 8e60012e..33a7dc2b 100644 --- a/redis/sdks/ts/commands/overview.mdx +++ b/redis/sdks/ts/commands/overview.mdx @@ -15,6 +15,15 @@ description: Available Commands in @upstash/redis Ping the server. + + + + + + + Set client library name and version information. + + @@ -24,7 +33,7 @@ description: Available Commands in @upstash/redis Count set bits in a string. - Perform bitwise operations between strings. + Perform bitwise operations between strings (includes DIFF, DIFF1, ANDOR, ONE). Find first bit set or clear in a string. @@ -148,6 +157,12 @@ description: Available Commands in @upstash/redis + + + Get and delete hash fields atomically. + + + Get hash fields with expiration support. @@ -187,6 +202,9 @@ description: Available Commands in @upstash/redis + + + Set hash fields with expiration support. @@ -558,8 +576,11 @@ Publish messages to many clients Acknowledge one or multiple messages as processed for a consumer group. + + Acknowledge and delete stream entries atomically. + - Append a new entry to a stream. + Append a new entry to a stream (supports auto sequence numbers). Transfer ownership of pending messages to another consumer automatically. @@ -570,6 +591,9 @@ Publish messages to many clients Remove one or multiple entries from a stream. + + Extended delete for streams with reference control. + Manage consumer groups for Redis streams. diff --git a/redis/sdks/ts/commands/stream/xackdel.mdx b/redis/sdks/ts/commands/stream/xackdel.mdx new file mode 100644 index 00000000..bb81c52b --- /dev/null +++ b/redis/sdks/ts/commands/stream/xackdel.mdx @@ -0,0 +1,211 @@ +--- +title: XACKDEL +description: Acknowledge and delete stream entries atomically. +--- + +The `XACKDEL` command acknowledges and deletes stream entries atomically in a single operation. This is useful for consumer groups where you want to acknowledge message processing and remove the entries from the stream simultaneously. + +## Arguments + + + The key of the stream. + + + + The consumer group name. + + + + Optional deletion behavior (case-insensitive). When provided, must come before the IDs: + - `KEEPREF`: Keep consumer group references + - `DELREF`: Delete consumer group references + - `ACKED`: Only acknowledge messages (don't delete) + + If not provided, entries are acknowledged and deleted by default. + + + + One or more stream entry IDs to acknowledge and delete. When an option is provided, IDs must come after the option. + + +## Response + + + An array indicating the result for each ID in the same order as provided. + + + +```ts Single Entry +// Acknowledge and delete a single entry +const result = await redis.xackdel("mystream", "mygroup", "1638360173533-0"); +console.log(result); // Array of results for each ID +``` + +```ts Multiple Entries +// Acknowledge and delete multiple entries +const result = await redis.xackdel( + "mystream", + "mygroup", + "1638360173533-0", + "1638360173533-1", + "1638360173533-2" +); +console.log(result); // Array of results for each ID +``` + +```ts With KEEPREF Option +// Keep consumer group references when deleting entries +// Useful when you want to delete entries but maintain group tracking +const result = await redis.xackdel( + "mystream", + "mygroup", + "KEEPREF", + "1638360173533-0", + "1638360173533-1", +); +console.log(result); // Array of results for each ID +``` + +```ts With DELREF Option +// Delete consumer group references along with entries +// Useful for complete cleanup of processed messages +const result = await redis.xackdel( + "mystream", + "mygroup", + "DELREF", + "1638360173533-0", + "1638360173533-1", +); +console.log(result); // Array of results for each ID +``` + +```ts With ACKED Option +// Only acknowledge messages without deleting them +// Useful when you want to mark as processed but keep for audit/debugging +const result = await redis.xackdel( + "mystream", + "mygroup", + "ACKED", + "1638360173533-0", +); +console.log(result); // Array of results for each ID +// Entry remains in stream but is acknowledged +``` + +```ts Consumer Group Processing +// Create a consumer group +await redis.xgroup("CREATE", "orders", "processors", "0", "MKSTREAM"); + +// Add some orders to the stream +await redis.xadd("orders", "*", { order_id: "123", status: "pending" }); +await redis.xadd("orders", "*", { order_id: "124", status: "pending" }); + +// Read messages as a consumer +const messages = await redis.xreadgroup( + "GROUP", "processors", "consumer1", + "COUNT", 2, + "STREAMS", "orders", ">" +); + +// Process messages and acknowledge + delete them +for (const [stream, entries] of messages) { + for (const [id, fields] of entries) { + // Process the order... + console.log(`Processing order ${fields.order_id}`); + + // Acknowledge and delete the entry (default behavior) + await redis.xackdel("orders", "processors", id); + + // Or use DELREF for complete cleanup + // await redis.xackdel("orders", "processors", "DELREF", id); + } +} +``` + +```ts Batch Processing +// Read pending messages +const pending = await redis.xreadgroup( + "GROUP", "tasks", "worker1", + "COUNT", 10, + "STREAMS", "task-queue", ">" +); + +if (pending && pending.length > 0) { + const [stream, entries] = pending[0]; + const processedIds: string[] = []; + + // Process each task + for (const [id, fields] of entries) { + try { + // Process task... + console.log(`Processing task ${id}`); + processedIds.push(id); + } catch (error) { + console.error(`Failed to process ${id}:`, error); + } + } + + // Acknowledge and delete all successfully processed tasks + // Using DELREF to completely remove references + if (processedIds.length > 0) { + const result = await redis.xackdel("task-queue", "tasks", "DELREF", ...processedIds); + console.log(`Results: ${result}`); // Array of results for each ID + } +} +``` + +```ts Audit Mode with ACKED +// Process messages but keep them for audit/debugging purposes +const messages = await redis.xreadgroup( + "GROUP", "audit", "auditor1", + "COUNT", 5, + "STREAMS", "events", ">" +); + +for (const [stream, entries] of messages) { + const entryIds = entries.map(([id]) => id); + + // Process and acknowledge but don't delete + for (const [id, fields] of entries) { + // Audit the event... + console.log(`Auditing event ${JSON.stringify(fields)}`); + } + + // Acknowledge without deleting (keep for audit trail) + const result = await redis.xackdel("events", "audit", "ACKED", ...entryIds); + console.log(`Acknowledged ${result.length} events (kept in stream)`); +} +``` + + +## Use Cases + +- **Message Queue Cleanup**: Process messages and remove them from the stream in one operation +- **Event Processing**: Acknowledge event handling and clean up the stream +- **Task Queue Management**: Complete tasks and remove them atomically +- **Memory Optimization**: Reduce stream memory usage by removing processed entries + +## Comparison with XACK + XDEL + +Traditional approach (two operations): +```ts +// Acknowledge the message +await redis.xack("mystream", "mygroup", "123-0"); +// Then delete it +await redis.xdel("mystream", "123-0"); +``` + +With XACKDEL (single atomic operation): +```ts +// Acknowledge and delete in one operation (default) +await redis.xackdel("mystream", "mygroup", "123-0"); + +// With options for fine-grained control +await redis.xackdel("mystream", "mygroup", "DELREF", "123-0"); // Complete cleanup +await redis.xackdel("mystream", "mygroup", "ACKED", "123-0"); // Acknowledge only +``` + + +This command is available in Redis 8.2.0 and later. It combines XACK and XDEL into a single atomic operation, which is more efficient and ensures consistency. + + diff --git a/redis/sdks/ts/commands/stream/xadd.mdx b/redis/sdks/ts/commands/stream/xadd.mdx index aee16c95..a75b917a 100644 --- a/redis/sdks/ts/commands/stream/xadd.mdx +++ b/redis/sdks/ts/commands/stream/xadd.mdx @@ -10,8 +10,10 @@ description: Appends one or more new entries to a stream. - The stream entry ID. If `*` is passed, a new ID will be generated - automatically. + The stream entry ID. Supports multiple formats: + - `*`: Fully automatic ID generation (both timestamp and sequence) + - `-`: Explicit ID (e.g., "1526919030474-55") + - `-*`: Auto-generate sequence number for the given millisecond timestamp (Redis 8+) @@ -92,5 +94,44 @@ const result = await redis.xadd("mystream", "*", { action: "purchase", amount: 9 } }); ``` + +```ts Auto Sequence Number (Redis 8+) +// Specify millisecond timestamp, let Redis generate sequence number +const ms = Date.now(); +const id1 = await redis.xadd("mystream", `${ms}-*`, { event: "login", user: "john" }); +console.log(id1); // e.g., "1769347235123-0" + +// Multiple entries with same ms but different sequence numbers +const id2 = await redis.xadd("mystream", `${ms}-*`, { event: "logout", user: "john" }); +console.log(id2); // e.g., "1769347235123-1" +``` + +```ts Precise Timestamp Control +// Use auto sequence for precise timestamp control +const timestamp = 1634567890123; +const result = await redis.xadd("events", `${timestamp}-*`, { + type: "payment", + amount: 100.00, + currency: "USD" +}); +// Redis generates the sequence number automatically +``` +## ID Format Details + +### Automatic ID (`*`) +Fully automatic - Redis generates both the millisecond timestamp and sequence number. + +### Explicit ID (`-`) +You provide both the millisecond timestamp and sequence number. Example: `"1526919030474-55"` + +### Auto Sequence (`-*`) - Redis 8+ +You provide the millisecond timestamp, Redis automatically generates the sequence number. This is useful when you need precise control over the timestamp but want Redis to handle sequence numbering. + +**Benefits of Auto Sequence:** +- Precise timestamp control for time-sensitive data +- Automatic sequence management prevents conflicts +- Multiple entries can share the same millisecond with different sequences +- Ideal for batch operations with consistent timestamps + diff --git a/redis/sdks/ts/commands/stream/xdelex.mdx b/redis/sdks/ts/commands/stream/xdelex.mdx new file mode 100644 index 00000000..f160a61e --- /dev/null +++ b/redis/sdks/ts/commands/stream/xdelex.mdx @@ -0,0 +1,119 @@ +--- +title: XDELEX +description: Extended delete for streams with reference control. +--- + +The `XDELEX` command provides extended deletion capabilities for stream entries with additional control options. This is an enhanced version of XDEL with more advanced features for managing stream data. + +## Arguments + + + The key of the stream. + + + + Optional deletion behavior (case-insensitive). When provided, must come before the IDs: + - `KEEPREF`: Keep consumer group references + - `DELREF`: Delete consumer group references + - `ACKED`: Only acknowledge messages (don't delete) + + If not provided, entries are deleted by default. + + + + One or more stream entry IDs to delete. When an option is provided, IDs must come after the option. + + +## Response + + + An array of numbers indicating the result for each ID in the same order as provided. + + + +```ts Single Entry +// Delete a single stream entry +const result = await redis.xdelex("mystream", "1638360173533-0"); +console.log(result); // Array of results for each ID +``` + +```ts Multiple Entries +// Delete multiple stream entries +const result = await redis.xdelex( + "mystream", + "1638360173533-0", + "1638360173533-1", + "1638360173533-2" +); +console.log(result); // Array of results for each ID +``` + +```ts With Option +// Use KEEPREF option to keep consumer group references +const result = await redis.xdelex( + "mystream", + "KEEPREF", + "1638360173533-0", + "1638360173533-1" +); +console.log(result); // Array of results for each ID + +// Use DELREF option to delete consumer group references +const result2 = await redis.xdelex( + "mystream", + "DELREF", + "1638360173533-0" +); +console.log(result2); // Array of results for each ID + +// Use ACKED option to only acknowledge (don't delete) +const result3 = await redis.xdelex( + "mystream", + "ACKED", + "1638360173533-0" +); +console.log(result3); // Array of results for each ID +``` + +```ts With Stream Processing +// Add entries to stream +await redis.xadd("events", "*", { type: "login", user: "john" }); +await redis.xadd("events", "*", { type: "logout", user: "john" }); + +// Read entries +const entries = await redis.xrange("events", "-", "+"); +console.log(entries); + +// Delete specific entries +const deleted = await redis.xdelex( + "events", + entries[0][0], // First entry ID + entries[1][0] // Second entry ID +); +console.log(`Results: ${deleted}`); // Array of results for each ID +``` + +```ts Cleanup Old Entries +// Get entries older than a certain timestamp +const oldEntries = await redis.xrange("logs", "-", "1638360000000-0"); + +// Delete old entries +if (oldEntries.length > 0) { + const ids = oldEntries.map(entry => entry[0]); + const deleted = await redis.xdelex("logs", ...ids); + console.log(`Results: ${deleted}`); // Array of results for each ID +} +``` + + +## Use Cases + +- **Stream Cleanup**: Remove processed or expired stream entries +- **Data Retention**: Implement custom retention policies +- **Error Handling**: Delete corrupted or invalid stream entries +- **Memory Management**: Clean up streams to free memory + + +This command is available in Redis 8.2.0 and later. It provides extended functionality compared to the standard XDEL command. + +