mirror of
https://gerrit.googlesource.com/git-repo
synced 2026-05-07 11:29:27 +00:00
manifest: Introduce sync-j-max attribute to cap sync jobs
Add a way for manifest owners to limit how many sync jobs run in parallel. Bug: 481100878 Change-Id: Ia6cbe02cbc83c9e414b53b8d14fe5e7e1b802505 Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/548963 Reviewed-by: Mike Frysinger <vapier@google.com> Tested-by: Gavin Mak <gavinmak@google.com> Commit-Queue: Gavin Mak <gavinmak@google.com>
This commit is contained in:
@@ -51,6 +51,7 @@ following DTD:
|
|||||||
<!ATTLIST default dest-branch CDATA #IMPLIED>
|
<!ATTLIST default dest-branch CDATA #IMPLIED>
|
||||||
<!ATTLIST default upstream CDATA #IMPLIED>
|
<!ATTLIST default upstream CDATA #IMPLIED>
|
||||||
<!ATTLIST default sync-j CDATA #IMPLIED>
|
<!ATTLIST default sync-j CDATA #IMPLIED>
|
||||||
|
<!ATTLIST default sync-j-max CDATA #IMPLIED>
|
||||||
<!ATTLIST default sync-c CDATA #IMPLIED>
|
<!ATTLIST default sync-c CDATA #IMPLIED>
|
||||||
<!ATTLIST default sync-s CDATA #IMPLIED>
|
<!ATTLIST default sync-s CDATA #IMPLIED>
|
||||||
<!ATTLIST default sync-tags CDATA #IMPLIED>
|
<!ATTLIST default sync-tags CDATA #IMPLIED>
|
||||||
@@ -213,7 +214,9 @@ can be found. Used when syncing a revision locked manifest in
|
|||||||
-c mode to avoid having to sync the entire ref space. Project elements
|
-c mode to avoid having to sync the entire ref space. Project elements
|
||||||
not setting their own `upstream` will inherit this value.
|
not setting their own `upstream` will inherit this value.
|
||||||
|
|
||||||
Attribute `sync-j`: Number of parallel jobs to use when synching.
|
Attribute `sync-j`: Number of parallel jobs to use when syncing.
|
||||||
|
|
||||||
|
Attribute `sync-j-max`: Maximum number of parallel jobs to use when syncing.
|
||||||
|
|
||||||
Attribute `sync-c`: Set to true to only sync the given Git
|
Attribute `sync-c`: Set to true to only sync the given Git
|
||||||
branch (specified in the `revision` attribute) rather than the
|
branch (specified in the `revision` attribute) rather than the
|
||||||
|
|||||||
+5
-2
@@ -1,5 +1,5 @@
|
|||||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
|
.\" DO NOT MODIFY THIS FILE! It was generated by help2man.
|
||||||
.TH REPO "1" "December 2025" "repo manifest" "Repo Manual"
|
.TH REPO "1" "February 2026" "repo manifest" "Repo Manual"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
repo \- repo manifest - manual page for repo manifest
|
repo \- repo manifest - manual page for repo manifest
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
@@ -131,6 +131,7 @@ include*)>
|
|||||||
<!ATTLIST default dest\-branch CDATA #IMPLIED>
|
<!ATTLIST default dest\-branch CDATA #IMPLIED>
|
||||||
<!ATTLIST default upstream CDATA #IMPLIED>
|
<!ATTLIST default upstream CDATA #IMPLIED>
|
||||||
<!ATTLIST default sync\-j CDATA #IMPLIED>
|
<!ATTLIST default sync\-j CDATA #IMPLIED>
|
||||||
|
<!ATTLIST default sync\-j\-max CDATA #IMPLIED>
|
||||||
<!ATTLIST default sync\-c CDATA #IMPLIED>
|
<!ATTLIST default sync\-c CDATA #IMPLIED>
|
||||||
<!ATTLIST default sync\-s CDATA #IMPLIED>
|
<!ATTLIST default sync\-s CDATA #IMPLIED>
|
||||||
<!ATTLIST default sync\-tags CDATA #IMPLIED>
|
<!ATTLIST default sync\-tags CDATA #IMPLIED>
|
||||||
@@ -309,7 +310,9 @@ when syncing a revision locked manifest in \fB\-c\fR mode to avoid having to syn
|
|||||||
entire ref space. Project elements not setting their own `upstream` will inherit
|
entire ref space. Project elements not setting their own `upstream` will inherit
|
||||||
this value.
|
this value.
|
||||||
.PP
|
.PP
|
||||||
Attribute `sync\-j`: Number of parallel jobs to use when synching.
|
Attribute `sync\-j`: Number of parallel jobs to use when syncing.
|
||||||
|
.PP
|
||||||
|
Attribute `sync\-j\-max`: Maximum number of parallel jobs to use when syncing.
|
||||||
.PP
|
.PP
|
||||||
Attribute `sync\-c`: Set to true to only sync the given Git branch (specified in
|
Attribute `sync\-c`: Set to true to only sync the given Git branch (specified in
|
||||||
the `revision` attribute) rather than the whole ref space. Project elements
|
the `revision` attribute) rather than the whole ref space. Project elements
|
||||||
|
|||||||
@@ -155,6 +155,7 @@ class _Default:
|
|||||||
upstreamExpr = None
|
upstreamExpr = None
|
||||||
remote = None
|
remote = None
|
||||||
sync_j = None
|
sync_j = None
|
||||||
|
sync_j_max = None
|
||||||
sync_c = False
|
sync_c = False
|
||||||
sync_s = False
|
sync_s = False
|
||||||
sync_tags = True
|
sync_tags = True
|
||||||
@@ -631,6 +632,9 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md
|
|||||||
if d.sync_j is not None:
|
if d.sync_j is not None:
|
||||||
have_default = True
|
have_default = True
|
||||||
e.setAttribute("sync-j", "%d" % d.sync_j)
|
e.setAttribute("sync-j", "%d" % d.sync_j)
|
||||||
|
if d.sync_j_max is not None:
|
||||||
|
have_default = True
|
||||||
|
e.setAttribute("sync-j-max", "%d" % d.sync_j_max)
|
||||||
if d.sync_c:
|
if d.sync_c:
|
||||||
have_default = True
|
have_default = True
|
||||||
e.setAttribute("sync-c", "true")
|
e.setAttribute("sync-c", "true")
|
||||||
@@ -1763,6 +1767,13 @@ https://gerrit.googlesource.com/git-repo/+/HEAD/docs/manifest-format.md
|
|||||||
% (self.manifestFile, d.sync_j)
|
% (self.manifestFile, d.sync_j)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
d.sync_j_max = XmlInt(node, "sync-j-max", None)
|
||||||
|
if d.sync_j_max is not None and d.sync_j_max <= 0:
|
||||||
|
raise ManifestParseError(
|
||||||
|
'%s: sync-j-max must be greater than 0, not "%s"'
|
||||||
|
% (self.manifestFile, d.sync_j_max)
|
||||||
|
)
|
||||||
|
|
||||||
d.sync_c = XmlBool(node, "sync-c", False)
|
d.sync_c = XmlBool(node, "sync-c", False)
|
||||||
d.sync_s = XmlBool(node, "sync-s", False)
|
d.sync_s = XmlBool(node, "sync-s", False)
|
||||||
d.sync_tags = XmlBool(node, "sync-tags", True)
|
d.sync_tags = XmlBool(node, "sync-tags", True)
|
||||||
|
|||||||
+26
-8
@@ -1940,15 +1940,33 @@ later is required to fix a server side protocol bug.
|
|||||||
opt.jobs_network = min(opt.jobs_network, jobs_soft_limit)
|
opt.jobs_network = min(opt.jobs_network, jobs_soft_limit)
|
||||||
opt.jobs_checkout = min(opt.jobs_checkout, jobs_soft_limit)
|
opt.jobs_checkout = min(opt.jobs_checkout, jobs_soft_limit)
|
||||||
|
|
||||||
# Warn once if effective job counts seem excessively high.
|
sync_j_max = mp.manifest.default.sync_j_max or None
|
||||||
|
|
||||||
|
# Check for shared options.
|
||||||
# Prioritize --jobs, then --jobs-network, then --jobs-checkout.
|
# Prioritize --jobs, then --jobs-network, then --jobs-checkout.
|
||||||
job_options_to_check = (
|
job_attributes = (
|
||||||
("--jobs", opt.jobs),
|
("--jobs", "jobs"),
|
||||||
("--jobs-network", opt.jobs_network),
|
("--jobs-network", "jobs_network"),
|
||||||
("--jobs-checkout", opt.jobs_checkout),
|
("--jobs-checkout", "jobs_checkout"),
|
||||||
)
|
)
|
||||||
for name, value in job_options_to_check:
|
|
||||||
if value > self._JOBS_WARN_THRESHOLD:
|
warned = False
|
||||||
|
limit_warned = False
|
||||||
|
for name, attr in job_attributes:
|
||||||
|
value = getattr(opt, attr)
|
||||||
|
|
||||||
|
if sync_j_max and value > sync_j_max:
|
||||||
|
if not limit_warned:
|
||||||
|
logger.warning(
|
||||||
|
"warning: manifest limits %s to %d",
|
||||||
|
name,
|
||||||
|
sync_j_max,
|
||||||
|
)
|
||||||
|
limit_warned = True
|
||||||
|
setattr(opt, attr, sync_j_max)
|
||||||
|
value = sync_j_max
|
||||||
|
|
||||||
|
if not warned and value > self._JOBS_WARN_THRESHOLD:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
"High job count (%d > %d) specified for %s; this may "
|
"High job count (%d > %d) specified for %s; this may "
|
||||||
"lead to excessive resource usage or diminishing returns.",
|
"lead to excessive resource usage or diminishing returns.",
|
||||||
@@ -1956,7 +1974,7 @@ later is required to fix a server side protocol bug.
|
|||||||
self._JOBS_WARN_THRESHOLD,
|
self._JOBS_WARN_THRESHOLD,
|
||||||
name,
|
name,
|
||||||
)
|
)
|
||||||
break
|
warned = True
|
||||||
|
|
||||||
def Execute(self, opt, args):
|
def Execute(self, opt, args):
|
||||||
errors = []
|
errors = []
|
||||||
|
|||||||
@@ -401,6 +401,32 @@ class XmlManifestTests(ManifestParseTestCase):
|
|||||||
self.assertEqual(len(manifest.projects), 1)
|
self.assertEqual(len(manifest.projects), 1)
|
||||||
self.assertEqual(manifest.projects[0].name, "test-project")
|
self.assertEqual(manifest.projects[0].name, "test-project")
|
||||||
|
|
||||||
|
def test_sync_j_max(self):
|
||||||
|
"""Check sync-j-max handling."""
|
||||||
|
# Check valid value.
|
||||||
|
manifest = self.getXmlManifest(
|
||||||
|
'<manifest><default sync-j-max="5" /></manifest>'
|
||||||
|
)
|
||||||
|
self.assertEqual(manifest.default.sync_j_max, 5)
|
||||||
|
self.assertEqual(
|
||||||
|
manifest.ToXml().toxml(),
|
||||||
|
'<?xml version="1.0" ?>'
|
||||||
|
'<manifest><default sync-j-max="5"/></manifest>',
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check invalid values.
|
||||||
|
with self.assertRaises(error.ManifestParseError):
|
||||||
|
manifest = self.getXmlManifest(
|
||||||
|
'<manifest><default sync-j-max="0" /></manifest>'
|
||||||
|
)
|
||||||
|
manifest.ToXml()
|
||||||
|
|
||||||
|
with self.assertRaises(error.ManifestParseError):
|
||||||
|
manifest = self.getXmlManifest(
|
||||||
|
'<manifest><default sync-j-max="-1" /></manifest>'
|
||||||
|
)
|
||||||
|
manifest.ToXml()
|
||||||
|
|
||||||
|
|
||||||
class IncludeElementTests(ManifestParseTestCase):
|
class IncludeElementTests(ManifestParseTestCase):
|
||||||
"""Tests for <include>."""
|
"""Tests for <include>."""
|
||||||
|
|||||||
@@ -97,6 +97,35 @@ def test_cli_jobs(argv, jobs_manifest, jobs, jobs_net, jobs_check):
|
|||||||
"""Tests --jobs option behavior."""
|
"""Tests --jobs option behavior."""
|
||||||
mp = mock.MagicMock()
|
mp = mock.MagicMock()
|
||||||
mp.manifest.default.sync_j = jobs_manifest
|
mp.manifest.default.sync_j = jobs_manifest
|
||||||
|
mp.manifest.default.sync_j_max = None
|
||||||
|
|
||||||
|
cmd = sync.Sync()
|
||||||
|
opts, args = cmd.OptionParser.parse_args(argv)
|
||||||
|
cmd.ValidateOptions(opts, args)
|
||||||
|
|
||||||
|
with mock.patch.object(sync, "_rlimit_nofile", return_value=(256, 256)):
|
||||||
|
with mock.patch.object(os, "cpu_count", return_value=OS_CPU_COUNT):
|
||||||
|
cmd._ValidateOptionsWithManifest(opts, mp)
|
||||||
|
assert opts.jobs == jobs
|
||||||
|
assert opts.jobs_network == jobs_net
|
||||||
|
assert opts.jobs_checkout == jobs_check
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"argv, jobs_manifest, jobs_manifest_max, jobs, jobs_net, jobs_check",
|
||||||
|
[
|
||||||
|
(["--jobs=10"], None, 5, 5, 5, 5),
|
||||||
|
(["--jobs=10", "--jobs-network=10"], None, 5, 5, 5, 5),
|
||||||
|
(["--jobs=10", "--jobs-checkout=10"], None, 5, 5, 5, 5),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_cli_jobs_sync_j_max(
|
||||||
|
argv, jobs_manifest, jobs_manifest_max, jobs, jobs_net, jobs_check
|
||||||
|
):
|
||||||
|
"""Tests --jobs option behavior with sync-j-max."""
|
||||||
|
mp = mock.MagicMock()
|
||||||
|
mp.manifest.default.sync_j = jobs_manifest
|
||||||
|
mp.manifest.default.sync_j_max = jobs_manifest_max
|
||||||
|
|
||||||
cmd = sync.Sync()
|
cmd = sync.Sync()
|
||||||
opts, args = cmd.OptionParser.parse_args(argv)
|
opts, args = cmd.OptionParser.parse_args(argv)
|
||||||
|
|||||||
Reference in New Issue
Block a user