[PATCH] transaction: move changelog finalizer to be before bookmark finalizer

Ryan McElroy rm at fb.com
Fri Mar 18 14:22:26 EDT 2016


On 3/16/2016 11:31 PM, Durham Goode wrote:
> # HG changeset patch
> # User Durham Goode <durham at fb.com>
> # Date 1458196059 25200
> #      Wed Mar 16 23:27:39 2016 -0700
> # Node ID cdf4a94fdc46d27196f5411fc7a4008690834fba
> # Parent  ed75909c4c670a7d9db4a2bef9817a0d5f0b4d9c
> transaction: move changelog finalizer to be before bookmark finalizer
>
> Previously, transaction close would run the file generators before running the
> finalizers (see the list below for what is in each). Since file generators
> contain the bookmarks and the dirstate, this meant we made the dirstate and
> bookmarks visible to external readers before we actually wrote the commits into
> the changelog, which could result in missing bookmarks and missing working copy
> parents (especially on servers with high commit throughput, since pulls might
> fail to see certain bookmarks in this situation).
>
> By moving the changelog writing to be before the bookmark/dirstate writing, we
> ensure the commits are present before they are referenced.
>
> For reference, file generators currently consist of: bookmarks, dirstate, and
> phases. Finalizers currently consist of: changelog, revbranchcache, and fncache.
> All of the former reference the latter, so therefore the latter should be
> written first.
>
> Technically there's still plenty of race conditions (can the order of finalizers
> affect how external readers see the repo?), but this is a step forward at least.
>
> diff --git a/mercurial/transaction.py b/mercurial/transaction.py
> --- a/mercurial/transaction.py
> +++ b/mercurial/transaction.py
> @@ -407,10 +407,10 @@ class transaction(object):
>           '''commit the transaction'''
>           if self.count == 1:
>               self.validator(self)  # will raise exception if needed
> -            self._generatefiles()
>               categories = sorted(self._finalizecallback)
>               for cat in categories:
>                   self._finalizecallback[cat](self)
> +            self._generatefiles()
>   
>           self.count -= 1
>           if self.count != 0:
> diff --git a/tests/test-bookmarks.t b/tests/test-bookmarks.t
> --- a/tests/test-bookmarks.t
> +++ b/tests/test-bookmarks.t
> @@ -846,3 +846,52 @@ updates the working directory and curren
>     6:81dcce76aa0b
>     $ hg -R ../cloned-bookmarks-update bookmarks | grep ' Y '
>      * Y                         6:81dcce76aa0b
> +
> +  $ cd ..
> +
> +ensure changelog is written before bookmarks
> +  $ hg init orderrepo
> +  $ cd orderrepo
> +  $ touch a
> +  $ hg commit -Aqm one
> +  $ hg book mybook
> +  $ echo a > a
> +
> +  $ cat > $TESTTMP/pausefinalize.py <<EOF
> +  > from mercurial import extensions, localrepo
> +  > import os, time
> +  > def transaction(orig, self, desc, report=None):
> +  >    tr = orig(self, desc, report)
> +  >    def sleep(*args, **kwargs):
> +  >        retry = 20
> +  >        while retry > 0 and not os.path.exists("$TESTTMP/unpause"):
> +  >            retry -= 1
> +  >            time.sleep(0.5)
> +  >        if os.path.exists("$TESTTMP/unpause"):
> +  >            os.remove("$TESTTMP/unpause")
> +  >    # It is important that this finalizer start with 'a', so it runs before
> +  >    # the changelog finalizer appends to the changelog.
> +  >    tr.addfinalize('a-sleep', sleep)
> +  >    return tr
> +  >
> +  > def extsetup(ui):
> +  >    # This extension inserts an artifical pause during the transaction
> +  >    # finalizer, so we can run commands mid-transaction-close.
> +  >    extensions.wrapfunction(localrepo.localrepository, 'transaction',
> +  >                            transaction)
> +  > EOF
> +  $ hg commit -qm two --config extensions.pausefinalize=$TESTTMP/pausefinalize.py &
> +  $ sleep 2
> +  $ hg log -r .
> +  changeset:   0:867bc5792c8c
> +  bookmark:    mybook
> +  tag:         tip
> +  user:        test
> +  date:        Thu Jan 01 00:00:00 1970 +0000
> +  summary:     one
> +
> +  $ hg bookmarks
> +   * mybook                    0:867bc5792c8c
> +  $ touch $TESTTMP/unpause
> +
> +  $ cd ..
>
This patch looks minimal and safe to me. The particular race condition 
that currently exists is really bad in high-commit-rate repositories -- 
bookmarks often "go missing"altogether, which is a terrible user experience.


More information about the Mercurial-devel mailing list