-
Notifications
You must be signed in to change notification settings - Fork 228
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix pending workload from StrictFIFO CQ doesn't block borrowing from other CQ #1399
Fix pending workload from StrictFIFO CQ doesn't block borrowing from other CQ #1399
Conversation
✅ Deploy Preview for kubernetes-sigs-kueue canceled.
|
/cc |
a903b03
to
04438b0
Compare
04438b0
to
762046c
Compare
4912c7e
to
0658449
Compare
/assign @alculquicondor |
0658449
to
a694fa8
Compare
This definitely requires a release note. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should also have unit test coverage for the new logic
a694fa8
to
33cb8d2
Compare
pkg/scheduler/scheduler.go
Outdated
if cqFlavor.Name == flavor { | ||
cqQuota = cqFlavor.Resources | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@alculquicondor In this implementation, the cqQuota
will have only the last flavor's resources, right?
So, should we add all resources to the cqQuota
?
if cqFlavor.Name == flavor {
for name, quota := range cqFlavor.Resources {
cqQuota[name] = quota
}
}
(This is a pseudo code.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My bad, you're right. Should be fixed now.
/remove-kind feature |
6ab1694
to
3e80a4f
Compare
ce056de
to
1e546c7
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated reservedResources if CQ is borrowing
@@ -1894,3 +1904,81 @@ func TestRequeueAndUpdate(t *testing.T) { | |||
}) | |||
} | |||
} | |||
|
|||
func TestResourcesToReserve(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TestSchedule test quite complex logic. I've added integration tests to test the full functionality
1e546c7
to
0c9b559
Compare
pkg/scheduler/scheduler.go
Outdated
reservedUsage[flavor][resource] = min(usage, cqQuota.Nominal-cq.Usage[flavor][resource]) | ||
} else if cqQuota.BorrowingLimit != nil { | ||
// if cq is borrowing, cq usage can't exceed nominal + borrowing limit | ||
reservedUsage[flavor][resource] = min(usage, cqQuota.Nominal+*cqQuota.BorrowingLimit-cq.Usage[flavor][resource]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't want to block up to the borrowing limit. We only want to block up to nominal.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mimowo what do you think should we do if borrowing with preemption? I guess we should block all of the usage in that case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated, cq usage shouldn't go over cqQuota.Nominal+*cqQuota.BorrowingLimit if borrowing limit is set
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mimowo what do you think should we do if borrowing with preemption? I guess we should block all of the usage in that case?
TBH, I'm not sure, I'm wondering if we can simplify
if cqQuota.BorrowingLimit == nil {
reservedUsage[flavor][resource] = usage
} else {
reservedUsage[flavor][resource] = min(usage, cqQuota.Nominal+*cqQuota.BorrowingLimit-cq.Usage[flavor][resource])
}
to just
reservedUsage[flavor][resource] = usage
@yaroslava-serdiuk do you know a scenario when this is needed?
This looks odd to me at the moment, because in the scenario of single shared CQ as here when cqQuota.Nominal=0
, then if borrowingLimit=null
we reserve usage
, but if borrowingLimit=nominal for the shared (essentially the same a null)
, and cq.Usage[flavor][resource]=borrowigLimit
, then we reserve 0
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we shouldn't go over cqQuota.Nominal+*cqQuota.BorrowingLimit if borrowing limit is set
Correct, but this is verified already in
kueue/pkg/scheduler/flavorassigner/flavorassigner.go
Lines 563 to 566 in 4bc332a
if (rQuota.BorrowingLimit == nil || val <= rQuota.Nominal+*rQuota.BorrowingLimit) && val <= cohortAvailable { | |
mode = Preempt | |
borrow = val > rQuota.Nominal | |
} |
cqQuota.Nominal+*cqQuota.BorrowingLimit-cq.Usage[flavor][resource]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Had discussion with @mimowo offline. I explained the condition:
reservedUsage[flavor][resource] = usage
} else {
reservedUsage[flavor][resource] = min(usage, cqQuota.Nominal+*cqQuota.BorrowingLimit-cq.Usage[flavor][resource])
}
However this is unlikely will be used, since the borrowing limit is often nil.
I also constructed example when the condition unblock another workload from borrowing:
https://github.com/kubernetes-sigs/kueue/assets/43413443/3ee2a4f6-d8ee-4bb4-96d8-2e9662e8869c
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the sync and explanations indeed.
One thing that it let me understand is that the StrictFIFO isn't really the main point here (as the title suggests). If the queue has only one workload it does not matter what is its type. Maybe we could say in the release notes:
"Fix a bug that a preempting workload in one CQ could block another pending workload in another CQ even if the other workload could be scheduled by borrowing".
Regarding the extra condition I think it adds complexity (and the code is already complex). The scenario isn't tested currently by integration tests. Also, the importance of the scenario from the user perspective seems questionable, because it is only relevant if borrowingLimit<nominalQuota of shared CQ
. I could open a PR to simplify. WDYT @alculquicondor ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, the importance of the scenario from the user perspective seems questionable, because it is only relevant if borrowingLimit<nominalQuota of shared CQ.
@mimowo, Could you clarify this situation? Which does this mean, a: borrowingLimit<(nominalQuota of shared CQ)
or b: (borrowingLimit<nominalQuota) of shared CQ
?
If you mean a
, I think a
will often happen. I guess admins want to set borrowingLimit<(nominalQuota of shared CQ)
in each ClusterQueue since admins want to prevent situations where one of the CQ monopolizes all shared capacity.
If my understanding is incorrect , please let me know.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mimowo, Could you clarify this situation? Which does this mean,
a: borrowingLimit<(nominalQuota of shared CQ)
orb: (borrowingLimit<nominalQuota) of shared CQ
?
I meant (a) borrowingLimit<(nominalQuota of shared CQ)
.
If you mean a, I think a will often happen. I guess admins want to set borrowingLimit<(nominalQuota of shared CQ) in each ClusterQueue since admins want to prevent situations where one of the CQ monopolizes all shared capacity.
Makes sense, I was thinking about the scenario as in here, but both are relevant.
So the remaining question: do we care about the scenario, that a workload that is borrowing (B) could get blocked by another workload (A) that is preempting and borrowing in the cohort. The trade-off does not seem obviousm because letting the other workload run may cause the waiting workload wait longer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Feel free to open a PR
pkg/scheduler/scheduler.go
Outdated
reservedUsage[flavor][resource] = min(usage, cqQuota.Nominal-cq.Usage[flavor][resource]) | ||
} else if cqQuota.BorrowingLimit != nil { | ||
// if cq is borrowing, cq usage can't exceed nominal + borrowing limit | ||
reservedUsage[flavor][resource] = min(usage, cqQuota.Nominal+*cqQuota.BorrowingLimit-cq.Usage[flavor][resource]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mimowo what do you think should we do if borrowing with preemption? I guess we should block all of the usage in that case?
pkg/scheduler/scheduler.go
Outdated
for resource, usage := range resourceUsage { | ||
reservedUsage[flavor] = make(map[corev1.ResourceName]int64) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for resource, usage := range resourceUsage { | |
reservedUsage[flavor] = make(map[corev1.ResourceName]int64) | |
reservedUsage[flavor] = make(map[corev1.ResourceName]int64) | |
for resource, usage := range resourceUsage { |
eb46bd1
to
c8bfa46
Compare
…her CQ Apply suggestions from code review Co-authored-by: Aldo Culquicondor <1299064+alculquicondor@users.noreply.github.com>
c8bfa46
to
380ad7d
Compare
/lgtm |
LGTM label has been added. Git tree hash: 8f26d378f3d6b62823e674f008cb2380ff3706b4
|
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: alculquicondor, yaroslava-serdiuk The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
Which issue(s) this PR fixes:
related to #1283
Does this PR introduce a user-facing change?