+ When the cache limit is exceeded, the least recently used objects are
+ cleared. Clearing the object means discarding its contents to release
+ memory. The next time the object is accessed, it must be re-fetched from
+ the server. Note that the inode cache limit is a soft limit; the cache
+ limit may be exceeded if necessary to load very large objects, it may also
+ be exceeded if open file handles prevent objects from being cleared.
+
+ """
+
+ def __init__(self, cap, min_entries=4):
+ self._entries = collections.OrderedDict()
+ self._by_uuid = {}
+ self._counter = itertools.count(0)
+ self.cap = cap
+ self._total = 0
+ self.min_entries = min_entries
+
+ def total(self):
+ return self._total
+
+ def _remove(self, obj, clear):
+ if clear and not obj.clear():
+ _logger.debug("InodeCache could not clear %i in_use %s", obj.inode, obj.in_use())
+ return False
+ self._total -= obj.cache_size
+ del self._entries[obj.cache_priority]
+ if obj.cache_uuid:
+ self._by_uuid[obj.cache_uuid].remove(obj)
+ if not self._by_uuid[obj.cache_uuid]:
+ del self._by_uuid[obj.cache_uuid]
+ obj.cache_uuid = None
+ if clear:
+ _logger.debug("InodeCache cleared %i total now %i", obj.inode, self._total)
+ return True
+
+ def cap_cache(self):
+ if self._total > self.cap:
+ for key in list(self._entries.keys()):
+ if self._total < self.cap or len(self._entries) < self.min_entries:
+ break
+ self._remove(self._entries[key], True)
+
+ def manage(self, obj):
+ if obj.persisted():
+ obj.cache_priority = next(self._counter)
+ obj.cache_size = obj.objsize()
+ self._entries[obj.cache_priority] = obj
+ obj.cache_uuid = obj.uuid()
+ if obj.cache_uuid:
+ if obj.cache_uuid not in self._by_uuid:
+ self._by_uuid[obj.cache_uuid] = [obj]
+ else:
+ if obj not in self._by_uuid[obj.cache_uuid]:
+ self._by_uuid[obj.cache_uuid].append(obj)
+ self._total += obj.objsize()
+ _logger.debug("InodeCache touched %i (size %i) (uuid %s) total now %i", obj.inode, obj.objsize(), obj.cache_uuid, self._total)
+ self.cap_cache()
+ else:
+ obj.cache_priority = None
+
+ def touch(self, obj):
+ if obj.persisted():
+ if obj.cache_priority in self._entries:
+ self._remove(obj, False)
+ self.manage(obj)
+
+ def unmanage(self, obj):
+ if obj.persisted() and obj.cache_priority in self._entries:
+ self._remove(obj, True)
+
+ def find(self, uuid):
+ return self._by_uuid.get(uuid)
+
+ def clear(self):
+ self._entries.clear()
+ self._by_uuid.clear()
+ self._total = 0