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

mail settings
Submitter Gregory Szorc
Date May 28, 2014, 6 a.m.
Message ID <>
Download mbox | patch
Permalink /patch/4880/
State Changes Requested
Headers show


Gregory Szorc - May 28, 2014, 6 a.m.
# HG changeset patch
# User Gregory Szorc <>
# 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/ b/mercurial/
--- a/mercurial/
+++ b/mercurial/
@@ -85,8 +85,9 @@  class transaction(object): = {}
         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))
+    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
             return self.entries[[file]]
         if file in self.backupmap:
@@ -232,8 +249,11 @@  class transaction(object):
         self.backupentries = []
         self.journal = None
+        for tr in self._chained:
+            tr.close()
     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):
                 _playback(self.journal,, self.opener,
                           self.entries, self.backupentries, False)
+                for tr in self._chained:
+                    tr._abort()
       "rollback completed\n"))
             except Exception:
       "rollback failed - please run hg recover\n"))