Forked from
DM / dm-docs
261 commits behind, 690 commits ahead of the upstream repository.
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
objectCache.py 4.86 KiB
#!/usr/bin/env python
import threading
import time
from collections import deque
class ObjectCache:
"""
Cache objects identified by id. Objects are removed from cache
based on the last accessed algorithm.
"""
# How much larger than object cache should time stamp deq be
# allowed to grow.
DEFAULT_TIME_STAMP_DEQ_SIZE_FACTOR = 2
# Cache info expiration time.
DEFAULT_OBJECT_LIFETIME = 60 # seconds
def __init__(self, cacheSize, objectLifetime=DEFAULT_OBJECT_LIFETIME, objectClass=None):
self.lock = threading.RLock()
self.objectMap = {} # id/object map
self.timestampDeq = deque() # timestamp deq
self.cacheSize = cacheSize
self.objectLifetime = objectLifetime
self.deqSize = ObjectCache.DEFAULT_TIME_STAMP_DEQ_SIZE_FACTOR*cacheSize
self.objectClass = objectClass
def setCacheSize(self, cacheSize):
self.cacheSize = cacheSize
def setObjectLifetime(self, objectLifetime):
self.objectLifetime = objectLifetime
def __purgeOne(self):
# Get rid of one cached item based on the last accessed algorithm.
while True:
deqEntry = self.timestampDeq.popleft()
oldId = deqEntry[0]
cachedEntry = self.objectMap.get(oldId)
if cachedEntry is not None:
# Timestamp entry is valid.
if cachedEntry == deqEntry:
# Found an old item, get rid of it from the cache.
del self.objectMap[oldId]
break
# Done.
return
def __purgeTimestampDeq(self):
# Get rid of stale entries.
timestampDeq = deque()
while len(self.timestampDeq):
deqEntry = self.timestampDeq.popleft()
id = deqEntry[0]
cachedEntry = self.objectMap.get(id)
if cachedEntry is not None:
# Timestamp entry is valid.
if cachedEntry == deqEntry:
# Found current item, keep it.
timestampDeq.append(deqEntry)
# Done.
self.timestampDeq = timestampDeq
return
def put(self, id, item, objectLifetime=None):
updateTime = time.time()
expirationTime = updateTime + self.objectLifetime
if objectLifetime is not None:
expirationTime = updateTime + objectLifetime
entry = (id, item, updateTime, expirationTime)
self.lock.acquire()
try:
self.objectMap[id] = entry
self.timestampDeq.append(entry)
if len(self.objectMap) > self.cacheSize:
self.__purgeOne()
if len(self.timestampDeq) > self.deqSize:
self.__purgeTimestampDeq()
finally:
self.lock.release()
def get(self, id):
item = None
itemTuple = self.objectMap.get(id)
if itemTuple is not None:
id, item, updateTime, expirationTime = itemTuple
return item
def getAll(self):
# Item tuple: id, item, updateTime, expirationTime = itemTuple
return map(lambda itemTuple:itemTuple[1], self.objectMap.values())
def getItemTuple(self, id):
itemTuple = self.objectMap.get(id)
if itemTuple is None:
itemTuple = (id, None, None, None)
return itemTuple
def remove(self, id):
self.lock.acquire()
try:
item = self.objectMap.get(id)
if item is not None:
del self.objectMap[id]
return item
finally:
self.lock.release()
def isEmpty(self):
return len(self.objectMap) == 0
def size(self):
return len(self.objectMap)
def __str__(self):
return '%s' % self.timestampDeq
#######################################################################
# Testing.
if __name__ == '__main__':
c = ObjectCache(3)
class Item:
def __init__(self, id):
self.id = id
def getId(self):
return self.id
def __str__(self):
return '%s' % self.id
class Item2:
def __init__(self, name):
self.name = name
def getName(self):
return self.name
def __str__(self):
return '%s' % self.name
for i in range(0,5):
item = Item(i)
c.put(i, item)
print 'Added item: ', item
print 'Cache: ', c
time.sleep(1)
for j in range(0,3):
item = Item(2)
c.put(2, item)
print 'Updated item: ', item
print 'Cache: ', c
time.sleep(1)
item = c.remove(2)
print 'Deleted item 2: ', item
print 'Cache: ', c
time.sleep(1)
item = c.get(2)
print 'Got item 2: ', item
print 'Cache: ', c
print
time.sleep(1)
print
c = ObjectCache(3)
c.put('sv', Item2('sv'))
print c
i = c.get('sv')
print i
print 'Done'