##// END OF EJS Templates
streamclone: disable the volatile file open handle optimization on Windows...
streamclone: disable the volatile file open handle optimization on Windows Leaving files open caused new failures like this, since a47f09da8bd1: diff --git a/tests/test-persistent-nodemap-stream-clone.t b/tests/test-persistent-nodemap-stream-clone.t --- a/tests/test-persistent-nodemap-stream-clone.t +++ b/tests/test-persistent-nodemap-stream-clone.t @@ -115,7 +115,12 @@ Do a mix of clone and commit at the same $ (hg clone -U --stream ssh://user@dummy/test-repo stream-clone-race-1 --debug 2>> clone-output | grep -E '00(changelog|manifest)' >> clone-output; touch $HG_TEST_STREAM_WALKED_FILE_3) & $ $RUNTESTDIR/testlib/wait-on-file 10 $HG_TEST_STREAM_WALKED_FILE_1 $ hg -R test-repo/ commit -m foo - created new head + transaction abort! + failed to recover 00changelog.n ([WinError 32] The process cannot access the file because it is being used by another process: b'$STR_REPR_TESTTMP\\test-repo/.hg/store/00changelog.n' -> b'$STR_REPR_TESTTMP\\test-repo/.hg/store/00changelog.n-f418dcd6') + rollback failed - please run hg recover + (failure reason: [WinError 32] The process cannot access the file because it is being used by another process: b'$STR_REPR_TESTTMP\\test-repo/.hg/store/00changelog.n' -> b'$STR_REPR_TESTTMP\\test-repo/.hg/store/00changelog.n-f418dcd6') + abort: The process cannot access the file because it is being used by another process: '$TESTTMP\test-repo\.hg\store\00changelog.n' + [255] $ touch $HG_TEST_STREAM_WALKED_FILE_2 $ $RUNTESTDIR/testlib/wait-on-file 10 $HG_TEST_STREAM_WALKED_FILE_3 $ cat clone-output Since the `VolatileManager` falls back to the old copy method when the open file threshold is exceeded, this just drops the threshold so that only 1 file is open. The actual value used (2) is unexpected, and explained inline. I'd like to have a config option for this so that we can test both ways (in theory, it could resort to copies on non-Windows systems too), but I don't see a `uimod.ui` handy. Alternately, I tried replacing the 3 `open()` calls in the `VolatileManager` with `util.posixfile()`, but that simply hung the test on Windows for some reason, I think on the same line that's indicated as failing above. (There was a `grep` command hanging around, as well as `hg -R test-repo serve --stdio`.)

File last commit:

r50404:946c0232 default
r53081:e4b242f9 stable
Show More
pull_logger.py
141 lines | 4.2 KiB | text/x-python | PythonLexer
# pull_logger.py - Logs pulls to a JSON-line file in the repo's VFS.
#
# Copyright 2022 Pacien TRAN-GIRARD <pacien.trangirard@pacien.net>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
'''logs pull parameters to a file
This extension logs the pull parameters, i.e. the remote and common heads,
when pulling from the local repository.
The collected data should give an idea of the state of a pair of repositories
and allow replaying past synchronisations between them. This is particularly
useful for working on data exchange, bundling and caching-related
optimisations.
The record is a JSON-line file located in the repository's VFS at
.hg/pull_log.jsonl.
Log write failures are not considered fatal: log writes may be skipped for any
reason such as insufficient storage or a timeout.
Some basic log file rotation can be enabled by setting 'rotate-size' to a value
greater than 0. This causes the current log file to be moved to
.hg/pull_log.jsonl.rotated when this threshold is met, discarding any
previously rotated log file.
The timeouts of the exclusive lock used when writing to the lock file can be
configured through the 'timeout.lock' and 'timeout.warn' options of this
plugin. Those are not expected to be held for a significant time in practice.::
[pull-logger]
timeout.lock = 300
timeout.warn = 100
rotate-size = 1kb
'''
import json
import time
from mercurial.i18n import _
from mercurial.utils import stringutil
from mercurial import (
error,
extensions,
lock,
registrar,
wireprotov1server,
)
EXT_NAME = b'pull-logger'
EXT_VERSION_CODE = 0
LOG_FILE = b'pull_log.jsonl'
OLD_LOG_FILE = LOG_FILE + b'.rotated'
LOCK_NAME = LOG_FILE + b'.lock'
configtable = {}
configitem = registrar.configitem(configtable)
configitem(EXT_NAME, b'timeout.lock', default=600)
configitem(EXT_NAME, b'timeout.warn', default=120)
configitem(EXT_NAME, b'rotate-size', default=b'100MB')
def wrap_getbundle(orig, repo, proto, others, *args, **kwargs):
heads, common = extract_pull_heads(others)
log_entry = {
'timestamp': time.time(),
'logger_version': EXT_VERSION_CODE,
'heads': sorted(heads),
'common': sorted(common),
}
try:
write_to_log(repo, log_entry)
except (IOError, error.LockError) as err:
msg = stringutil.forcebytestr(err)
repo.ui.warn(_(b'unable to append to pull log: %s\n') % msg)
return orig(repo, proto, others, *args, **kwargs)
def extract_pull_heads(bundle_args):
opts = wireprotov1server.options(
b'getbundle',
wireprotov1server.wireprototypes.GETBUNDLE_ARGUMENTS.keys(),
bundle_args.copy(), # this call consumes the args destructively
)
heads = opts.get(b'heads', b'').decode('utf-8').split(' ')
common = opts.get(b'common', b'').decode('utf-8').split(' ')
return (heads, common)
def write_to_log(repo, entry):
locktimeout = repo.ui.configint(EXT_NAME, b'timeout.lock')
lockwarntimeout = repo.ui.configint(EXT_NAME, b'timeout.warn')
rotatesize = repo.ui.configbytes(EXT_NAME, b'rotate-size')
with lock.trylock(
ui=repo.ui,
vfs=repo.vfs,
lockname=LOCK_NAME,
timeout=locktimeout,
warntimeout=lockwarntimeout,
):
if rotatesize > 0 and repo.vfs.exists(LOG_FILE):
if repo.vfs.stat(LOG_FILE).st_size >= rotatesize:
repo.vfs.rename(LOG_FILE, OLD_LOG_FILE)
with repo.vfs.open(LOG_FILE, b'a+') as logfile:
serialised = json.dumps(entry, sort_keys=True)
logfile.write(serialised.encode('utf-8'))
logfile.write(b'\n')
logfile.flush()
def reposetup(ui, repo):
if repo.local():
repo._wlockfreeprefix.add(LOG_FILE)
repo._wlockfreeprefix.add(OLD_LOG_FILE)
def uisetup(ui):
del wireprotov1server.commands[b'getbundle']
decorator = wireprotov1server.wireprotocommand(
name=b'getbundle',
args=b'*',
permission=b'pull',
)
extensions.wrapfunction(
container=wireprotov1server,
funcname='getbundle',
wrapper=wrap_getbundle,
)
decorator(wireprotov1server.getbundle)