| 1 | # coding=utf-8
|
|---|
| 2 | import os
|
|---|
| 3 | import tempfile
|
|---|
| 4 | import time
|
|---|
| 5 | from multiprocessing import Pool
|
|---|
| 6 | from django.conf import settings
|
|---|
| 7 | try:
|
|---|
| 8 | from django.utils.six.moves import cPickle as pickle
|
|---|
| 9 | except ImportError:
|
|---|
| 10 | import pickle
|
|---|
| 11 |
|
|---|
| 12 | settings.configure()
|
|---|
| 13 |
|
|---|
| 14 | from django.core.cache.backends.filebased import FileBasedCache
|
|---|
| 15 |
|
|---|
| 16 |
|
|---|
| 17 | class SafeFileBasedCache(FileBasedCache):
|
|---|
| 18 | """
|
|---|
| 19 | FileBasedCache backend that uses temp file write-troughs to prevent
|
|---|
| 20 | concurrency issues.
|
|---|
| 21 | """
|
|---|
| 22 | _tmp_suffix = '.__django_cache'
|
|---|
| 23 |
|
|---|
| 24 | def set(self, key, value, timeout=None, version=None):
|
|---|
| 25 | key = self.make_key(key, version=version)
|
|---|
| 26 | self.validate_key(key)
|
|---|
| 27 |
|
|---|
| 28 | fname = self._key_to_file(key)
|
|---|
| 29 | dirname = os.path.dirname(fname)
|
|---|
| 30 |
|
|---|
| 31 | if timeout is None:
|
|---|
| 32 | timeout = self.default_timeout
|
|---|
| 33 |
|
|---|
| 34 | self._cull()
|
|---|
| 35 |
|
|---|
| 36 | try:
|
|---|
| 37 | if not os.path.exists(dirname):
|
|---|
| 38 | os.makedirs(dirname)
|
|---|
| 39 |
|
|---|
| 40 | fd, tmp = tempfile.mkstemp(suffix=self._tmp_suffix, dir=dirname)
|
|---|
| 41 | with os.fdopen(fd, 'wb') as f:
|
|---|
| 42 | now = time.time()
|
|---|
| 43 | pickle.dump(now + timeout, f, pickle.HIGHEST_PROTOCOL)
|
|---|
| 44 | pickle.dump(value, f, pickle.HIGHEST_PROTOCOL)
|
|---|
| 45 | os.rename(tmp, fname)
|
|---|
| 46 | except (IOError, OSError):
|
|---|
| 47 | pass
|
|---|
| 48 |
|
|---|
| 49 |
|
|---|
| 50 | cache = FileBasedCache('./tmp', {})
|
|---|
| 51 | safecache = SafeFileBasedCache('./tmp', {})
|
|---|
| 52 | text = u'Iñtërnâtiônàlizætiøn' * 1024
|
|---|
| 53 |
|
|---|
| 54 |
|
|---|
| 55 | def test(cache):
|
|---|
| 56 | value = {'list1': range(1024), 'text': text, 'list': range(1024)}
|
|---|
| 57 | try:
|
|---|
| 58 | cache.set('test', value)
|
|---|
| 59 | cached = cache.get('test')
|
|---|
| 60 | if cached is None:
|
|---|
| 61 | return 'miss'
|
|---|
| 62 | elif cached != value:
|
|---|
| 63 | return 'fail'
|
|---|
| 64 | except Exception, e:
|
|---|
| 65 | return str(e)
|
|---|
| 66 |
|
|---|
| 67 |
|
|---|
| 68 | def test_cache(i):
|
|---|
| 69 | return test(cache)
|
|---|
| 70 |
|
|---|
| 71 |
|
|---|
| 72 | def test_safe_cache(i):
|
|---|
| 73 | return test(safecache)
|
|---|
| 74 |
|
|---|
| 75 |
|
|---|
| 76 | def test_multiprocess(func):
|
|---|
| 77 | pool = Pool(processes=6)
|
|---|
| 78 | result = pool.map_async(func, xrange(512))
|
|---|
| 79 | pool.close()
|
|---|
| 80 | pool.join()
|
|---|
| 81 | results = result.get()
|
|---|
| 82 | misses = [fail for fail in results if fail == 'miss']
|
|---|
| 83 | fails = [fail for fail in results if fail is not None and fail != 'miss']
|
|---|
| 84 | print '%s miss(es), %s fail(s)' % (len(misses), len(fails))
|
|---|
| 85 |
|
|---|
| 86 |
|
|---|
| 87 | if __name__ == '__main__':
|
|---|
| 88 | test_multiprocess(test_cache)
|
|---|
| 89 | test_multiprocess(test_safe_cache)
|
|---|