soulomoon/python-throttle 在假如多线程和多进程测试前一切都是好好的, 感觉好开心(^·ω·^ )
但是加入多线程,多进程测试后发现出现了 race condition
但是写的时候我已经好好地去尝试规避这个问题了,找不到原因
race condition 的问题出现在 test_limiter 中(;Д`)
def add_key(self, key, expired): """use lua script to avoid race condition""" multiply = self.redis.register_script(self.lua_incr) return multiply([key, expired])
出现问题的地方在这儿0 - 0
不好意思 这个才是fixed windows的测试, 实际上,两种implementation都fail了
![]() | 1 holyghost 2018-03-13 21:29:09 +08:00 via iPhone ![]() 手机上看的,看测试大概了解了思路,不成熟的想法 如果你真要这么做,用 incr 保证线程安全 另外,你没有考虑到滑动窗口的问题 最后 token bucket 了解下? |
![]() | 2 OP @holyghost 不好意思 我图放错了 0 0。 这里我有两种 implementaion, 一个用 ordered set, 一个用 incr 写在 luascript,redis 官方是这么推荐的 0。 都用 expire。 token bucket 要单开 put token 的进程吧? |
![]() | 3 soulomoon OP ```python lua_incr = """ local current current = redis.call("incr",KEYS[1]) if tonumber(current) == 1 then redis.call("expire",KEYS[1],KEYS[2]) end return current-1 """ ``` 这个是我改过的 lua script,原版的在[这里]( https://redis.io/commands/incr) |
![]() | 4 holyghost 2018-03-13 22:18:54 +08:00 @soulomoon 我的理解,incr 不需要放在 lua script 里面来保证单线程吧? incr 本身是有返回值的, 比较下返回值和 threshold 应该就可以了 另外,粗略算的话,token bucket 不需要另外开进程,可以在消耗时顺便添加 token (当然了,这种实现需要带时间戳) |
![]() | 5 soulomoon OP incr 放进去是为了和 expire 一起,保证 key 不会因为没有 set expire 而 leak @holyghost,现在我怀疑 redis-py run script 的特点,因为是通过 register 到远端,然后再通过 sha1 作为 key 执行,可能是多个 instance 同时执行了同一 script,获取到了相同的返回值。。。 还有一个 sliding log 的 implementaion,pipeline 看 redis-py 的简介 0 0 理论上是一个 multi exce 的行为,0 0, 也是 fail。这样的: def add_key(self, key, expired): """use ordered set for counting keys get_set manner """ now = time.time() with self.redis.pipeline() as session: session.zremrangebyscore(key, 0, now - expired) session.zrange(key, 0, -1) session.zadd(key, now, uuid.uuid4().hex) session.expire(key, expired) result = session.execute() return len(result[1]) |
![]() | 6 soulomoon OP *多个 instance 接受了同一个 script 的执行结果。。 |
![]() | 7 soulomoon OP 发现即使是简单如 def add_key(self, key, expired): return self.redis.incr(key) 也会有同样的问题 who's to blame.... |
![]() | 8 soulomoon OP 发现问题了, 是我写 unittest 的问题,interleaving 多个不同时限的 limiter 到相同的 key 中,当然会出现不同的结果 ozn 好开心解决了,原来不是我 implementaion 的问题是我的测试的问题,看来要好好学习测试, 最后谢谢 @holyghost 的回答。 |