##// 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 dst.setconfig('web', 'cacerts', util.expandpath(v), 'copied')
823 dst.setconfig('web', 'cacerts', util.expandpath(v), 'copied')
824
824
825 return dst
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 'pushkey': 'push',
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 def makebreadcrumb(url, prefix=''):
28 def makebreadcrumb(url, prefix=''):
38 '''Return a 'URL breadcrumb' list
29 '''Return a 'URL breadcrumb' list
39
30
@@ -66,8 +57,8 b' class requestcontext(object):'
66 is prone to race conditions. Instances of this class exist to hold
57 is prone to race conditions. Instances of this class exist to hold
67 mutable and race-free state for requests.
58 mutable and race-free state for requests.
68 """
59 """
69 def __init__(self, app):
60 def __init__(self, app, repo):
70 self.repo = app.repo
61 self.repo = repo
71 self.reponame = app.reponame
62 self.reponame = app.reponame
72
63
73 self.archives = ('zip', 'gz', 'bz2')
64 self.archives = ('zip', 'gz', 'bz2')
@@ -217,10 +208,8 b' class hgweb(object):'
217 # break some wsgi implementation.
208 # break some wsgi implementation.
218 r.ui.setconfig('progress', 'disable', 'true', 'hgweb')
209 r.ui.setconfig('progress', 'disable', 'true', 'hgweb')
219 r.baseui.setconfig('progress', 'disable', 'true', 'hgweb')
210 r.baseui.setconfig('progress', 'disable', 'true', 'hgweb')
220 self.repo = self._webifyrepo(r)
211 self._repo = hg.cachedlocalrepo(self._webifyrepo(r))
221 hook.redirect(True)
212 hook.redirect(True)
222 self.repostate = None
223 self.mtime = -1
224 self.reponame = name
213 self.reponame = name
225
214
226 def _webifyrepo(self, repo):
215 def _webifyrepo(self, repo):
@@ -228,25 +217,13 b' class hgweb(object):'
228 self.websubtable = webutil.getwebsubs(repo)
217 self.websubtable = webutil.getwebsubs(repo)
229 return repo
218 return repo
230
219
231 def refresh(self):
220 def _getrepo(self):
232 repostate = []
221 r, created = self._repo.fetch()
233 mtime = 0
222 if created:
234 # file of interrests mtime and size
223 r = self._webifyrepo(r)
235 for meth, fname in foi:
224
236 prefix = getattr(self.repo, meth)
225 self.mtime = self._repo.mtime
237 st = get_stat(prefix, fname)
226 return r
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
250
227
251 def run(self):
228 def run(self):
252 """Start a server from CGI environment.
229 """Start a server from CGI environment.
@@ -274,8 +251,8 b' class hgweb(object):'
274 This is typically only called by Mercurial. External consumers
251 This is typically only called by Mercurial. External consumers
275 should be using instances of this class as the WSGI application.
252 should be using instances of this class as the WSGI application.
276 """
253 """
277 self.refresh()
254 repo = self._getrepo()
278 rctx = requestcontext(self)
255 rctx = requestcontext(self, repo)
279
256
280 # This state is global across all threads.
257 # This state is global across all threads.
281 encoding.encoding = rctx.config('web', 'encoding', encoding.encoding)
258 encoding.encoding = rctx.config('web', 'encoding', encoding.encoding)
@@ -115,8 +115,6 b" Check hgweb's load order:"
115 3) bar extsetup
115 3) bar extsetup
116 4) foo reposetup
116 4) foo reposetup
117 4) bar reposetup
117 4) bar reposetup
118 4) foo reposetup
119 4) bar reposetup
120
118
121 $ echo 'foo = !' >> $HGRCPATH
119 $ echo 'foo = !' >> $HGRCPATH
122 $ echo 'bar = !' >> $HGRCPATH
120 $ echo 'bar = !' >> $HGRCPATH
@@ -64,7 +64,7 b' by the WSGI standard and strictly implem'
64 > print '---- OS.ENVIRON wsgi variables'
64 > print '---- OS.ENVIRON wsgi variables'
65 > print sorted([x for x in os.environ if x.startswith('wsgi')])
65 > print sorted([x for x in os.environ if x.startswith('wsgi')])
66 > print '---- request.ENVIRON wsgi variables'
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 > EOF
68 > EOF
69 $ python request.py
69 $ python request.py
70 ---- STATUS
70 ---- STATUS
General Comments 0
You need to be logged in to leave comments. Login now