Show More
@@ -0,0 +1,49 b'' | |||
|
1 | from __future__ import absolute_import | |
|
2 | ||
|
3 | import os | |
|
4 | from mercurial.hgweb import hgwebdir_mod | |
|
5 | ||
|
6 | hgwebdir = hgwebdir_mod.hgwebdir | |
|
7 | ||
|
8 | os.mkdir(b'webdir') | |
|
9 | os.chdir(b'webdir') | |
|
10 | ||
|
11 | webdir = os.path.realpath(b'.') | |
|
12 | ||
|
13 | ||
|
14 | def trivial_response(req, res): | |
|
15 | return [] | |
|
16 | ||
|
17 | ||
|
18 | def make_hgwebdir(gc_rate=None): | |
|
19 | config = os.path.join(webdir, b'hgwebdir.conf') | |
|
20 | with open(config, 'wb') as configfile: | |
|
21 | configfile.write(b'[experimental]\n') | |
|
22 | if gc_rate is not None: | |
|
23 | configfile.write(b'web.full-garbage-collection-rate=%d\n' % gc_rate) | |
|
24 | hg_wd = hgwebdir(config) | |
|
25 | hg_wd._runwsgi = trivial_response | |
|
26 | return hg_wd | |
|
27 | ||
|
28 | ||
|
29 | def process_requests(webdir_instance, number): | |
|
30 | # we don't care for now about passing realistic arguments | |
|
31 | for _ in range(number): | |
|
32 | for chunk in webdir_instance.run_wsgi(None, None): | |
|
33 | pass | |
|
34 | ||
|
35 | ||
|
36 | without_gc = make_hgwebdir(gc_rate=0) | |
|
37 | process_requests(without_gc, 5) | |
|
38 | assert without_gc.requests_count == 5 | |
|
39 | assert without_gc.gc_full_collections_done == 0 | |
|
40 | ||
|
41 | with_gc = make_hgwebdir(gc_rate=2) | |
|
42 | process_requests(with_gc, 5) | |
|
43 | assert with_gc.requests_count == 5 | |
|
44 | assert with_gc.gc_full_collections_done == 2 | |
|
45 | ||
|
46 | with_systematic_gc = make_hgwebdir() # default value of the setting | |
|
47 | process_requests(with_systematic_gc, 3) | |
|
48 | assert with_systematic_gc.requests_count == 3 | |
|
49 | assert with_systematic_gc.gc_full_collections_done == 3 |
@@ -1266,6 +1266,11 b' coreconfigitem(' | |||
|
1266 | 1266 | ) |
|
1267 | 1267 | coreconfigitem( |
|
1268 | 1268 | b'experimental', |
|
1269 | b'web.full-garbage-collection-rate', | |
|
1270 | default=1, # still forcing a full collection on each request | |
|
1271 | ) | |
|
1272 | coreconfigitem( | |
|
1273 | b'experimental', | |
|
1269 | 1274 | b'worker.wdir-get-thread-safe', |
|
1270 | 1275 | default=False, |
|
1271 | 1276 | ) |
@@ -285,6 +285,7 b' class hgwebdir(object):' | |||
|
285 | 285 | self.lastrefresh = 0 |
|
286 | 286 | self.motd = None |
|
287 | 287 | self.refresh() |
|
288 | self.requests_count = 0 | |
|
288 | 289 | if not baseui: |
|
289 | 290 | # set up environment for new ui |
|
290 | 291 | extensions.loadall(self.ui) |
@@ -341,6 +342,10 b' class hgwebdir(object):' | |||
|
341 | 342 | |
|
342 | 343 | self.repos = repos |
|
343 | 344 | self.ui = u |
|
345 | self.gc_full_collect_rate = self.ui.configint( | |
|
346 | b'experimental', b'web.full-garbage-collection-rate' | |
|
347 | ) | |
|
348 | self.gc_full_collections_done = 0 | |
|
344 | 349 | encoding.encoding = self.ui.config(b'web', b'encoding') |
|
345 | 350 | self.style = self.ui.config(b'web', b'style') |
|
346 | 351 | self.templatepath = self.ui.config( |
@@ -383,12 +388,27 b' class hgwebdir(object):' | |||
|
383 | 388 | finally: |
|
384 | 389 | # There are known cycles in localrepository that prevent |
|
385 | 390 | # those objects (and tons of held references) from being |
|
386 |
# collected through normal refcounting. |
|
|
387 | # leaks by performing an explicit GC on every request. | |
|
388 | # TODO remove this once leaks are fixed. | |
|
389 | # TODO only run this on requests that create localrepository | |
|
390 | # instances instead of every request. | |
|
391 | gc.collect() | |
|
391 | # collected through normal refcounting. | |
|
392 | # In some cases, the resulting memory consumption can | |
|
393 | # be tamed by performing explicit garbage collections. | |
|
394 | # In presence of actual leaks or big long-lived caches, the | |
|
395 | # impact on performance of such collections can become a | |
|
396 | # problem, hence the rate shouldn't be set too low. | |
|
397 | # See "Collecting the oldest generation" in | |
|
398 | # https://devguide.python.org/garbage_collector | |
|
399 | # for more about such trade-offs. | |
|
400 | rate = self.gc_full_collect_rate | |
|
401 | ||
|
402 | # this is not thread safe, but the consequence (skipping | |
|
403 | # a garbage collection) is arguably better than risking | |
|
404 | # to have several threads perform a collection in parallel | |
|
405 | # (long useless wait on all threads). | |
|
406 | self.requests_count += 1 | |
|
407 | if rate > 0 and self.requests_count % rate == 0: | |
|
408 | gc.collect() | |
|
409 | self.gc_full_collections_done += 1 | |
|
410 | else: | |
|
411 | gc.collect(generation=1) | |
|
392 | 412 | |
|
393 | 413 | def _runwsgi(self, req, res): |
|
394 | 414 | try: |
General Comments 0
You need to be logged in to leave comments.
Login now