Skip to content

Ask the HotKV docs

Answers come only from these docs and can be wrong; check the linked pages before you rely on them.

Lists

Last updated

A list is an ordered sequence of strings. You add and remove elements at either end, read ranges by position, and can block until an element arrives. Lists suit job queues, stacks, activity feeds (“the last 100 events”), and handing work from producers to consumers.

Elements keep the order you inserted them in, duplicates are allowed, and positions start at 0 from the head (left). Negative positions count from the tail: -1 is the last element.

There is no separate create step: pushing to a missing key creates the list, and a list whose last element is removed is deleted.

127.0.0.1:6379> RPUSH tasks "resize:1" "resize:2" "resize:3"
(integer) 3
127.0.0.1:6379> LPUSH tasks "urgent:9"
(integer) 4
127.0.0.1:6379> LRANGE tasks 0 -1
1) "urgent:9"
2) "resize:1"
3) "resize:2"
4) "resize:3"
127.0.0.1:6379> LLEN tasks
(integer) 4
127.0.0.1:6379> LINDEX tasks -1
"resize:3"
127.0.0.1:6379> LSET tasks 1 "resize:1b"
OK
127.0.0.1:6379> LPOP tasks
"urgent:9"
127.0.0.1:6379> RPOP tasks 2
1) "resize:3"
2) "resize:2"
127.0.0.1:6379> LPOP tasks
"resize:1b"
127.0.0.1:6379> EXISTS tasks
(integer) 0

To change the middle of a list, use LINSERT to add before or after a given element, LREM to remove occurrences of a value, and LPOS to find where a value is.

127.0.0.1:6379> RPUSH letters a b c b
(integer) 4
127.0.0.1:6379> LPOS letters b
(integer) 1
127.0.0.1:6379> LPOS letters b COUNT 0
1) (integer) 1
2) (integer) 3
127.0.0.1:6379> LINSERT letters BEFORE c x
(integer) 5
127.0.0.1:6379> LREM letters 0 b
(integer) 2
127.0.0.1:6379> LRANGE letters 0 -1
1) "a"
2) "x"
3) "c"

LPUSHX and RPUSHX push only when the list already exists.

Queue (first in, first out). Producers RPUSH, consumers LPOP. Stack (last in, first out). Push and pop at the same end.

Capped feed. Keep only the newest N items by trimming after each push:

127.0.0.1:6379> LPUSH feed:user:42 "liked post 7"
(integer) 1
127.0.0.1:6379> LTRIM feed:user:42 0 99
OK

Reliable processing. LMOVE pops from one list and pushes onto another atomically. Move a job from jobs:pending to jobs:processing, and remove it from there with LREM once it is done. If a worker dies, the job is still in jobs:processing for another worker to pick up.

127.0.0.1:6379> RPUSH jobs:pending "job:1" "job:2"
(integer) 2
127.0.0.1:6379> LMOVE jobs:pending jobs:processing LEFT RIGHT
"job:1"
127.0.0.1:6379> LREM jobs:processing 1 "job:1"
(integer) 1

Pop from several lists. LMPOP pops from the first non-empty list among the keys you name, which lets you poll queues in priority order.

Instead of polling, a consumer can wait for work. BLPOP and BRPOP block until one of the lists has an element or the timeout (in seconds, fractions allowed, 0 for no limit) passes. The reply names the list the element came from.

127.0.0.1:6379> BLPOP jobs:high jobs:pending 5
1) "jobs:pending"
2) "job:2"
127.0.0.1:6379> BLPOP jobs:high jobs:pending 1
(nil)

The second call found both lists empty, waited one second, and timed out.

BLMOVE and BLMPOP are the blocking forms of LMOVE and LMPOP. See Blocking operations for how waiting clients are served.

A time to live applies to the whole list: set it with EXPIRE and friends. Single elements cannot expire. If you need per-item expiry, use a sorted set scored by timestamp and remove old members with ZREMRANGEBYSCORE, or a hash with field TTLs.

Pushing to or popping from a list keeps its time to live. Removing the last element deletes the key, and with it the expiry.

Each element is a string up to proto-max-bulk-len (512 MB by default). Pushing and popping at either end take constant time. Commands that work by position or by value (LINDEX, LSET, LINSERT, LREM, LPOS) walk the list, so they get slower as the list grows; for large collections that you look up by value, a set or sorted set is the better fit. LRANGE costs grow with the number of elements returned, so read big lists in pages.

Command What it does
LPUSH / RPUSH Add elements at the head or tail
LPUSHX / RPUSHX Add only if the list exists
LPOP / RPOP Remove and return elements from the head or tail
LMPOP Pop from the first non-empty list
LLEN Number of elements
LRANGE Elements in a range of positions
LINDEX Element at a position
LSET Replace the element at a position
LINSERT Insert before or after an element
LREM Remove occurrences of a value
LPOS Positions of a value
LTRIM Keep only a range of positions
LMOVE Move an element between lists
RPOPLPUSH Older form of LMOVE ... RIGHT LEFT
BLPOP / BRPOP Blocking pop
BLMOVE / BRPOPLPUSH Blocking move
BLMPOP Blocking pop from several lists
import hotkv
client = hotkv.HotKV(host="localhost", port=6379, decode_responses=True)
client.rpush("jobs:pending", "job:1", "job:2")
job = client.lmove("jobs:pending", "jobs:processing", "LEFT", "RIGHT")
print(job) # job:1
print(client.lrange("jobs:processing", 0, -1))
# Wait up to 5 seconds for work on either queue.
print(client.blpop(["jobs:high", "jobs:pending"], 5))
client.close()