##// END OF EJS Templates
deltas: set estimated compression upper bound to "3x" instead of "10x"...
deltas: set estimated compression upper bound to "3x" instead of "10x" In pratice, we very rarely observer compression better than "3x" on manifest deltas. Having a more aggressive estimate significantly helps our pathological use case on a private repository. Here are a comparison of timings using different upper bound. Estimated compression | ø | ×10 | ×5 | ×3 | timing | 14.11 | 2.61 | 1.96 | 1.53 | We also tested the impact of this series on an array of public repositories. This shown no impact in either size nor timing. Full data set below for those interested. Size ---- Regarding size, not significant impact have been noticed on neither public nor private repositories. Here are the number we gathered on public repositories: zlib/upperbound | no | 10x | 5x | 3x mercurial | 5 875 730 | 5 875 730 | 5 875 730 | 5 875 730 pypy | 27 782 913 | 27 782 913 | 27 782 913 | 27 782 913 netbeans | 159 161 207 | 159 161 207 | 159 161 207 | 159 959 879 (+0.5%) mozilla-central | 323 841 642 | 323 841 642 | 323 841 642 | 319 867 519 (-2.5%) mozilla-try | 746 649 123 | 746 649 123 | 746 649 123 | 741 155 568 (-0.7%) private-repo | 1 485 287 294 | 1 485 287 294 | 1 485 287 294 | 1 409 248 382 (-5.1%) zstd/upperbound | no | 10x | 5x | 3x mercurial | 5 895 206 | 5 895 206 | 5 895 206 | 5 895 206 pypy | 28 689 230 | 28 689 230 | 28 689 230 | 28 689 230 netbeans | 157 636 387 | 157 636 387 | 157 636 387 | 159 692 678 (+1.3%) mozilla-central | 317 650 281 | 317 650 281 | 317 650 281 | 319 613 603 (+0.6%) mozilla-try | 737 555 275 | 737 555 275 | 737 555 275 | 738 079 473 (+0.1%) private-repo | 1 352 362 982 | 1 352 362 982 | 1 346 961 880 | 1 361 327 384 (+0.7%) Speed ------ Timing gathered using `hg perfrevlogwrite -m`. Value are in seconds. mercurial zlib | no | 10x | 5x | 3x | total | 65.551783 | 65.388887 | 65.260658 | 65.321199 | max | 0.034544 | 0.034571 | 0.034659 | 0.034521 | 99.99% | 0.034544 | 0.034571 | 0.034659 | 0.034521 | zstd | no | 10x | 5x | 3x | total | 49.118449 | 49.054062 | 48.753588 | 48.740230 | max | 0.009338 | 0.009239 | 0.009202 | 0.009178 | 99.99% | 0.007618 | 0.007639 | 0.007626 | 0.007621 | pypy zlib | no | 10x | 5x | 3x | total | 560.865984 | 558.983817 | 559.083815 | 559.349152 | max | 0.219614 | 0.215922 | 0.218112 | 0.218107 | 99.99% | 0.219614 | 0.215922 | 0.218112 | 0.218107 | zstd | no | 10x | 5x | 3x | total | 349.393280 | 347.395819 | 347.185407 | 345.643985 | max | 0.084143 | 0.083536 | 0.081834 | 0.082178 | 99.99% | 0.039445 | 0.039639 | 0.039612 | 0.039175 | netbeans zlib | no | 10x | 5x | 3x | total | 33103.327727 | 33314.932260 | 33211.745233 | 33345.891778 | max | 2.666852 | 2.672059 | 2.662453 | 2.662936 | 99.99% | 2.058772 | 2.070429 | 2.069569 | 2.064653 | zstd | no | 10x | 5x | 3x | total | 20112.102708 | 20095.879719 | 20083.390300 | 20123.221859 | max | 2.063482 | 2.062851 | 2.065229 | 2.060147 | 99.99% | 1.146647 | 1.143794 | 1.142933 | 1.146529 | mozilla zlib | no | 10x | 5x | 3x | total | 41374.102138 | 41418.816773 | 41381.956370 | 41334.280732 | max | 3.383474 | 3.387400 | 3.405711 | 3.387316 | 99.99% | 1.006755 | 1.005954 | 1.007700 | 1.007373 | zstd | no | 10x | 5x | 3x | total | 24689.691520 | 24643.939662 | 24664.630027 | 24664.512714 | max | 1.460822 | 1.449640 | 1.439747 | 1.465304 | 99.99% | 0.527111 | 0.527377 | 0.527807 | 0.527226 |

File last commit:

