diff options
author | Mike Frysinger <vapier@google.com> | 2022-12-07 09:41:47 -0500 |
---|---|---|
committer | Mike Frysinger <vapier@google.com> | 2022-12-08 15:06:24 +0000 |
commit | f159ce0f9ef5bce5a4866a677f0534e3b45a80e2 (patch) | |
tree | 0d8410ff9ef7377f9bc2fa7c413bb408cb7cd6c1 | |
parent | 802cd0c6016d91c62c25178ee1ccc1e78505502c (diff) | |
download | git-repo-f159ce0f9ef5bce5a4866a677f0534e3b45a80e2.tar.gz |
sync: fix manifest sync-j handling
Since --jobs defaults to 0, not None, we never pull the value out
of the manifest. Treat values of 0 and None the same to fix.
Bug: http://b/239712300
Bug: http://b/260908907
Change-Id: I9b1026682072366616825fd72f90bd90c10a252f
Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/354254
Tested-by: Mike Frysinger <vapier@google.com>
Reviewed-by: Josip Sokcevic <sokcevic@google.com>
Reviewed-by: Sam Saccone <samccone@google.com>
-rw-r--r-- | subcmds/sync.py | 71 | ||||
-rw-r--r-- | tests/test_subcmds_sync.py | 47 |
2 files changed, 89 insertions, 29 deletions
diff --git a/subcmds/sync.py b/subcmds/sync.py index 0c0f0cf3..e4e7a971 100644 --- a/subcmds/sync.py +++ b/subcmds/sync.py | |||
@@ -1190,6 +1190,45 @@ later is required to fix a server side protocol bug. | |||
1190 | 'release. Use `--auto-gc` instead.', file=sys.stderr) | 1190 | 'release. Use `--auto-gc` instead.', file=sys.stderr) |
1191 | opt.auto_gc = True | 1191 | opt.auto_gc = True |
1192 | 1192 | ||
1193 | def _ValidateOptionsWithManifest(self, opt, mp): | ||
1194 | """Like ValidateOptions, but after we've updated the manifest. | ||
1195 | |||
1196 | Needed to handle sync-xxx option defaults in the manifest. | ||
1197 | |||
1198 | Args: | ||
1199 | opt: The options to process. | ||
1200 | mp: The manifest project to pull defaults from. | ||
1201 | """ | ||
1202 | if not opt.jobs: | ||
1203 | # If the user hasn't made a choice, use the manifest value. | ||
1204 | opt.jobs = mp.manifest.default.sync_j | ||
1205 | if opt.jobs: | ||
1206 | # If --jobs has a non-default value, propagate it as the default for | ||
1207 | # --jobs-xxx flags too. | ||
1208 | if not opt.jobs_network: | ||
1209 | opt.jobs_network = opt.jobs | ||
1210 | if not opt.jobs_checkout: | ||
1211 | opt.jobs_checkout = opt.jobs | ||
1212 | else: | ||
1213 | # Neither user nor manifest have made a choice, so setup defaults. | ||
1214 | if not opt.jobs_network: | ||
1215 | opt.jobs_network = 1 | ||
1216 | if not opt.jobs_checkout: | ||
1217 | opt.jobs_checkout = DEFAULT_LOCAL_JOBS | ||
1218 | opt.jobs = os.cpu_count() | ||
1219 | |||
1220 | # Try to stay under user rlimit settings. | ||
1221 | # | ||
1222 | # Since each worker requires at 3 file descriptors to run `git fetch`, use | ||
1223 | # that to scale down the number of jobs. Unfortunately there isn't an easy | ||
1224 | # way to determine this reliably as systems change, but it was last measured | ||
1225 | # by hand in 2011. | ||
1226 | soft_limit, _ = _rlimit_nofile() | ||
1227 | jobs_soft_limit = max(1, (soft_limit - 5) // 3) | ||
1228 | opt.jobs = min(opt.jobs, jobs_soft_limit) | ||
1229 | opt.jobs_network = min(opt.jobs_network, jobs_soft_limit) | ||
1230 | opt.jobs_checkout = min(opt.jobs_checkout, jobs_soft_limit) | ||
1231 | |||
1193 | def Execute(self, opt, args): | 1232 | def Execute(self, opt, args): |
1194 | manifest = self.outer_manifest | 1233 | manifest = self.outer_manifest |
1195 | if not opt.outer_manifest: | 1234 | if not opt.outer_manifest: |
@@ -1240,35 +1279,9 @@ later is required to fix a server side protocol bug. | |||
1240 | else: | 1279 | else: |
1241 | print('Skipping update of local manifest project.') | 1280 | print('Skipping update of local manifest project.') |
1242 | 1281 | ||
1243 | # Now that the manifests are up-to-date, setup the jobs value. | 1282 | # Now that the manifests are up-to-date, setup options whose defaults might |
1244 | if opt.jobs is None: | 1283 | # be in the manifest. |
1245 | # User has not made a choice, so use the manifest settings. | 1284 | self._ValidateOptionsWithManifest(opt, mp) |
1246 | opt.jobs = mp.default.sync_j | ||
1247 | if opt.jobs is not None: | ||
1248 | # Neither user nor manifest have made a choice. | ||
1249 | if opt.jobs_network is None: | ||
1250 | opt.jobs_network = opt.jobs | ||
1251 | if opt.jobs_checkout is None: | ||
1252 | opt.jobs_checkout = opt.jobs | ||
1253 | # Setup defaults if jobs==0. | ||
1254 | if not opt.jobs: | ||
1255 | if not opt.jobs_network: | ||
1256 | opt.jobs_network = 1 | ||
1257 | if not opt.jobs_checkout: | ||
1258 | opt.jobs_checkout = DEFAULT_LOCAL_JOBS | ||
1259 | opt.jobs = os.cpu_count() | ||
1260 | |||
1261 | # Try to stay under user rlimit settings. | ||
1262 | # | ||
1263 | # Since each worker requires at 3 file descriptors to run `git fetch`, use | ||
1264 | # that to scale down the number of jobs. Unfortunately there isn't an easy | ||
1265 | # way to determine this reliably as systems change, but it was last measured | ||
1266 | # by hand in 2011. | ||
1267 | soft_limit, _ = _rlimit_nofile() | ||
1268 | jobs_soft_limit = max(1, (soft_limit - 5) // 3) | ||
1269 | opt.jobs = min(opt.jobs, jobs_soft_limit) | ||
1270 | opt.jobs_network = min(opt.jobs_network, jobs_soft_limit) | ||
1271 | opt.jobs_checkout = min(opt.jobs_checkout, jobs_soft_limit) | ||
1272 | 1285 | ||
1273 | superproject_logging_data = {} | 1286 | superproject_logging_data = {} |
1274 | self._UpdateProjectsRevisionId(opt, args, superproject_logging_data, | 1287 | self._UpdateProjectsRevisionId(opt, args, superproject_logging_data, |
diff --git a/tests/test_subcmds_sync.py b/tests/test_subcmds_sync.py index 13f3f873..236d54e5 100644 --- a/tests/test_subcmds_sync.py +++ b/tests/test_subcmds_sync.py | |||
@@ -13,11 +13,13 @@ | |||
13 | # limitations under the License. | 13 | # limitations under the License. |
14 | """Unittests for the subcmds/sync.py module.""" | 14 | """Unittests for the subcmds/sync.py module.""" |
15 | 15 | ||
16 | import os | ||
16 | import unittest | 17 | import unittest |
17 | from unittest import mock | 18 | from unittest import mock |
18 | 19 | ||
19 | import pytest | 20 | import pytest |
20 | 21 | ||
22 | import command | ||
21 | from subcmds import sync | 23 | from subcmds import sync |
22 | 24 | ||
23 | 25 | ||
@@ -43,6 +45,51 @@ def test_get_current_branch_only(use_superproject, cli_args, result): | |||
43 | assert cmd._GetCurrentBranchOnly(opts, cmd.manifest) == result | 45 | assert cmd._GetCurrentBranchOnly(opts, cmd.manifest) == result |
44 | 46 | ||
45 | 47 | ||
48 | # Used to patch os.cpu_count() for reliable results. | ||
49 | OS_CPU_COUNT = 24 | ||
50 | |||
51 | @pytest.mark.parametrize('argv, jobs_manifest, jobs, jobs_net, jobs_check', [ | ||
52 | # No user or manifest settings. | ||
53 | ([], None, OS_CPU_COUNT, 1, command.DEFAULT_LOCAL_JOBS), | ||
54 | # No user settings, so manifest settings control. | ||
55 | ([], 3, 3, 3, 3), | ||
56 | # User settings, but no manifest. | ||
57 | (['--jobs=4'], None, 4, 4, 4), | ||
58 | (['--jobs=4', '--jobs-network=5'], None, 4, 5, 4), | ||
59 | (['--jobs=4', '--jobs-checkout=6'], None, 4, 4, 6), | ||
60 | (['--jobs=4', '--jobs-network=5', '--jobs-checkout=6'], None, 4, 5, 6), | ||
61 | (['--jobs-network=5'], None, OS_CPU_COUNT, 5, command.DEFAULT_LOCAL_JOBS), | ||
62 | (['--jobs-checkout=6'], None, OS_CPU_COUNT, 1, 6), | ||
63 | (['--jobs-network=5', '--jobs-checkout=6'], None, OS_CPU_COUNT, 5, 6), | ||
64 | # User settings with manifest settings. | ||
65 | (['--jobs=4'], 3, 4, 4, 4), | ||
66 | (['--jobs=4', '--jobs-network=5'], 3, 4, 5, 4), | ||
67 | (['--jobs=4', '--jobs-checkout=6'], 3, 4, 4, 6), | ||
68 | (['--jobs=4', '--jobs-network=5', '--jobs-checkout=6'], 3, 4, 5, 6), | ||
69 | (['--jobs-network=5'], 3, 3, 5, 3), | ||
70 | (['--jobs-checkout=6'], 3, 3, 3, 6), | ||
71 | (['--jobs-network=5', '--jobs-checkout=6'], 3, 3, 5, 6), | ||
72 | # Settings that exceed rlimits get capped. | ||
73 | (['--jobs=1000000'], None, 83, 83, 83), | ||
74 | ([], 1000000, 83, 83, 83), | ||
75 | ]) | ||
76 | def test_cli_jobs(argv, jobs_manifest, jobs, jobs_net, jobs_check): | ||
77 | """Tests --jobs option behavior.""" | ||
78 | mp = mock.MagicMock() | ||
79 | mp.manifest.default.sync_j = jobs_manifest | ||
80 | |||
81 | cmd = sync.Sync() | ||
82 | opts, args = cmd.OptionParser.parse_args(argv) | ||
83 | cmd.ValidateOptions(opts, args) | ||
84 | |||
85 | with mock.patch.object(sync, '_rlimit_nofile', return_value=(256, 256)): | ||
86 | with mock.patch.object(os, 'cpu_count', return_value=OS_CPU_COUNT): | ||
87 | cmd._ValidateOptionsWithManifest(opts, mp) | ||
88 | assert opts.jobs == jobs | ||
89 | assert opts.jobs_network == jobs_net | ||
90 | assert opts.jobs_checkout == jobs_check | ||
91 | |||
92 | |||
46 | class GetPreciousObjectsState(unittest.TestCase): | 93 | class GetPreciousObjectsState(unittest.TestCase): |
47 | """Tests for _GetPreciousObjectsState.""" | 94 | """Tests for _GetPreciousObjectsState.""" |
48 | 95 | ||