Транзакция в отсортированном наборе Redis не выглядит атомарной - PullRequest
0 голосов
/ 07 марта 2020

Я сталкиваюсь с некоторым странным поведением в конвейере / транзакции в Redis, что заставляет меня усомниться в том, что код фактически выполняется в транзакции:

class RedisThread:

    KEY = "threading_test"

    def __init__(self, id:int):
        self._redis = Redis(host="localhost", port=6379, db=0)
        self._id    = id
        self._redis.delete(self.KEY)

    def __call__(self, *args, **kwargs):
        results = []
        for i in range(3):
            # Transaction
            pipe = self._redis.pipeline(transaction=True)

            # ZADD current time as score
            pipe.zadd(self.KEY, {f"{self._id}-{i}": time.time()})

            # ZRANK
            pipe.zrank(self.KEY, f"{self._id}-{i}")

            # Commit and get result of ZRANK
            results.append(str(pipe.execute()[1]))

        print(", ".join(results))


    threads = [
        threading.Thread(target=RedisThread(1)),
        threading.Thread(target=RedisThread(2)),
        threading.Thread(target=RedisThread(3)),
        threading.Thread(target=RedisThread(4)),
        threading.Thread(target=RedisThread(5)),
    ]

    for t in threads:
        t.start()

    for t in threads:
        t.join()

Когда я запускаю этот код, это результат:

1, 5, 9
3, 6, 10
0, 3, 13
4, 11, 12
1, 4, 13

Обратите внимание, что между потоками есть повторяющиеся значения. Так как я делаю ZRANK, и значения, которые я добавляю (через ZADD) к набору, основаны на времени (и, следовательно, всегда увеличиваются в значениях), я не должен видеть никаких дубликатов, хотя есть дубликаты ...

1 Ответ

0 голосов
/ 07 марта 2020

транзакция Redis - это атом c. Проблема в том, что вы используете time() в качестве оценки.

Поскольку программы работают быстро, поэтому time() может возвращать один и тот же результат для нескольких вызовов. Другими словами, вы устанавливаете членов в отсортированном наборе с одинаковым счетом.

Если участники имеют одинаковый счет, они сортируются по лексикографическому порядку.

Теперь давайте посмотрим, почему вы получили дубликат значения:

thread1: ZADD key same-time 1-0
thread1: ZRANK key 1-0 ----------> 0

thread2: ZADD key same-time 2-0
thread2: ZRANK key 2-0 ----------> 1

thread1: ZADD key same-time 1-1
thread1: ZRANK key 1-1 ----------> 1

Вторая транзакция потока1 выполняется после первой транзакции потока2, а 1-1 меньше, чем 2-0 в лексикографическом порядке. Поэтому, когда будет выполнена вторая транзакция потока1, 1-1 будет вставлен перед 2-0, и поэтому они оба получают ранг: 1.

...