[PATCH STABLE] localrepo: discard objects in _filecache at transaction failure (issue4876)
Augie Fackler
raf at durin42.com
Thu Oct 22 05:40:17 CDT 2015
On Thu, Oct 22, 2015 at 05:20:17AM +0900, FUJIWARA Katsunori wrote:
> # HG changeset patch
> # User FUJIWARA Katsunori <foozy at lares.dti.ne.jp>
> # Date 1445458662 -32400
> # Thu Oct 22 05:17:42 2015 +0900
> # Branch stable
> # Node ID 865862bdaa59319b0304864ca20066a81529830d
> # Parent b57e5bfaad7ccc9d45884717115ba9a36d6b7b41
> localrepo: discard objects in _filecache at transaction failure (issue4876)
>
> '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 (a710936c3037 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.
>
> 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.
>
> In order to avoid this redundant truncation in the future,
> 'revlog._writeentry()' and so on may have to be changed around
> 'transaction.add()' invocations.
>
> 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]
I can't comment on the correctness of the patch (it looks
superficially reasonable to me), but could we do self._filecache = {}
instead of del-ing each filecache entry individually?
> try:
> delattr(unfiltered, k)
> except AttributeError:
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel at selenic.com
> https://selenic.com/mailman/listinfo/mercurial-devel
More information about the Mercurial-devel
mailing list