На одном из проект понадобилось провернуть схему, где нужно было найти ключи в редис кластере с бесконечным TTL (-1) и выставить им конечный, чтобы они автоматически "протухли".
Небольшой скрипт на python для решения этой задачи:
import asyncio
from redis.asyncio import RedisCluster
from redis.asyncio.cluster import ClusterNode
REDIS_HOST = "master-ip"
REDIS_PORT = 6379
REDIS_PASSWORD = "<pass>"
TTL_TO_SET = 10000
SCAN_COUNT = 300
BATCH_SIZE = 100
async def process_keys(node, client, cursor=0):
while True:
cursor, keys = await node.execute_command("SCAN", cursor, "COUNT", SCAN_COUNT)
if keys:
ttl_tasks = [client.ttl(key) for key in keys]
ttls = await asyncio.gather(*ttl_tasks)
tasks = [
client.pexpire(key, TTL_TO_SET)
for key, ttl in zip(keys, ttls)
if ttl == -1
]
if tasks:
for i in range(0, len(tasks), BATCH_SIZE):
await asyncio.gather(*tasks[i:i + BATCH_SIZE])
if cursor == 0:
break
async def no_expiry_process(client: RedisCluster):
master_nodes = client.get_primaries()
print(f"[+] Found {len(master_nodes)} master nodes.")
tasks = [process_keys(node, client) for node in master_nodes]
await asyncio.gather(*tasks)
await client.aclose()
async def main():
client = RedisCluster(
startup_nodes=[
ClusterNode(REDIS_HOST, REDIS_PORT),
],
password=REDIS_PASSWORD,
decode_responses=True,
)
await client.initialize()
await no_expiry_process(client=client)
if __name__ == "__main__":
asyncio.run(main())
Редис кластер из 6 нод, но нам понадобятся только мастера. Первый из них укажем в качестве REDIS_HOST, остальные получим в коде. Далее мы пробегаемя по всем мастер нодам и выставляем бесконечным ключам TTL равный 10 секундам.
Для запуска можно использовать venv:
python -m venv .redis_clean
source .redis_clean/bin/activate
pip install redis
python script.py
Время выполнения на 3 мастер узлах, на каждом из которых около 25к ключей с TTL -1 заняло примерно 44 секунды.
#scripting