view mercurial/server.py @ 30758:76104a4899ad

commands: config option to control bundle compression level Currently, bundle compression uses the default compression level for the active compression engine. The default compression level is tuned as a compromise between speed and size. Some scenarios may call for a different compression level. For example, with clone bundles, bundles are generated once and used several times. Since the cost to generate is paid infrequently, server operators may wish to trade extra CPU time for better compression ratios. This patch introduces an experimental and undocumented config option to control the bundle compression level. As the inline comment says, this approach is a bit hacky. I'd prefer for the compression level to be encoded in the bundle spec. e.g. "zstd-v2;complevel=15." However, given that the 4.1 freeze is imminent, I'm not comfortable implementing this user-facing change without much time to test and consider the implications. So, we're going with the quick and dirty solution for now. Having this option in the 4.1 release will enable Mozilla to easily produce and test zlib and zstd bundles with non-default compression levels in production. This will help drive future development of the feature and zstd integration with Mercurial.
author Gregory Szorc <gregory.szorc@gmail.com>
date Tue, 10 Jan 2017 11:20:32 -0800
parents ff7df4bb75de
children ce4ddcda868b
line wrap: on
line source

# 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 errno
import os
import sys
import tempfile

from .i18n import _

from . import (
    chgserver,
    commandserver,
    error,
    hgweb,
    util,
)

def runservice(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
               runargs=None, appendpid=False):
    '''Run a command as a service.'''

    def writepid(pid):
        if opts['pid_file']:
            if appendpid:
                mode = 'a'
            else:
                mode = 'w'
            fp = open(opts['pid_file'], mode)
            fp.write(str(pid) + '\n')
            fp.close()

    if opts['daemon'] and not opts['daemon_postexec']:
        # Signal child process startup with file removal
        lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
        os.close(lockfd)
        try:
            if not runargs:
                runargs = util.hgcmd() + sys.argv[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 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 = util.rundetached(runargs, condfn)
            if pid < 0:
                raise error.Abort(_('child process failed to start'))
            writepid(pid)
        finally:
            try:
                os.unlink(lockpath)
            except OSError as e:
                if e.errno != errno.ENOENT:
                    raise
        if parentfn:
            return parentfn(pid)
        else:
            return

    if initfn:
        initfn()

    if not opts['daemon']:
        writepid(util.getpid())

    if opts['daemon_postexec']:
        try:
            os.setsid()
        except AttributeError:
            pass
        for inst in opts['daemon_postexec']:
            if inst.startswith('unlink:'):
                lockpath = inst[7:]
                os.unlink(lockpath)
            elif inst.startswith('chdir:'):
                os.chdir(inst[6:])
            elif inst != 'none':
                raise error.Abort(_('invalid value for --daemon-postexec: %s')
                                  % inst)
        util.hidewindow()
        util.stdout.flush()
        util.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)
        os.dup2(nullfd, 0)
        os.dup2(logfilefd, 1)
        os.dup2(logfilefd, 2)
        if nullfd not in (0, 1, 2):
            os.close(nullfd)
        if logfile and logfilefd not in (0, 1, 2):
            os.close(logfilefd)

    if runfn:
        return runfn()

_cmdservicemap = {
    'chgunix': chgserver.chgunixservice,
    'pipe': commandserver.pipeservice,
    'unix': commandserver.unixforkingservice,
}

def _createcmdservice(ui, repo, opts):
    mode = opts['cmdserver']
    try:
        return _cmdservicemap[mode](ui, repo, opts)
    except KeyError:
        raise error.Abort(_('unknown mode %s') % mode)

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 = set([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:
        # 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)
    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)