Merge "Prevent corruption of cache by using atomic update"
This commit is contained in:
commit
008082568c
@ -23,6 +23,7 @@ import operator
|
|||||||
import os
|
import os
|
||||||
from pprint import pformat
|
from pprint import pformat
|
||||||
import re
|
import re
|
||||||
|
import tempfile
|
||||||
import time
|
import time
|
||||||
import xml.etree.ElementTree as XML
|
import xml.etree.ElementTree as XML
|
||||||
import yaml
|
import yaml
|
||||||
@ -45,8 +46,10 @@ class CacheStorage(object):
|
|||||||
# modules so that they are available to be used when the destructor
|
# 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
|
# is being called since python will not guarantee that it won't have
|
||||||
# removed global module references during teardown.
|
# removed global module references during teardown.
|
||||||
_yaml = yaml
|
|
||||||
_logger = logger
|
_logger = logger
|
||||||
|
_os = os
|
||||||
|
_tempfile = tempfile
|
||||||
|
_yaml = yaml
|
||||||
|
|
||||||
def __init__(self, jenkins_url, flush=False):
|
def __init__(self, jenkins_url, flush=False):
|
||||||
cache_dir = self.get_cache_dir()
|
cache_dir = self.get_cache_dir()
|
||||||
@ -97,23 +100,29 @@ class CacheStorage(object):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def save(self):
|
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__
|
# due to an exception occurring in the __init__
|
||||||
if getattr(self, 'data', None) is not None:
|
if getattr(self, 'data', None) is not None:
|
||||||
try:
|
try:
|
||||||
with io.open(self.cachefilename, 'w',
|
self.save()
|
||||||
encoding='utf-8') as yfile:
|
|
||||||
self._yaml.dump(self.data, yfile)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self._logger.error("Failed to write to cache file '%s' on "
|
self._logger.error("Failed to write to cache file '%s' on "
|
||||||
"exit: %s" % (self.cachefilename, e))
|
"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):
|
class Jenkins(object):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user