fncachestore: defer updating the fncache file to a single file open
authorAdrian Buehlmann <adrian@cadifra.com>
Fri, 28 Jan 2011 13:38:34 +0100
changeset 13391 d00bbff8600e
parent 13390 327719a44b6a
child 13392 777cef34a890
fncachestore: defer updating the fncache file to a single file open Defers updating the fncache file with newly added entries to the end of the transaction (on e.g. pull), doing a single open call on the fncache file, instead of opening and closing it each time a new entry is added to the store. Implemented by adding a new abstract write() function on store.basicstore and registering it as a release function on the store lock in localrepo.lock (compare with dirstate.write). store.fncachestore overrides write() from basicstore and calls a new write function on the fncache object, which writes all entries to the fncache file if it's dirty. store.fncache.add() now just marks itself as dirty if a new name is added.
mercurial/localrepo.py
mercurial/store.py
--- a/mercurial/localrepo.py	Fri Jan 28 13:34:07 2011 +0100
+++ b/mercurial/localrepo.py	Fri Jan 28 13:38:34 2011 +0100
@@ -781,8 +781,8 @@
             l.lock()
             return l
 
-        l = self._lock(self.sjoin("lock"), wait, None, self.invalidate,
-                       _('repository %s') % self.origroot)
+        l = self._lock(self.sjoin("lock"), wait, self.store.write,
+                       self.invalidate, _('repository %s') % self.origroot)
         self._lockref = weakref.ref(l)
         return l
 
--- a/mercurial/store.py	Fri Jan 28 13:34:07 2011 +0100
+++ b/mercurial/store.py	Fri Jan 28 13:38:34 2011 +0100
@@ -213,6 +213,9 @@
     def copylist(self):
         return ['requires'] + _data.split()
 
+    def write(self):
+        pass
+
 class encodedstore(basicstore):
     def __init__(self, path, opener, pathjoiner):
         self.pathjoiner = pathjoiner
@@ -243,10 +246,12 @@
     def __init__(self, opener):
         self.opener = opener
         self.entries = None
+        self._dirty = False
 
     def _load(self):
         '''fill the entries from the fncache file'''
         self.entries = set()
+        self._dirty = False
         try:
             fp = self.opener('fncache', mode='rb')
         except IOError:
@@ -265,12 +270,22 @@
             fp.write(encodedir(p) + '\n')
         fp.close()
         self.entries = set(files)
+        self._dirty = False
+
+    def write(self):
+        if not self._dirty:
+            return
+        fp = self.opener('fncache', mode='wb', atomictemp=True)
+        for p in self.entries:
+            fp.write(encodedir(p) + '\n')
+        fp.rename()
+        self._dirty = False
 
     def add(self, fn):
         if self.entries is None:
             self._load()
         if fn not in self.entries:
-            self.opener('fncache', 'ab').write(encodedir(fn) + '\n')
+            self._dirty = True
             self.entries.add(fn)
 
     def __contains__(self, fn):
@@ -328,6 +343,9 @@
         return (['requires', '00changelog.i'] +
                 [self.pathjoiner('store', f) for f in d.split()])
 
+    def write(self):
+        self.fncache.write()
+
 def store(requirements, path, opener, pathjoiner=None):
     pathjoiner = pathjoiner or os.path.join
     if 'store' in requirements: