Patchwork [1,of,4] bundle2: make it possible have a global transaction for the unbundling

login
register
mail settings
Submitter Pierre-Yves David
Date April 4, 2014, 10:29 p.m.
Message ID <6107f67dd40949dab34c.1396650578@marginatus.alto.octopoid.net>
Download mbox | patch
Permalink /patch/4230/
State Accepted
Commit b24ee5076b94ea446a1e04dd976a1a9e409f0130
Headers show

Comments

Pierre-Yves David - April 4, 2014, 10:29 p.m.
# HG changeset patch
# User Pierre-Yves David <pierre-yves.david@fb.com>
# Date 1396508209 25200
#      Wed Apr 02 23:56:49 2014 -0700
# Node ID 6107f67dd40949dab34cb2501e7c2c20d5f8a0cf
# Parent  c7ceae0faf6997ff3c262a0b641719b6fd357055
bundle2: make it possible have a global transaction for the unbundling.

We use the `gettransaction` method approach already used for pull. We need this
because we do not know beforehand if the bundle need a transaction to be
created. And (1) we do not want to create a transaction for nothing. (2) Some
bundle2 may be read only and doesn't require any lock or transaction to be held.

Patch

diff --git a/mercurial/bundle2.py b/mercurial/bundle2.py
--- a/mercurial/bundle2.py
+++ b/mercurial/bundle2.py
@@ -236,27 +236,38 @@  class bundleoperation(object):
     * a way to retrieve a transaction to add changes to the repo,
     * a way to record the result of processing each part,
     * a way to construct a bundle response when applicable.
     """
 
-    def __init__(self, repo):
+    def __init__(self, repo, transactiongetter):
         self.repo = repo
         self.ui = repo.ui
         self.records = unbundlerecords()
+        self.gettransaction = transactiongetter
 
-def processbundle(repo, unbundler):
+class TransactionUnavailable(RuntimeError):
+    pass
+
+def _notransaction():
+    """default method to get a transaction while processing a bundle
+
+    Raise an exception to highlight the fact that no transaction was expected
+    to be created"""
+    raise TransactionUnavailable()
+
+def processbundle(repo, unbundler, transactiongetter=_notransaction):
     """This function process a bundle, apply effect to/from a repo
 
     It iterates over each part then searches for and uses the proper handling
     code to process the part. Parts are processed in order.
 
     This is very early version of this function that will be strongly reworked
     before final usage.
 
     Unknown Mandatory part will abort the process.
     """
-    op = bundleoperation(repo)
+    op = bundleoperation(repo, transactiongetter)
     # todo:
     # - replace this is a init function soon.
     # - exception catching
     unbundler.params
     iterparts = iter(unbundler)
@@ -530,10 +541,16 @@  def handlechangegroup(op, part):
     """apply a changegroup part on the repo
 
     This is a very early implementation that will massive rework before being
     inflicted to any end-user.
     """
+    # Make sure we trigger a transaction creation
+    #
+    # The addchangegroup function will get a transaction object by itself, but
+    # we need to make sure we trigger the creation of a transaction object used
+    # for the whole processing scope.
+    op.gettransaction()
     data = StringIO.StringIO(part.data)
     data.seek(0)
     cg = changegroup.readbundle(data, 'bundle2part')
     ret = changegroup.addchangegroup(op.repo, cg, 'bundle2', 'bundle2')
     op.records.add('changegroup', {'return': ret})
diff --git a/tests/test-bundle2.t b/tests/test-bundle2.t
--- a/tests/test-bundle2.t
+++ b/tests/test-bundle2.t
@@ -94,17 +94,22 @@  Create an extension to test bundle2 API
   > 
   > @command('unbundle2', [], '')
   > def cmdunbundle2(ui, repo):
   >     """process a bundle2 stream from stdin on the current repo"""
   >     try:
+  >         tr = None
   >         lock = repo.lock()
+  >         tr = repo.transaction('processbundle')
   >         try:
   >             unbundler = bundle2.unbundle20(ui, sys.stdin)
-  >             op = bundle2.processbundle(repo, unbundler)
+  >             op = bundle2.processbundle(repo, unbundler, lambda: tr)
+  >             tr.close()
   >         except KeyError, exc:
   >             raise util.Abort('missing support for %s' % exc)
   >     finally:
+  >         if tr is not None:
+  >             tr.release()
   >         lock.release()
   >         remains = sys.stdin.read()
   >         ui.write('%i unread bytes\n' % len(remains))
   >     if op.records['song']:
   >         totalverses = sum(r['verses'] for r in op.records['song'])