Skip to content

Commit 523caa9

Browse files
ifreundbapt
authored andcommitted
scheduler: loosen ordering for upgrade jobs
Currently the scheduler splits pretty much every upgrade job in practice. This is because when both the old and new packages for A depend on the old and new packages for B respectively, the strict rules of the scheduler see a cycle and don't allow upgrading B while A is install to avoid a state where old A is installed but old B is not. I believe this rule to be too strict when applied to upgrade jobs and have loosened the "old depends on old" rule to ignore upgrade jobs where the "new depends on new" rule also applies. This patch passes all existing test cases, including the test cases checking that split upgrades are done when necessary. Therefore, I do not think the new rules are too loose. This patch also adds a basic test case that was not passed with the old logic, I'm very surprised we didn't have a test checking this. Sponsored by: The FreeBSD Foundation
1 parent a64787a commit 523caa9

File tree

2 files changed

+102
-5
lines changed

2 files changed

+102
-5
lines changed

libpkg/pkg_jobs_schedule.c

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,11 @@ enum pkg_jobs_schedule_graph_edge_type {
9595
* 3. A's old package conflicts with B's new package
9696
* 4. A and B are the two halves of a split upgrade job
9797
* and A is the delete half.
98+
*
99+
* There is one exception made to rule 2 in order to avoid splitting in
100+
* the common case of upgrading packages X and Y where Xold depends on Yold
101+
* and Xnew depends on Ynew. In this case, rule 2 is ignored and there is no
102+
* edge from X to Y.
98103
*/
99104
static enum pkg_jobs_schedule_graph_edge_type
100105
pkg_jobs_schedule_graph_edge(struct pkg_solved *a, struct pkg_solved *b)
@@ -160,17 +165,22 @@ pkg_jobs_schedule_graph_edge(struct pkg_solved *a, struct pkg_solved *b)
160165
if (a_new != NULL && b_new != NULL &&
161166
pkg_jobs_schedule_direct_depends(b_new, a_new)) {
162167
return (PKG_SCHEDULE_EDGE_NEW_DEP_NEW);
163-
} else if (a_old != NULL && b_old != NULL &&
164-
pkg_jobs_schedule_direct_depends(a_old, b_old)) {
165-
return (PKG_SCHEDULE_EDGE_OLD_DEP_OLD);
166-
} else if (a_old != NULL && b_new != NULL) {
168+
}
169+
if (a_old != NULL && b_new != NULL) {
167170
struct pkg_conflict *conflict = NULL;
168171
while (pkg_conflicts(a_old, &conflict) == EPKG_OK) {
169172
if (STREQ(b_new->uid, conflict->uid)) {
170173
return (PKG_SCHEDULE_EDGE_OLD_CONFLICT_NEW);
171174
}
172175
}
173176
}
177+
if (a_old != NULL && b_old != NULL &&
178+
pkg_jobs_schedule_direct_depends(a_old, b_old)) {
179+
if (!(a_new != NULL && b_new != NULL &&
180+
pkg_jobs_schedule_direct_depends(a_new, b_new))) {
181+
return (PKG_SCHEDULE_EDGE_OLD_DEP_OLD);
182+
}
183+
}
174184

175185
return (PKG_SCHEDULE_EDGE_NONE);
176186
}

tests/frontend/upgrade.sh

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ tests_init \
1212
dir_is_symlink_to_a_dir \
1313
vital \
1414
vital_force \
15-
vital_force_cant_remove
15+
vital_force_cant_remove \
16+
upgrade_with_dependency
1617

1718
issue1881_body() {
1819
atf_check -s exit:0 sh ${RESOURCEDIR}/test_subr.sh new_pkg pkg1 pkg_a 1
@@ -422,3 +423,89 @@ Your packages are up to date.
422423
ERROR=""
423424
atf_check -o inline:"${OUTPUT}" -e inline:"${ERROR}" -s exit:0 pkg -o REPOS_DIR="$TMPDIR/repoconf" -r ${TMPDIR}/target -o PKG_CACHEDIR="$TMPDIR" -o FORCE_CAN_REMOVE_VITAL=NO upgrade -fy myplop
424425
}
426+
427+
upgrade_with_dependency_body() {
428+
atf_check -s exit:0 sh ${RESOURCEDIR}/test_subr.sh new_pkg "testa" "testa" "1.0"
429+
atf_check pkg create -M testa.ucl -o ./repo
430+
431+
atf_check -s exit:0 sh ${RESOURCEDIR}/test_subr.sh new_pkg "testb" "testb" "1.0"
432+
cat << EOF >> testb.ucl
433+
deps: {
434+
testa: {
435+
origin: "testa",
436+
}
437+
}
438+
EOF
439+
atf_check pkg create -M testb.ucl -o ./repo
440+
441+
cat << EOF > pkg.conf
442+
PKG_DBDIR=${TMPDIR}
443+
REPOS_DIR=[]
444+
repositories: {
445+
local: { url : file://${TMPDIR}/repo }
446+
}
447+
EOF
448+
atf_check \
449+
-o inline:"Creating repository in ./repo: done\nPacking files for repository: done\n" \
450+
-e empty \
451+
-s exit:0 \
452+
pkg -C ./pkg.conf repo ./repo
453+
454+
atf_check \
455+
-o ignore \
456+
-s exit:0 \
457+
pkg -C ./pkg.conf update -f
458+
459+
atf_check \
460+
pkg -C ./pkg.conf install -qy testb
461+
462+
rm -r ./repo
463+
464+
atf_check -s exit:0 sh ${RESOURCEDIR}/test_subr.sh new_pkg "testa" "testa" "2.0"
465+
atf_check pkg create -M testa.ucl -o ./repo
466+
467+
atf_check -s exit:0 sh ${RESOURCEDIR}/test_subr.sh new_pkg "testb" "testb" "2.0"
468+
cat << EOF >> testb.ucl
469+
deps: {
470+
testa: {
471+
origin: "testa",
472+
}
473+
}
474+
EOF
475+
atf_check pkg create -M testb.ucl -o ./repo
476+
477+
atf_check \
478+
-o inline:"Creating repository in ./repo: done\nPacking files for repository: done\n" \
479+
-e empty \
480+
-s exit:0 \
481+
pkg -C ./pkg.conf repo ./repo
482+
483+
atf_check \
484+
-o ignore \
485+
-e empty \
486+
-s exit:0 \
487+
pkg -C ./pkg.conf update -f
488+
489+
OUTPUT="Updating local repository catalogue...
490+
local repository is up to date.
491+
All repositories are up to date.
492+
Checking for upgrades (2 candidates): done
493+
Processing candidates (2 candidates): done
494+
Checking integrity... done (0 conflicting)
495+
The following 2 package(s) will be affected (of 0 checked):
496+
497+
Installed packages to be UPGRADED:
498+
testa: 1.0 -> 2.0
499+
testb: 1.0 -> 2.0
500+
501+
Number of packages to be upgraded: 2
502+
${JAILED}[1/2] Upgrading testa from 1.0 to 2.0...
503+
${JAILED}[2/2] Upgrading testb from 1.0 to 2.0...
504+
"
505+
506+
atf_check \
507+
-o inline:"${OUTPUT}" \
508+
-e empty \
509+
-s exit:0 \
510+
pkg -C ./pkg.conf upgrade -y
511+
}

0 commit comments

Comments
 (0)