Merge "Prevent corruption of cache by using atomic update"

This commit is contained in:
Jenkins 2016-05-21 00:15:32 +00:00 committed by Gerrit Code Review
commit 008082568c
1 changed files with 21 additions and 12 deletions

View File

@ -23,6 +23,7 @@ import operator
import os
from pprint import pformat
import re
import tempfile
import time
import xml.etree.ElementTree as XML
import yaml
@ -45,8 +46,10 @@ class CacheStorage(object):
# modules so that they are available to be used when the destructor
# is being called since python will not guarantee that it won't have
# removed global module references during teardown.
_yaml = yaml
_logger = logger
_os = os
_tempfile = tempfile
_yaml = yaml
def __init__(self, jenkins_url, flush=False):
cache_dir = self.get_cache_dir()
@ -97,23 +100,29 @@ class CacheStorage(object):
return True
def save(self):
# check we initialized sufficiently in case called via __del__
# use self references to required modules in case called via __del__
# write to tempfile under same directory and then replace to avoid
# issues around corruption such the process be killed
tfile = self._tempfile.NamedTemporaryFile(dir=self.get_cache_dir(),
delete=False)
self._yaml.dump(self.data, utils.wrap_stream(tfile))
# force contents to be synced on disk before overwriting cachefile
tfile.flush()
self._os.fsync(tfile.fileno())
tfile.close()
self._os.rename(tfile.name, self.cachefilename)
self._logger.debug("Cache written out to '%s'" % self.cachefilename)
def __del__(self):
# check we initialized sufficiently in case called
# due to an exception occurring in the __init__
if getattr(self, 'data', None) is not None:
try:
with io.open(self.cachefilename, 'w',
encoding='utf-8') as yfile:
self._yaml.dump(self.data, yfile)
self.save()
except Exception as e:
self._logger.error("Failed to write to cache file '%s' on "
"exit: %s" % (self.cachefilename, e))
else:
self._logger.info("Cache saved")
self._logger.debug("Cache written out to '%s'" %
self.cachefilename)
def __del__(self):
self.save()
class Jenkins(object):