##// END OF EJS Templates
hg: establish a cache for localrepository instances...
Gregory Szorc -
r26219:ae33fff1 default
parent child Browse files
Show More
@@ -823,3 +823,72 b' def remoteui(src, opts):'
823 823 dst.setconfig('web', 'cacerts', util.expandpath(v), 'copied')
824 824
825 825 return dst
826
827 # Files of interest
828 # Used to check if the repository has changed looking at mtime and size of
829 # theses files.
830 foi = [('spath', '00changelog.i'),
831 ('spath', 'phaseroots'), # ! phase can change content at the same size
832 ('spath', 'obsstore'),
833 ('path', 'bookmarks'), # ! bookmark can change content at the same size
834 ]
835
836 class cachedlocalrepo(object):
837 """Holds a localrepository that can be cached and reused."""
838
839 def __init__(self, repo):
840 """Create a new cached repo from an existing repo.
841
842 We assume the passed in repo was recently created. If the
843 repo has changed between when it was created and when it was
844 turned into a cache, it may not refresh properly.
845 """
846 assert isinstance(repo, localrepo.localrepository)
847 self._repo = repo
848 self._state, self.mtime = self._repostate()
849
850 def fetch(self):
851 """Refresh (if necessary) and return a repository.
852
853 If the cached instance is out of date, it will be recreated
854 automatically and returned.
855
856 Returns a tuple of the repo and a boolean indicating whether a new
857 repo instance was created.
858 """
859 # We compare the mtimes and sizes of some well-known files to
860 # determine if the repo changed. This is not precise, as mtimes
861 # are susceptible to clock skew and imprecise filesystems and
862 # file content can change while maintaining the same size.
863
864 state, mtime = self._repostate()
865 if state == self._state:
866 return self._repo, False
867
868 self._repo = repository(self._repo.baseui, self._repo.url())
869 self._state = state
870 self.mtime = mtime
871
872 return self._repo, True
873
874 def _repostate(self):
875 state = []
876 maxmtime = -1
877 for attr, fname in foi:
878 prefix = getattr(self._repo, attr)
879 p = os.path.join(prefix, fname)
880 try:
881 st = os.stat(p)
882 except OSError:
883 st = os.stat(prefix)
884 state.append((st.st_mtime, st.st_size))
885 maxmtime = max(maxmtime, st.st_mtime)
886
887 return tuple(state), maxmtime
888
889 def copy(self):
890 """Obtain a copy of this class instance."""
891 c = cachedlocalrepo(self._repo)
892 c._state = self._state
893 c.mtime = self.mtime
894 return c
@@ -25,15 +25,6 b' perms = {'
25 25 'pushkey': 'push',
26 26 }
27 27
28 ## Files of interest
29 # Used to check if the repository has changed looking at mtime and size of
30 # theses files. This should probably be relocated a bit higher in core.
31 foi = [('spath', '00changelog.i'),
32 ('spath', 'phaseroots'), # ! phase can change content at the same size
33 ('spath', 'obsstore'),
34 ('path', 'bookmarks'), # ! bookmark can change content at the same size
35 ]
36
37 28 def makebreadcrumb(url, prefix=''):
38 29 '''Return a 'URL breadcrumb' list
39 30
@@ -66,8 +57,8 b' class requestcontext(object):'
66 57 is prone to race conditions. Instances of this class exist to hold
67 58 mutable and race-free state for requests.
68 59 """
69 def __init__(self, app):
70 self.repo = app.repo
60 def __init__(self, app, repo):
61 self.repo = repo
71 62 self.reponame = app.reponame
72 63
73 64 self.archives = ('zip', 'gz', 'bz2')
@@ -217,10 +208,8 b' class hgweb(object):'
217 208 # break some wsgi implementation.
218 209 r.ui.setconfig('progress', 'disable', 'true', 'hgweb')
219 210 r.baseui.setconfig('progress', 'disable', 'true', 'hgweb')
220 self.repo = self._webifyrepo(r)
211 self._repo = hg.cachedlocalrepo(self._webifyrepo(r))
221 212 hook.redirect(True)
222 self.repostate = None
223 self.mtime = -1
224 213 self.reponame = name
225 214
226 215 def _webifyrepo(self, repo):
@@ -228,25 +217,13 b' class hgweb(object):'
228 217 self.websubtable = webutil.getwebsubs(repo)
229 218 return repo
230 219
231 def refresh(self):
232 repostate = []
233 mtime = 0
234 # file of interrests mtime and size
235 for meth, fname in foi:
236 prefix = getattr(self.repo, meth)
237 st = get_stat(prefix, fname)
238 repostate.append((st.st_mtime, st.st_size))
239 mtime = max(mtime, st.st_mtime)
240 repostate = tuple(repostate)
241 # we need to compare file size in addition to mtime to catch
242 # changes made less than a second ago
243 if repostate != self.repostate:
244 r = hg.repository(self.repo.baseui, self.repo.url())
245 self.repo = self._webifyrepo(r)
246 # update these last to avoid threads seeing empty settings
247 self.repostate = repostate
248 # mtime is needed for ETag
249 self.mtime = mtime
220 def _getrepo(self):
221 r, created = self._repo.fetch()
222 if created:
223 r = self._webifyrepo(r)
224
225 self.mtime = self._repo.mtime
226 return r
250 227
251 228 def run(self):
252 229 """Start a server from CGI environment.
@@ -274,8 +251,8 b' class hgweb(object):'
274 251 This is typically only called by Mercurial. External consumers
275 252 should be using instances of this class as the WSGI application.
276 253 """
277 self.refresh()
278 rctx = requestcontext(self)
254 repo = self._getrepo()
255 rctx = requestcontext(self, repo)
279 256
280 257 # This state is global across all threads.
281 258 encoding.encoding = rctx.config('web', 'encoding', encoding.encoding)
@@ -115,8 +115,6 b" Check hgweb's load order:"
115 115 3) bar extsetup
116 116 4) foo reposetup
117 117 4) bar reposetup
118 4) foo reposetup
119 4) bar reposetup
120 118
121 119 $ echo 'foo = !' >> $HGRCPATH
122 120 $ echo 'bar = !' >> $HGRCPATH
@@ -64,7 +64,7 b' by the WSGI standard and strictly implem'
64 64 > print '---- OS.ENVIRON wsgi variables'
65 65 > print sorted([x for x in os.environ if x.startswith('wsgi')])
66 66 > print '---- request.ENVIRON wsgi variables'
67 > print sorted([x for x in i.repo.ui.environ if x.startswith('wsgi')])
67 > print sorted([x for x in i._getrepo().ui.environ if x.startswith('wsgi')])
68 68 > EOF
69 69 $ python request.py
70 70 ---- STATUS
General Comments 0
You need to be logged in to leave comments. Login now