Django

Code

Changeset 686

Show
Ignore:
Timestamp:
09/25/05 15:01:35 (3 years ago)
Author:
jacob
Message:

Added "locmem" and "file" cache backends. "locmem" is a thread-safe local-memory cache, and "file" is a file-based cache.
This refs #515; much thanks to Eugene Lazutkin!

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/trunk/django/core/cache.py

    r598 r686  
    1616                                    on localhost port 11211. 
    1717 
    18     pgsql://tablename/              A pgsql backend (the pgsql backend uses 
    19                                     the same database/username as the rest of 
    20                                     the CMS, so only a table name is needed.) 
    21  
    22     file:///var/tmp/django.cache/      A file-based cache at /var/tmp/django.cache 
     18    sql://tablename/                A SQL backend.  If you use this backend, 
     19                                    you must have django.contrib.cache in 
     20                                    INSTALLED_APPS, and you must have installed 
     21                                    the tables for django.contrib.cache. 
     22 
     23    file:///var/tmp/django_cache/   A file-based cache stored in the directory 
     24                                    /var/tmp/django_cache/. 
    2325 
    2426    simple:///                      A simple single-process memory cache; you 
     
    2628                                    testing. Note that this cache backend is 
    2729                                    NOT threadsafe! 
     30                                         
     31    locmem:///                      A more sophisticaed local memory cache; 
     32                                    this is multi-process- and thread-safe. 
    2833 
    2934All caches may take arguments; these are given in query-string style.  Valid 
     
    5156 
    5257    memcached://127.0.0.1:11211/?timeout=60 
    53     pgsql://tablename/?timeout=120&max_entries=500&cull_percentage=4 
     58    sql://tablename/?timeout=120&max_entries=500&cull_percentage=4 
    5459 
    5560Invalid arguments are silently ignored, as are invalid values of known 
    5661arguments. 
    57  
    58 So far, only the memcached and simple backend have been implemented; backends 
    59 using postgres, and file-system storage are planned. 
    6062""" 
    6163 
     
    182184    def get(self, key, default=None): 
    183185        now = time.time() 
    184         exp = self._expire_info.get(key, now) 
    185         if exp is not None and exp < now: 
     186        exp = self._expire_info.get(key) 
     187        if exp is None: 
     188            return default 
     189        elif exp < now: 
    186190            del self._cache[key] 
    187191            del self._expire_info[key] 
    188192            return default 
    189193        else: 
    190             return self._cache.get(key, default) 
     194            return self._cache[key] 
    191195 
    192196    def set(self, key, value, timeout=None): 
     
    220224                self.delete(k) 
    221225 
     226############################### 
     227# Thread-safe in-memory cache # 
     228############################### 
     229 
     230try: 
     231    import cPickle as pickle 
     232except ImportError: 
     233    import pickle 
     234from django.utils.synch import RWLock 
     235 
     236class _LocMemCache(_SimpleCache): 
     237    """Thread-safe in-memory cache""" 
     238     
     239    def __init__(self, host, params): 
     240        _SimpleCache.__init__(self, host, params) 
     241        self._lock = RWLock() 
     242 
     243    def get(self, key, default=None): 
     244        should_delete = False 
     245        self._lock.reader_enters() 
     246        try: 
     247            now = time.time() 
     248            exp = self._expire_info.get(key) 
     249            if exp is None: 
     250                return default 
     251            elif exp < now: 
     252                should_delete = True 
     253            else: 
     254                return self._cache[key] 
     255        finally: 
     256            self._lock.reader_leaves() 
     257        if should_delete: 
     258            self._lock.writer_enters() 
     259            try: 
     260                del self._cache[key] 
     261                del self._expire_info[key] 
     262                return default 
     263            finally: 
     264                self._lock.writer_leaves() 
     265                 
     266    def set(self, key, value, timeout=None): 
     267        self._lock.writer_enters() 
     268        try: 
     269            _SimpleCache.set(self, key, value, timeout) 
     270        finally: 
     271            self._lock.writer_leaves() 
     272             
     273    def delete(self, key): 
     274        self._lock.writer_enters() 
     275        try: 
     276            _SimpleCache.delete(self, key) 
     277        finally: 
     278            self._lock.writer_leaves() 
     279 
     280#################### 
     281# File-based cache # 
     282#################### 
     283 
     284import os 
     285import urllib 
     286 
     287class _FileCache(_SimpleCache): 
     288    """File-based cache""" 
     289     
     290    def __init__(self, dir, params): 
     291        self._dir = dir 
     292        if not os.path.exists(self._dir): 
     293            try: 
     294                os.makedirs(self._dir) 
     295            except OSError: 
     296                raise EnvironmentError, "Cache directory '%s' does not exist and could not be created'" % self._dir 
     297        _SimpleCache.__init__(self, dir, params) 
     298        del self._cache 
     299        del self._expire_info 
     300         
     301    def get(self, key, default=None): 
     302        fname = self._key_to_file(key) 
     303        try: 
     304            f = open(fname, 'rb') 
     305            exp = pickle.load(f) 
     306            now = time.time() 
     307            if exp < now: 
     308                f.close() 
     309                os.remove(fname) 
     310            else: 
     311                return pickle.load(f) 
     312        except (IOError, pickle.PickleError): 
     313            pass 
     314        return default 
     315         
     316    def set(self, key, value, timeout=None): 
     317        fname = self._key_to_file(key) 
     318        if timeout is None: 
     319            timeout = self.default_timeout 
     320        filelist = os.listdir(self._dir) 
     321        if len(filelist) > self._max_entries: 
     322            self._cull(filelist) 
     323        try: 
     324            f = open(fname, 'wb') 
     325            now = time.time() 
     326            pickle.dump(now + timeout, f, 2) 
     327            pickle.dump(value, f, 2) 
     328        except (IOError, OSError): 
     329            raise 
     330             
     331    def delete(self, key): 
     332        try: 
     333            os.remove(self._key_to_file(key)) 
     334        except (IOError, OSError): 
     335            pass 
     336             
     337    def has_key(self, key): 
     338        return os.path.exists(self._key_to_file(key)) 
     339         
     340    def _cull(self, filelist): 
     341        if self.cull_frequency == 0: 
     342            doomed = filelist 
     343        else: 
     344            doomed = [k for (i, k) in enumerate(filelist) if i % self._cull_frequency == 0] 
     345        for fname in doomed: 
     346            try: 
     347                os.remove(os.path.join(self._dir, fname)) 
     348            except (IOError, OSError): 
     349                pass 
     350       
     351    def _key_to_file(self, key): 
     352        return os.path.join(self._dir, urllib.quote_plus(key)) 
     353     
    222354########################################## 
    223355# Read settings and load a cache backend # 
     
    229361    'memcached' : _MemcachedCache, 
    230362    'simple'    : _SimpleCache, 
     363    'locmem'    : _LocMemCache, 
     364    'file'      : _FileCache, 
    231365} 
    232366