cache.py 3.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. # The contents of this file are subject to the BitTorrent Open Source License
  2. # Version 1.1 (the License). You may not copy or use this file, in either
  3. # source code or executable form, except in compliance with the License. You
  4. # may obtain a copy of the License at http://www.bittorrent.com/license/.
  5. #
  6. # Software distributed under the License is distributed on an AS IS basis,
  7. # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  8. # for the specific language governing rights and limitations under the
  9. # License.
  10. from BTL.platform import bttime as time
  11. from collections import deque
  12. # The main drawback of this cache is that it only supports one time out
  13. # delay. If you experience heavy load of cache inserts at time t then
  14. # at t + expiration time, you will experience a heavy load due to
  15. # expirations. An alternative is to randomize the timeouts. With
  16. # exponentially distributed timeouts we would expect load roughly obeying
  17. # a Poisson process. However, one can do better if the randomness if a
  18. # function of load such that excess arrivals are redistributed evenly over the
  19. # interval.
  20. class Cache:
  21. # fixed TTL cache. This assumes all entries have the same
  22. # TTL.
  23. def __init__(self, touch_on_access = False):
  24. self.data = {}
  25. self.q = deque()
  26. self.touch = touch_on_access
  27. def __getitem__(self, key):
  28. if self.touch:
  29. v = self.data[key][1]
  30. self[key] = v
  31. return self.data[key][1]
  32. def __setitem__(self, key, value):
  33. t = time()
  34. self.data[key] = (t, value)
  35. self.q.appendleft((t, key, value))
  36. def __delitem__(self, key):
  37. del(self.data[key])
  38. def has_key(self, key):
  39. return self.data.has_key(key)
  40. def keys(self):
  41. return self.data.keys()
  42. def expire(self, expire_time):
  43. try:
  44. while self.q[-1][0] < expire_time:
  45. x = self.q.pop()
  46. assert(x[0] < expire_time)
  47. try:
  48. t, v = self.data[x[1]]
  49. if v == x[2] and t == x[0]:
  50. del(self.data[x[1]]) # only eliminates one reference to the
  51. # object. If there is more than one
  52. # reference (for example if an
  53. # elements was "touched" by getitem)
  54. # then the item persists in the cache
  55. # until the last reference expires.
  56. # Note: frequently touching a cache entry
  57. # with long timeout intervals could be
  58. # viewed as a memory leak since the
  59. # cache can grow quite large.
  60. # This class is best used without
  61. # touch_on_access or with short expirations.
  62. except KeyError:
  63. pass
  64. except IndexError:
  65. pass