r40859:82210d88 default
r42669:4a3abb33 default
Show More
server.py
212 lines | 6.9 KiB | text/x-python | PythonLexer
# server.py - utility and factory of server
#
# Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
from __future__ import absolute_import
import os
from .i18n import _
from . import (
chgserver,
cmdutil,
commandserver,
error,
hgweb,
pycompat,
util,
)
from .utils import (
procutil,
)
def runservice(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
runargs=None, appendpid=False):
'''Run a command as a service.'''
postexecargs = {}
if opts['daemon_postexec']:
for inst in opts['daemon_postexec']:
if inst.startswith('unlink:'):
postexecargs['unlink'] = inst[7:]
elif inst.startswith('chdir:'):
postexecargs['chdir'] = inst[6:]
elif inst != 'none':
raise error.Abort(_('invalid value for --daemon-postexec: %s')
% inst)
# When daemonized on Windows, redirect stdout/stderr to the lockfile (which
# gets cleaned up after the child is up and running), so that the parent can
# read and print the error if this child dies early. See 594dd384803c. On
# other platforms, the child can write to the parent's stdio directly, until
# it is redirected prior to runfn().
if pycompat.iswindows and opts['daemon_postexec']:
if 'unlink' in postexecargs and os.path.exists(postexecargs['unlink']):
procutil.stdout.flush()
procutil.stderr.flush()
fd = os.open(postexecargs['unlink'],
os.O_WRONLY | os.O_APPEND | os.O_BINARY)
try:
os.dup2(fd, procutil.stdout.fileno())
os.dup2(fd, procutil.stderr.fileno())
finally:
os.close(fd)
def writepid(pid):
if opts['pid_file']:
if appendpid:
mode = 'ab'
else:
mode = 'wb'
fp = open(opts['pid_file'], mode)
fp.write('%d\n' % pid)
fp.close()
if opts['daemon'] and not opts['daemon_postexec']:
# Signal child process startup with file removal
lockfd, lockpath = pycompat.mkstemp(prefix='hg-service-')
os.close(lockfd)
try:
if not runargs:
runargs = procutil.hgcmd() + pycompat.sysargv[1:]
runargs.append('--daemon-postexec=unlink:%s' % lockpath)
# Don't pass --cwd to the child process, because we've already
# changed directory.
for i in pycompat.xrange(1, len(runargs)):
if runargs[i].startswith('--cwd='):
del runargs[i]
break
elif runargs[i].startswith('--cwd'):
del runargs[i:i + 2]
break
def condfn():
return not os.path.exists(lockpath)
pid = procutil.rundetached(runargs, condfn)
if pid < 0:
# If the daemonized process managed to write out an error msg,
# report it.
if pycompat.iswindows and os.path.exists(lockpath):
with open(lockpath, 'rb') as log:
for line in log:
procutil.stderr.write(line)
raise error.Abort(_('child process failed to start'))
writepid(pid)
finally:
util.tryunlink(lockpath)
if parentfn:
return parentfn(pid)
else:
return
if initfn:
initfn()
if not opts['daemon']:
writepid(procutil.getpid())
if opts['daemon_postexec']:
try:
os.setsid()
except AttributeError:
pass
if 'chdir' in postexecargs:
os.chdir(postexecargs['chdir'])
procutil.hidewindow()
procutil.stdout.flush()
procutil.stderr.flush()
nullfd = os.open(os.devnull, os.O_RDWR)
logfilefd = nullfd
if logfile:
logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND,
0o666)
os.dup2(nullfd, procutil.stdin.fileno())
os.dup2(logfilefd, procutil.stdout.fileno())
os.dup2(logfilefd, procutil.stderr.fileno())
stdio = (procutil.stdin.fileno(), procutil.stdout.fileno(),
procutil.stderr.fileno())
if nullfd not in stdio:
os.close(nullfd)
if logfile and logfilefd not in stdio:
os.close(logfilefd)
# Only unlink after redirecting stdout/stderr, so Windows doesn't
# complain about a sharing violation.
if 'unlink' in postexecargs:
os.unlink(postexecargs['unlink'])
if runfn:
return runfn()
_cmdservicemap = {
'chgunix': chgserver.chgunixservice,
'pipe': commandserver.pipeservice,
'unix': commandserver.unixforkingservice,
}
def _createcmdservice(ui, repo, opts):
mode = opts['cmdserver']
try:
servicefn = _cmdservicemap[mode]
except KeyError:
raise error.Abort(_('unknown mode %s') % mode)
commandserver.setuplogging(ui, repo)
return servicefn(ui, repo, opts)
def _createhgwebservice(ui, repo, opts):
# this way we can check if something was given in the command-line
if opts.get('port'):
opts['port'] = util.getport(opts.get('port'))
alluis = {ui}
if repo:
baseui = repo.baseui
alluis.update([repo.baseui, repo.ui])
else:
baseui = ui
webconf = opts.get('web_conf') or opts.get('webdir_conf')
if webconf:
if opts.get('subrepos'):
raise error.Abort(_('--web-conf cannot be used with --subrepos'))
# load server settings (e.g. web.port) to "copied" ui, which allows
# hgwebdir to reload webconf cleanly
servui = ui.copy()
servui.readconfig(webconf, sections=['web'])
alluis.add(servui)
elif opts.get('subrepos'):
servui = ui
# If repo is None, hgweb.createapp() already raises a proper abort
# message as long as webconf is None.
if repo:
webconf = dict()
cmdutil.addwebdirpath(repo, "", webconf)
else:
servui = ui
optlist = ("name templates style address port prefix ipv6"
" accesslog errorlog certificate encoding")
for o in optlist.split():
val = opts.get(o, '')
if val in (None, ''): # should check against default options instead
continue
for u in alluis:
u.setconfig("web", o, val, 'serve')
app = hgweb.createapp(baseui, repo, webconf)
return hgweb.httpservice(servui, app, opts)
def createservice(ui, repo, opts):
if opts["cmdserver"]:
return _createcmdservice(ui, repo, opts)
else:
return _createhgwebservice(ui, repo, opts)