Commit 2b111356 authored by teor's avatar teor Committed by juga
Browse files

Stop deleting the latest.v3bw symlink. Instead, do an atomic rename.

Also:
* document the location of latest.v3bw
* document the atomic creation of latest.v3bw
* explain how to atomically create latest.v3bw when transferring it to
  another host

Closes tor trac 26740.
parent 53a1bf8b
...@@ -12,9 +12,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ...@@ -12,9 +12,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Log line on start up with sbws version, platform info, and library versions - Log line on start up with sbws version, platform info, and library versions
(trac#26751) (trac#26751)
### Fixed
- Stop deleting the latest.v3bw symlink. Instead, do an atomic rename.
(#26740)
### Changed ### Changed
- Document at which times should v3bw files be generated (#26740)
- Remove test data v3bw file and generate it from the same test. (#26736) - Remove test data v3bw file and generate it from the same test. (#26736)
### Fixed ### Fixed
......
[paths] [paths]
datadir = ${sbws_home}/datadir datadir = ${sbws_home}/datadir
v3bw_dname = ${sbws_home}/v3bw v3bw_dname = ${sbws_home}/v3bw
# The latest bandwidth file is atomically symlinked to
# V3BandwidthsFile ${v3bw_dname}/latest.v3bw
v3bw_fname = ${v3bw_dname}/{}.v3bw v3bw_fname = ${v3bw_dname}/{}.v3bw
started_filepath = ${sbws_home}/started_at started_filepath = ${sbws_home}/started_at
log_dname = ${sbws_home}/log log_dname = ${sbws_home}/log
......
...@@ -12,12 +12,13 @@ log = logging.getLogger(__name__) ...@@ -12,12 +12,13 @@ log = logging.getLogger(__name__)
def gen_parser(sub): def gen_parser(sub):
d = 'Generate a v3bw file based on recent results. A v3bw file is the '\ d = 'Generate a v3bw file based on recent results. A v3bw file is the '\
'file Tor directory authorities want to read and base their '\ 'file Tor directory authorities want to read and base their '\
'bandwidth votes on.' \ 'bandwidth votes on. '\
'This file should be generated every hour at any minute, except ' \ 'To avoid inconsistent reads, configure tor with '\
'between 45 and 55 minutes past the hour because Tor read this file '\ '"V3BandwidthsFile /path/to/latest.v3bw". '\
' at minute 50 and except ' \ '(latest.v3bw is an atomically created symlink in the same '\
'between 15 and 25 minutes past the hour because Tor read this file '\ 'directory as output.) '\
' at minute 20 during a consensus failure.' 'If the file is transferred to another host, it should be written to '\
'a temporary path, then renamed to the V3BandwidthsFile path.'
p = sub.add_parser('generate', description=d, p = sub.add_parser('generate', description=d,
formatter_class=ArgumentDefaultsHelpFormatter) formatter_class=ArgumentDefaultsHelpFormatter)
p.add_argument('--output', default=None, type=str, p.add_argument('--output', default=None, type=str,
......
...@@ -413,17 +413,24 @@ class V3BWFile(object): ...@@ -413,17 +413,24 @@ class V3BWFile(object):
log.info("Writing to stdout is not supported.") log.info("Writing to stdout is not supported.")
return return
log.info('Writing v3bw file to %s', output) log.info('Writing v3bw file to %s', output)
# To avoid inconsistent reads, the bandwidth data is written to an
# archive path, then atomically symlinked to 'latest.v3bw'
out_dir = os.path.dirname(output) out_dir = os.path.dirname(output)
out_link = os.path.join(out_dir, 'latest.v3bw') out_link = os.path.join(out_dir, 'latest.v3bw')
if os.path.exists(out_link): out_link_tmp = out_link + '.tmp'
log.debug('Deleting existing symlink before creating a new one.')
os.remove(out_link)
with DirectoryLock(out_dir): with DirectoryLock(out_dir):
with open(output, 'wt') as fd: with open(output, 'wt') as fd:
fd.write(str(self.header)) fd.write(str(self.header))
for line in self.bw_lines: for line in self.bw_lines:
fd.write(str(line)) fd.write(str(line))
output_basename = os.path.basename(output) output_basename = os.path.basename(output)
log.debug('Creating symlink from {} to {}.' # To atomically symlink a file, we need to create a temporary link,
.format(output_basename, out_link)) # then rename it to the final link name. (POSIX guarantees that
os.symlink(output_basename, out_link) # rename is atomic.)
log.debug('Creating symlink {} -> {}.'
.format(out_link_tmp, output_basename))
os.symlink(output_basename, out_link_tmp)
log.debug('Renaming symlink {} -> {} to {} -> {}.'
.format(out_link_tmp, output_basename,
out_link, output_basename))
os.rename(out_link_tmp, out_link)
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment