diff --git a/contrib/uwsgi-cache-monitor.py b/contrib/uwsgi-cache-monitor.py new file mode 100644 index 00000000..d597c39d --- /dev/null +++ b/contrib/uwsgi-cache-monitor.py @@ -0,0 +1,113 @@ +import mmap +import os +import struct +import array +import time +import sys +from optparse import OptionParser + +class Cache: + + def __init__(self, filename, cache_slots, block_size=65536, sample_sleep=1): + + self.block_size = block_size + self.cache_slots = cache_slots + + self.key_size = 2048 + self.item_size = 2 + 2 + 4 + 8 + 8 + 8 + 8 + 8 + self.key_size + self.block_size_start = self.item_size * self.cache_slots + + fd = os.open(filename, os.O_RDONLY) + self.cache_store = mmap.mmap(fd, 0, mmap.MAP_SHARED, mmap.PROT_READ) + + self.sample_sleep = sample_sleep + self.samples = 0 + self.history = [] + self.cache_full = 0 + self.cache_empty = 0 + self.cache_items = 0 + self.block_sizes = 0 + + def store_read_item_block(self, position): + pos = self.cache_store.tell() + # uwsgi cache stores cache entries first and then the blocks + self.cache_store.seek(self.block_size_start + (position * self.block_size)) + buf = self.cache_store.read(self.block_size) + self.cache_store.seek(pos) + return buf + + def store_read_item(self, position): + buf = self.cache_store.read(self.item_size) + fields = struct.unpack_from('@HHIQQQQQ2048c', buf) + key = array.array('c', fields[8:self.key_size+8]).tostring().rstrip('\x00') + + if [x for x in key if x!= '\x00']: + buf = self.store_read_item_block(position) + value = array.array('c', buf).tostring().rstrip('\x00') + else: + value = '' + return (position, key, value, len(value)) + + def read(self): + data = [self.store_read_item(i) for i in range(self.cache_slots)] + self.cache_store.seek(0) + self.update_stats(data) + if self.sample_sleep: + time.sleep(self.sample_sleep) + return data + + def update_stats(self, data): + # data is a list of (position, key, value, len(value)) tuples + items = len([1 for x in data if x[3] > 0]) + self.cache_items += items + full, empty = items == self.cache_slots, items == 0 + if full: + self.cache_full += 1 + if empty: + self.cache_empty += 1 + self.samples += 1 + block_sizes = sum([x[3] for x in data]) + self.block_sizes += block_sizes + self.history.append({'full': full, 'empty': empty, 'data': data, 'items': + items, 'block_sizes': block_sizes}) + + def dump(self): + return { + 'samples': self.samples, + 'history': self.history, + 'cache_slots': self.cache_slots, + 'sample_sleep': self.sample_sleep, + 'cache_empty': self.cache_empty, + 'cache_full': self.cache_full, + 'cache_items': self.cache_items, + 'block_sizes': self.block_sizes, + } + + def show_dump(self): + d = self.dump() + print + print "Recorded %d samples (%d second(s) sleep between samples)" % + (d['samples'], d['sample_sleep']) + print "Cache empty %d times, full %d times, %.2f items on average" % + (d['cache_empty'], d['cache_full'], d['cache_items'] / d['samples']) + print "Block size average size: %d bytes" % (d['block_sizes'] / + d['cache_items'] * 8) + print "Data in cache average: %d bytes" % (d['block_sizes'] / d['samples'] + * 8) + +def main(options): + cache = Cache(options.cache_store, options.cache_slots, options.block_size, + options.sleep_time) + print "Recording..." + while True: + try: + data = cache.read() + except KeyboardInterrupt: + cache.show_dump() + sys.exit(0) + +if __name__ == '__main__': + parser = OptionParser() + parser.add_option("-s", "--cache-slots", dest="cache_slots", type="int", + help="Slots available in the cache, uwsgi cache option") + parser.add_option("-c", "--cache-store", dest="cache_store", diff --git a/welcome.py b/welcome.py index 09f4b47f..6a16f0fb 100644 --- a/welcome.py +++ b/welcome.py @@ -30,12 +30,12 @@ def serve_logo(e, sr): def serve_options(e, sr): sr('200 OK', [('Content-Type', 'text/html')]) for opt in xrange(0,256): - yield "%d = %d
" % (opt, uwsgi.get_option(opt)) + yield "%d = %d
" % (opt, uwsgi.get_option(opt)) def serve_config(e, sr): sr('200 OK', [('Content-Type', 'text/html')]) for opt in uwsgi.opt.keys(): - yield "%s = %s
" % (opt, uwsgi.opt[opt]) + yield "%s = %s
" % (opt, uwsgi.opt[opt]) routes = {} routes['/xsendfile'] = xsendfile