[PATCH 1 of 3 RFC] transaction: ability to chain multiple transactions together

Gregory Szorc gregory.szorc at gmail.com
Wed May 28 01:00:45 CDT 2014


# HG changeset patch
# User Gregory Szorc <gregory.szorc at gmail.com>
# Date 1401247442 25200
#      Tue May 27 20:24:02 2014 -0700
# Node ID 13eea2d1b1ada719436f52596a71d8286d7c1391
# Parent  652e07debf10193f4973a48ead96a95e81d0a55b
transaction: ability to chain multiple transactions together

A subsequent patch will add caches to transactions. The ideal way to
achieve this would be to use repo.opener instead of repo.sopener in
localrepository.transaction(). However, this change is non-trivial
and very invasive.

The solution we employ instead is to give transactions the ability
to "chain" together. We can now create multiple transaction instances
and trigger operations on all of them by performing an operation on
the "parent" transaction. While this will be used as the easy solution
for adding caches to transactions, it also enables other advanced use
cases, such as creating transactions for multiple repositories and
easily operating on all of them without involving complex nested
try..finally blocks.

diff --git a/mercurial/transaction.py b/mercurial/transaction.py
--- a/mercurial/transaction.py
+++ b/mercurial/transaction.py
@@ -85,8 +85,9 @@ class transaction(object):
         self.map = {}
         self.backupmap = {}
         self.journal = journal
         self._queue = []
+        self._chained = []
         # a dict of arguments to be passed to hooks
         self.hookargs = {}
 
         self.backupjournal = "%s.backupfiles" % journal
@@ -172,8 +173,24 @@ class transaction(object):
         self.backupsfile.write("%s\0%s\0" % (file, backupfile))
         self.backupsfile.flush()
 
     @active
+    def chaintransaction(self, tr):
+        """Chains another transaction to this one.
+
+        Sometimes it is desirable to have multiple simultaneous transactions
+        with mutually exclusive stores/vfss. This function allows you to
+        chain/associate another transaction with this one. Transaction
+        terminating actions such as close and abort will be applied to chained
+        transactions as well. This makes calling logic interfacing with
+        multiple transaction easier to manage.
+
+        Operations on chained transactions will be performed after they are
+        performed on this transaction.
+        """
+        self._chained.append(tr)
+
+    @active
     def find(self, file):
         if file in self.map:
             return self.entries[self.map[file]]
         if file in self.backupmap:
@@ -232,8 +249,11 @@ class transaction(object):
                 self.opener.unlink(b)
         self.backupentries = []
         self.journal = None
 
+        for tr in self._chained:
+            tr.close()
+
     @active
     def abort(self):
         '''abort the transaction (generally called on error, or when the
         transaction is not explicitly committed before going out of
@@ -261,8 +281,11 @@ class transaction(object):
 
             try:
                 _playback(self.journal, self.report, self.opener,
                           self.entries, self.backupentries, False)
+                for tr in self._chained:
+                    tr._abort()
+
                 self.report(_("rollback completed\n"))
             except Exception:
                 self.report(_("rollback failed - please run hg recover\n"))
         finally:


More information about the Mercurial-devel mailing list