changeset 281:33625fa61d6c

* If a slave disconnects after the master has started to create a snapshot archive for it, just remain calm and keep the archive in place. * Fix a potential race condition where two slaves could trigger the same snapshot archive to be created.
author cmlenz
date Fri, 14 Oct 2005 19:50:08 +0000
parents 24ba49dee33c
children 3950b8141e85
files bitten/master.py bitten/snapshot.py
diffstat 2 files changed, 30 insertions(+), 14 deletions(-) [+]
line wrap: on
line diff
--- a/bitten/master.py
+++ b/bitten/master.py
@@ -177,6 +177,10 @@
                     if worker.isAlive():
                         self.master.schedule(2, _check_snapshot)
                     else:
+                        if self.name not in self.master.handlers:
+                            # The slave disconnected while we were building
+                            # the archive
+                            return
                         snapshot = snapshots.get(build.rev)
                         if snapshot is None:
                             log.error('Failed to create snapshot archive for '
--- a/bitten/snapshot.py
+++ b/bitten/snapshot.py
@@ -99,6 +99,8 @@
         self._lock = threading.RLock()
         self._cleanup()
 
+        self._workers = {}
+
     def _scan(self):
         """Find all existing snapshots in the directory."""
         for filename in [f for f in os.listdir(self.directory)
@@ -150,23 +152,31 @@
         function is the thread object. The caller may use this object to check
         for completion of the operation.
         """
-        prefix = self.prefix + '_r' + str(rev)
-        filename = prefix + '.zip'
-        filepath = os.path.join(self.directory, filename)
-        if os.path.exists(filepath):
-            raise IOError, 'Snapshot file already exists at %s' % filepath
+        self._lock.acquire()
+        try:
+            repos = self.env.get_repository()
+            root = repos.get_node(self.config.path or '/', rev)
+            assert root.isdir, '"%s" is not a directory' % self.config.path
 
-        repos = self.env.get_repository()
-        root = repos.get_node(self.config.path or '/', rev)
-        assert root.isdir, '"%s" is not a directory' % self.config.path
+            if root.rev in self._workers:
+                return self._workers[root.rev]
 
-        self._cleanup(self.limit - 1)
+            prefix = self.prefix + '_r' + str(rev)
+            filename = prefix + '.zip'
+            filepath = os.path.join(self.directory, filename)
+            if os.path.exists(filepath):
+                raise IOError, 'Snapshot file already exists at %s' % filepath
 
-        worker = threading.Thread(target=self._create,
-                                  args=(prefix, root, filepath),
-                                  name='Create snapshot %s' % filename)
-        worker.start()
-        return worker
+            self._cleanup(self.limit - 1)
+
+            worker = threading.Thread(target=self._create,
+                                      args=(prefix, root, filepath),
+                                      name='Create snapshot %s' % filename)
+            worker.start()
+            self._workers[root.rev] = worker
+            return worker
+        finally:
+            self._lock.release()
 
     def _create(self, prefix, root, filepath):
         """Actually create a snapshot archive.
@@ -185,6 +195,7 @@
                 path = os.path.join(prefix, name).rstrip('/\\') + '/'
                 info = zipfile.ZipInfo(path)
                 zip.writestr(info, '')
+                log.debug('Adding directory %s to archive' % name)
                 for entry in node.get_entries():
                     _add_entry(entry)
                 time.sleep(.5) # be nice
@@ -211,6 +222,7 @@
         self._lock.acquire()
         try:
             self._index.append((os.path.getmtime(filepath), root.rev, filepath))
+            del self._workers[root.rev]
         finally:
             self._lock.release()
         log.info('Prepared snapshot archive at %s', filepath)
Copyright (C) 2012-2017 Edgewall Software