7
from optparse import OptionParser
11
def __init__(self, filename, cache_slots, block_size=65536, sample_sleep=1):
13
self.block_size = block_size
14
self.cache_slots = cache_slots
17
self.item_size = 2 + 2 + 4 + 8 + 8 + 8 + 8 + 8 + self.key_size
18
self.block_size_start = self.item_size * self.cache_slots
20
fd = os.open(filename, os.O_RDONLY)
21
self.cache_store = mmap.mmap(fd, 0, mmap.MAP_SHARED, mmap.PROT_READ)
23
self.sample_sleep = sample_sleep
31
def store_read_item_block(self, position):
32
pos = self.cache_store.tell()
33
# uwsgi cache stores cache entries first and then the blocks
34
self.cache_store.seek(self.block_size_start + (position * self.block_size))
35
buf = self.cache_store.read(self.block_size)
36
self.cache_store.seek(pos)
39
def store_read_item(self, position):
40
buf = self.cache_store.read(self.item_size)
41
fields = struct.unpack_from('@HHIQQQQQ2048c', buf)
42
key = array.array('c', fields[8:self.key_size+8]).tostring().rstrip('\x00')
44
if [x for x in key if x!= '\x00']:
45
buf = self.store_read_item_block(position)
46
value = array.array('c', buf).tostring().rstrip('\x00')
49
return (position, key, value, len(value))
52
data = [self.store_read_item(i) for i in range(self.cache_slots)]
53
self.cache_store.seek(0)
54
self.update_stats(data)
56
time.sleep(self.sample_sleep)
59
def update_stats(self, data):
60
# data is a list of (position, key, value, len(value)) tuples
61
items = len([1 for x in data if x[3] > 0])
62
self.cache_items += items
63
full, empty = items == self.cache_slots, items == 0
69
block_sizes = sum([x[3] for x in data])
70
self.block_sizes += block_sizes
71
self.history.append({'full': full, 'empty': empty, 'data': data, \
72
'items': items, 'block_sizes': block_sizes})
76
'samples': self.samples,
77
'history': self.history,
78
'cache_slots': self.cache_slots,
79
'sample_sleep': self.sample_sleep,
80
'cache_empty': self.cache_empty,
81
'cache_full': self.cache_full,
82
'cache_items': self.cache_items,
83
'block_sizes': self.block_sizes,
89
print "Recorded %d samples (%d second(s) sleep between samples)" % \
90
(d['samples'], d['sample_sleep'])
91
print "Cache empty %d times, full %d times, %.2f items on average" % \
92
(d['cache_empty'], d['cache_full'], d['cache_items'] / d['samples'])
93
print "Block size average size: %d bytes" % \
94
(d['block_sizes'] / d['cache_items'] * 8)
95
print "Data in cache average: %d bytes" % \
96
(d['block_sizes'] / d['samples'] * 8)
99
cache = Cache(options.cache_store, options.cache_slots, options.block_size,
105
except KeyboardInterrupt:
109
if __name__ == '__main__':
110
parser = OptionParser()
111
parser.add_option("-s", "--cache-slots", dest="cache_slots", type="int",
112
help="Slots available in the cache, uwsgi cache option")
113
parser.add_option("-c", "--cache-store", dest="cache_store", default="uwsgi.cache",
114
help="The filename of the cache store, uwsgi cache-store option. Default: uwsgi.cache")
115
parser.add_option("-b", "--block-size", dest="block_size", default=65536, type="int",
116
help="The size of the cache block, uwsgi cache-blocksize option. Default: 65536")
117
parser.add_option("-t", "--sleep-time", dest="sleep_time", default=1, type="int",
118
help="The time to sleep between each sample. Default: 1")
120
(options, args) = parser.parse_args()
121
if not options.cache_slots:
122
parser.error('Option -s / --cache-slots is mandatory')