clear redis harder, use os.exec to clean up memory

This commit is contained in:
Jeff Balogh 2011-05-31 11:25:38 -07:00
Родитель 3a628a2c22
Коммит ffac6fa7b0
1 изменённых файлов: 48 добавлений и 19 удалений

Просмотреть файл

@ -1,6 +1,8 @@
import itertools
import logging
import os
import socket
import subprocess
import sys
import tempfile
import time
@ -14,8 +16,8 @@ log = logging.getLogger('z.redis')
# We process the keys in chunks of size CHUNK.
CHUNK = 3000
# Remove any sets with less than MIN or more than MAX elements.
MIN = 2
MAX = 75
MIN = 10
MAX = 50
# Expire keys after EXPIRE seconds.
EXPIRE = 60 * 5
@ -24,41 +26,61 @@ RedisError = redislib.RedisError, socket.error
def vacuum(master, slave):
total = [1, 0]
def keys():
ks = slave.keys()
total[0] = len(ks)
log.info('There are %s keys to clean up.' % total[0])
log.info('There are %s keys to clean up.' % len(ks))
ks = iter(ks)
while 1:
yield [ks.next() for _ in xrange(CHUNK)]
buffer = []
for _ in xrange(CHUNK):
try:
buffer.append(ks.next())
except StopIteration:
yield buffer
return
yield buffer
tmp = tempfile.TemporaryFile()
tmp = tempfile.NamedTemporaryFile(delete=False)
for ks in keys():
tmp.write('\n'.join(ks))
tmp.seek(0)
tmp.close()
# It's hard to get Python to clean up the memory from slave.keys(), so
# we'll let the OS do it. argv[0] is a dummy argument, the rest get passed
# like a normal command line.
os.execl(sys.executable, 'argv[0]', sys.argv[0], sys.argv[1], tmp.name)
def cleanup(master, slave, filename):
tmp = open(filename)
total = [1, 0]
p = subprocess.Popen(['wc', '-l', filename], stdout=subprocess.PIPE)
total[0] = int(p.communicate()[0].strip().split()[0])
def file_keys():
while 1:
# Get about 300 keys.
x = tmp.readlines(1024 * 30)
if x:
yield [k.strip() for k in x]
else:
raise StopIteration
buffer = []
for _ in xrange(CHUNK):
line = tmp.readline()
if line:
buffer.append(line.strip())
else:
yield buffer
return
yield buffer
count = itertools.count()
num = 0
for ks in file_keys():
pipe = slave.pipeline()
for k in ks:
pipe.scard(k)
try:
drop = [k for k, size in zip(ks, pipe.execute())
if size < MIN or size > MAX]
if 0 < size < MIN or size > MAX]
except RedisError:
continue
num = count.next() * CHUNK
num += len(ks)
percent = round(float(num) / total[0] * 100, 1)
total[1] += len(drop)
log.debug('[%s %.1f%%] Dropping %s keys.' % (num, percent, len(drop)))
@ -86,4 +108,11 @@ class Command(BaseCommand):
except Exception:
log.error('Could not connect to redis.', exc_info=True)
return
vacuum(master, slave)
if args:
filename = args[0]
try:
cleanup(master, slave, filename)
finally:
os.unlink(filename)
else:
vacuum(master, slave)