From 6b9789250e19c4e02c1c8efad80cf324bb787466 Mon Sep 17 00:00:00 2001
From: Dan Callaghan <dcallagh@redhat.com>
Date: Wed, 7 May 2014 11:17:38 +1000
Subject: [PATCH 07/14] Create cache entries atomically in FilesystemCache
The filesystem cache needs to ensure entries only become visible once
they are completely written. Otherwise multiple processes sharing
a cache directory could see empty or incomplete files while they are
being written by another process. Fixes #313.
This patch uses the standard write-then-rename dance for POSIX systems.
I'm not sure if it works on Windows though...
(cherry picked from commit 18ffd83655f11898d4f7f52e9ed47e892a6955b9)
diff --git a/src/webassets/cache.py b/src/webassets/cache.py
index d1245e6..af2590e 100644
--- a/src/webassets/cache.py
+++ b/src/webassets/cache.py
@@ -16,6 +16,7 @@ also serve in other places.
import os
from os import path
import errno
+import tempfile
from webassets import six
from webassets.merge import BaseHunk
from webassets.filter import Filter, freezedicts
@@ -186,12 +187,18 @@ class FilesystemCache(BaseCache):
return safe_unpickle(result)
def set(self, key, data):
- filename = path.join(self.directory, '%s' % make_md5(self.V, key))
- f = open(filename, 'wb')
+ md5 = '%s' % make_md5(self.V, key)
+ filename = path.join(self.directory, md5)
+ fd, temp_filename = tempfile.mkstemp(prefix='.' + md5,
+ dir=self.directory)
try:
- f.write(pickle.dumps(data))
- finally:
- f.close()
+ with os.fdopen(fd, 'wb') as f:
+ pickle.dump(data, f)
+ f.flush()
+ os.rename(temp_filename, filename)
+ except:
+ os.unlink(temp_filename)
+ raise
def get_cache(option, env):
--
1.9.3