[PATCH STABLE V2] localrepo: discard objects in _filecache at transaction failure (issue4876)
FUJIWARA Katsunori
foozy at lares.dti.ne.jp
Sat Oct 24 00:27:29 CDT 2015
At Sat, 24 Oct 2015 09:32:18 +0900,
FUJIWARA Katsunori wrote:
>
> # HG changeset patch
> # User FUJIWARA Katsunori <foozy at lares.dti.ne.jp>
> # Date 1445644900 -32400
> # Sat Oct 24 09:01:40 2015 +0900
> # Branch stable
> # Node ID 590f5ac256895fb857dafabdefd6863b82679583
> # Parent 39dbf495880b8a439d912091109427d27a7e616a
> localrepo: discard objects in _filecache at transaction failure (issue4876)
Please ignore this posting, because this description is a little
ambiguous.
I'll post revised one, again.
> 'repo.invalidate()' deletes 'filecache'-ed properties by
> 'filecache.__delete__()' below via 'delattr(unfiltered, k)'. But
> cached objects are still kept in 'repo._filecache'.
>
> def __delete__(self, obj):
> try:
> del obj.__dict__[self.name]
> except KeyError:
> raise AttributeError(self.name)
>
> If 'repo' object is reused even after failure of command execution,
> referring 'filecache'-ed property may reuse one kept in
> 'repo._filecache', even if reloading from a file is expected.
>
> Executing command sequence on command server is a typical case of this
> situation (5c0f5db65c6b also tried to fix this issue). For example:
>
> 1. start a command execution
>
> 2. 'changelog.delayupdate()' is invoked in a transaction scope
>
> This replaces own 'opener' by '_divertopener()' for additional
> accessing to '00changelog.i.a'
>
> 3. transaction is aborted, and command (1) execution is ended
>
> After 'repo.invalidate()' at releasing store lock, changelog
> object above (= 'opener' of it is still replaced) is deleted from
> 'repo.__dict__', but still kept in 'repo._filecache'
>
> 4. start next command execution with same 'repo'
>
> 5. referring 'repo.changelog' may reuse changelog object kept in
> 'repo._filecache' according to timestamp
>
> Then, "No such file or directory" error occurs for
> '00changelog.i.a', which is already removed at (3)
>
> This patch discards objects in '_filecache' at transaction failure.
>
> 'repo.invalidate()' at failure of "hg qpush" is removed, because now
> it is redundant.
>
> Changes in 'invalidate()' can't be simplified by 'self._filecache =
> {}', because 'invalidate()' itself should keep 'dirstate' in it.
>
> This patch doesn't make 'repo.invalidate()' always discard objects in
> '_filecache', because 'repo.invalidate()' is invoked also at unlocking
> store lock.
>
> - "always discard objects in filecache at unlocking" may cause
> serious performance problem for subsequent procedures at normal
> execution
>
> - but it is impossible to "discard objects in filecache at unlocking
> only at failure", because 'releasefn' of lock can't know whether a
> lock scope is terminated normally or not
>
> BTW, using "with" statement described in PEP343 for lock may
> resolve this ?
>
> IMHO, fundamental cause of this issue seems that truncation of
> '00changelog.i' occurs at failure of transaction, even though newly
> added revisions exist only in '00changelog.i.a' (aka "pending
> file"). This truncation implies useless updating 'st_mtime' of
> '00changelog.i', even though size of '00changelog.i' isn't changed by
> truncation itself.
>
> This behavior will be fixed by dropping '00changelog.i' at aborting
> from the list of files to be truncated in transaction after code
> freeze.
>
> diff --git a/hgext/mq.py b/hgext/mq.py
> --- a/hgext/mq.py
> +++ b/hgext/mq.py
> @@ -847,7 +847,6 @@
> try:
> tr.abort()
> finally:
> - repo.invalidate()
> self.invalidate()
> raise
> finally:
> diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py
> --- a/mercurial/localrepo.py
> +++ b/mercurial/localrepo.py
> @@ -1014,6 +1014,8 @@
> # out) in this transaction
> repo.vfs.rename('journal.dirstate', 'dirstate')
>
> + repo.invalidate(clearfilecache=True)
> +
> tr = transaction.transaction(rp, self.svfs, vfsmap,
> "journal",
> "undo",
> @@ -1205,13 +1207,15 @@
> pass
> delattr(self.unfiltered(), 'dirstate')
>
> - def invalidate(self):
> + def invalidate(self, clearfilecache=False):
> unfiltered = self.unfiltered() # all file caches are stored unfiltered
> - for k in self._filecache:
> + for k in self._filecache.keys():
> # dirstate is invalidated separately in invalidatedirstate()
> if k == 'dirstate':
> continue
>
> + if clearfilecache:
> + del self._filecache[k]
> try:
> delattr(unfiltered, k)
> except AttributeError:
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel at selenic.com
> https://selenic.com/mailman/listinfo/mercurial-devel
----------------------------------------------------------------------
[FUJIWARA Katsunori] foozy at lares.dti.ne.jp
More information about the Mercurial-devel
mailing list