sapling/hg
Jun Wu bf577875b3 chg: use a separate entry point
Summary:
Motivated by recent D7784903 which kills chg because it holds blackbox.log
file descriptor, and that patch is causing race conditions running with chg
(chg's sock atomic rename might fail if the directory is deleted).

There are other ways to solve the direct issue. This diff takes a more
aggressive but much cleaner approach. Basically, the `hg serve` framework is
too late for chg's usecase - the repo was already loaded, extension
side-effects have been already done at that time - chg has to use
workarounds to be compatible with that. Even with a best effort, it is still
possible to have weird interactions with shared repo because how hg loads
extensions.

The new approach is to pre-import a list of bundled extensions but do not
run their `uisetup`s.  This solves a couple of hard problems:

 - Compatibility - `uisetup` runs per request. That behaves exactly as what
   an extension author expects.
 - Less memory usage - there is no `repo` object is loaded in memory.
 - Reduced process count - since extension config change does not require a
   new chg server, the count of server processes would decrease (ex.
   `--config extensions.blackbox=!` won't require a new chg server)
 - Not holding fd to edenfs, since neither blackbox nor repo is loaded. This
   makes it possible to remove the hacky killing chg logic in D7784903.

The downside is performance, since extension loading, and `uisetup` will run
every time. Benchmark shows that's could be 50ms-ish. But we could move
forward by moving extension logic to core incrementally to get rid of that
cost too.

This is basically a simplified version of my previous stack starting
with [1]. The original commit message was:

  This is the beginning of a series that does a re-architect to chg mentioned
  in [1], to achieve better compatibility.

  The compatibility issues are mainly around "uisetup"s and "reposetup"s:

    - Developers are usually unaware that uisetup runs only once per chg
      process. We cannot reliably devel-warn them. The result is, potential
      broken code are written. For example, it's really hard for chg to deal
      with "experimental.evolution" changed from unset to manually set in
      config files because setconfig is used if that config option is not set.
    - An unnecessary "reposetup" caused by "hg serve" may have unwanted
      side effects. This can become troublesome if the repo requires things
      like remotefilelog or lz4revlog, and the user sets HGRCPATH to run
      tests.

  The current chg implementation assumes that "loading" an extension is not
  side effect free - if extension related config has changed, a restart is
  needed. The new idea is, "loading" = "importing" + "run ui/extsetup", the
  "importing" part can be side-effect free for some extensions. And benchmark
  shows "import" takes most of the time consumed, while "uisetup" is usually
  very fast. We can afford running "uisetup"s per request.

  To be able to (pre-)"import" extensions without running any "uisetup"s, a
  different entry point is needed. Otherwise as long as we go through the
  normal dispatch / runcommand ("hg serve") flow, "uisetup"s cannot be
  avoided.

  Aside from better compatibility, we can also remove some hacks:

    - chg client: no longer needs to extract sensitive argv
    - chg server: confighash can be changed to only hash environment variables
      (reduce the number of server processes)
    - chg server: srcui.walkconfig hack is no longer necessary

  This patch adds a new script "chgserve" as the new entry point. Currently,
  it is just a minimal implementation that makes "CHGHG=chgserve chg ..."
  work, without doing any pre-importing. The change could also be done in the
  "hg" script. But since chg is still experimental, let's keep "hg" untouched
  for now.

  [1]: www.mercurial-scm.org/pipermail/mercurial-devel/2016-July/085965.html

[1]: 6f91a1a69f

Reviewed By: singhsrb

Differential Revision: D7840237

fbshipit-source-id: e3d613b41fe4b721238b86c5bf84434d32cf0609
2018-05-08 17:02:22 -07:00

48 lines
1.4 KiB
Python
Executable File

#!/usr/bin/env python
#
# mercurial - scalable distributed SCM
#
# 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
import sys
if os.environ.get('HGUNICODEPEDANTRY', False):
try:
reload(sys)
sys.setdefaultencoding("undefined")
except NameError:
pass
libdir = '@LIBDIR@'
if libdir != '@' 'LIBDIR' '@':
if not os.path.isabs(libdir):
libdir = os.path.join(os.path.dirname(os.path.realpath(__file__)),
libdir)
libdir = os.path.abspath(libdir)
sys.path.insert(0, libdir)
if (sys.argv[1:5] == ['serve', '--cmdserver', 'chgunix', '--address']
and sys.argv[6:8] == ['--daemon-postexec', 'chdir:/']
and 'CHGINTERNALMARK' in os.environ):
# Shortcut path for chg server
from mercurial import dispatch
dispatch.runchgserver()
else:
# Non-chg path
try:
if sys.version_info[0] < 3 or sys.version_info >= (3, 6):
import hgdemandimport; hgdemandimport.enable()
except ImportError:
sys.stderr.write("abort: couldn't find mercurial libraries in [%s]\n" %
' '.join(sys.path))
sys.stderr.write("(check your install and PYTHONPATH)\n")
sys.exit(-1)
from mercurial import dispatch
dispatch.run()