Package entropy :: Module cache

Source Code for Module entropy.cache

  1  # -*- coding: utf-8 -*- 
  2  """ 
  3   
  4      @author: Fabio Erculiani <lxnay@sabayonlinux.org> 
  5      @contact: lxnay@sabayonlinux.org 
  6      @copyright: Fabio Erculiani 
  7      @license: GPL-2 
  8   
  9      B{Entropy Framework cache module}. 
 10   
 11      This module contains the Entropy, asynchronous caching logic. 
 12      It is not meant to handle cache pollution management, because 
 13      this is either handled implicitly when cached items are pulled 
 14      in or by using entropy.dump or cache cleaners (see 
 15      entropy.client.interfaces.cache mixin methods) 
 16   
 17  """ 
 18   
 19  from __future__ import with_statement 
 20  from entropy.core import Singleton 
 21  from entropy.misc import TimeScheduled, Lifo 
 22  import time 
 23   
24 -class EntropyCacher(Singleton):
25 """ 26 Entropy asynchronous and synchronous cache writer 27 and reader. This class is a Singleton and contains 28 a thread doing the cache writes asynchronously, thus 29 it must be stopped before your application is terminated 30 calling the stop() method. 31 32 Sample code: 33 34 >>> # import module 35 >>> from entropy.cache import EntropyCacher 36 ... 37 >>> # first EntropyCacher load, start it 38 >>> cacher = EntropyCacher() 39 >>> cacher.start() 40 ... 41 >>> # now store something into its cache 42 >>> cacher.push('my_identifier1', [1, 2, 3]) 43 >>> # now store something synchronously 44 >>> cacher.push('my_identifier2', [1, 2, 3], async = False) 45 ... 46 >>> # now flush all the caches to disk, and make sure all 47 >>> # is written 48 >>> cacher.sync(wait = True) 49 ... 50 >>> # now fetch something from the cache 51 >>> data = cacher.pop('my_identifier1') 52 [1, 2, 3] 53 ... 54 >>> # now discard all the cached (async) writes 55 >>> cacher.discard() 56 ... 57 >>> # and stop EntropyCacher 58 >>> cacher.stop() 59 60 """ 61 62 import entropy.dump as dumpTools 63 import entropy.tools as entropyTools 64 import copy 65
66 - def init_singleton(self):
67 """ 68 Singleton overloaded method. Equals to __init__. 69 This is the place where all the properties initialization 70 takes place. 71 """ 72 import threading 73 self.__alive = False 74 self.__cache_writer = None 75 self.__cache_buffer = Lifo() 76 self.__cache_lock = threading.Lock()
77
78 - def __copy_obj(self, obj):
79 """ 80 Return a copy of an object done by the standard 81 library "copy" module. 82 83 @param obj: object to copy 84 @type obj: any Python object 85 @rtype: copied object 86 @return: copied object 87 """ 88 return self.copy.deepcopy(obj)
89
90 - def __cacher(self):
91 """ 92 This is where the actual asynchronous copy takes 93 place. __cacher runs on a different threads and 94 all the operations done by this are atomic and 95 thread-safe. It just loops over and over until 96 __alive becomes False. 97 """ 98 while 1: 99 if not self.__alive: 100 break 101 with self.__cache_lock: 102 data = self.__cache_buffer.pop() 103 if data == None: 104 break 105 key, data = data 106 d_o = self.dumpTools.dumpobj 107 if not d_o: 108 break 109 d_o(key, data)
110
111 - def __del__(self):
112 self.stop()
113
114 - def start(self):
115 """ 116 This is the method used to start the asynchronous cache 117 writer but also the whole cacher. If this method is not 118 called, the instance will always trash and cache write 119 request. 120 121 @return: None 122 """ 123 with self.__cache_lock: 124 self.__cache_buffer.clear() 125 self.__cache_writer = TimeScheduled(1, self.__cacher) 126 self.__cache_writer.set_delay_before(True) 127 self.__cache_writer.start() 128 while not self.__cache_writer.isAlive(): 129 continue 130 self.__alive = True
131
132 - def is_started(self):
133 """ 134 Return whether start is called or not. This equals to 135 checking if the cacher is running, thus is writing cache 136 to disk. 137 138 @return: None 139 """ 140 return self.__alive
141
142 - def stop(self):
143 """ 144 This method stops the execution of the cacher, which won't 145 accept cache writes anymore. The thread responsible of writing 146 to disk is stopped here and the Cacher will be back to being 147 inactive. A watchdog will avoid the thread to freeze the 148 call if the write buffer is overloaded. 149 150 @return: None 151 """ 152 153 watch_dog = 80 154 while self.__cache_buffer.is_filled() and (watch_dog > 0): 155 watch_dog -= 1 156 time.sleep(0.125) 157 self.__alive = False 158 159 with self.__cache_lock: 160 self.__cache_buffer.clear() 161 if self.__cache_writer != None: 162 self.__cache_writer.kill() 163 self.__cache_writer.join() 164 self.__cache_writer = None
165
166 - def sync(self, wait = False):
167 """ 168 This method can be called anytime and forces the instance 169 to flush all the cache writes queued to disk. If wait == False 170 a watchdog prevents this call to get stuck in case of write 171 buffer overloads. 172 173 @keyword wait: indicates if waiting until done (synchronous mode) or not 174 @type wait: bool 175 @rtype: None 176 @return: None 177 """ 178 if not self.__alive: 179 self.__cache_buffer.clear() 180 return 181 182 watch_dog = 40 183 while self.__cache_buffer.is_filled() and ((watch_dog > 0) or wait) \ 184 and self.__alive: 185 186 if not wait: 187 watch_dog -= 1 188 time.sleep(0.125) 189 190 self.__cache_buffer.clear()
191
192 - def discard(self):
193 """ 194 This method makes buffered cache to be discarded synchronously. 195 196 @return: None 197 """ 198 self.__cache_buffer.clear() 199 with self.__cache_lock: 200 self.__cache_buffer.clear() # make sure twice
201
202 - def push(self, key, data, async = True):
203 """ 204 This is the place where data is either added 205 to the write queue or written to disk (if async == False) 206 only and only if start() method has been called. 207 208 @param key: cache data identifier 209 @type key: string 210 @param data: picklable object 211 @type data: any picklable object 212 @keyword async: store cache asynchronously or not 213 @type async: bool 214 @rtype: None 215 @return: None 216 """ 217 if not self.__alive: 218 return 219 if async: 220 with self.__cache_lock: 221 self.__cache_buffer.push((key, self.__copy_obj(data),)) 222 else: 223 self.dumpTools.dumpobj(key, data)
224
225 - def pop(self, key):
226 """ 227 This is the place where data is retrieved from cache. 228 You must know the cache identifier used when push() 229 was called. 230 231 @param key: cache data identifier 232 @type key: string 233 @rtype: Python object 234 @return: object stored into the stack or None (if stack is empty) 235 """ 236 with self.__cache_lock: 237 l_o = self.dumpTools.loadobj 238 if not l_o: 239 return 240 return l_o(key)
241