Skip to content

SchedulingEquivalenceHashing: PodSet.Name in the equivalence hash defeats dedup for clients with unique PodSet names #14534

Description

@venuchitta

What happens

computeSchedulingHash includes each PodSet's Name in the scheduling equivalence hash (workload.go#L403). The name does not affect flavor assignment or placement. So any client that gives each Workload a unique PodSet name puts every Workload in its own equivalence class. The BestEffortFIFO dedup from KEP-9694 then never fires, even when the Workloads are identical for scheduling.

The KEP already flags this

KEP-9694 sets the rule for what belongs in the shape (README):

the scheduling shape must include every Workload property used by the relevant flavor-assignment and placement decision, while excluding metadata that does not affect the decision. Including irrelevant identity fields would split otherwise equivalent Workloads and reduce the benefit without improving correctness.

And it lists the name as a known gap (README):

PodSet name is included conservatively even though the current flavor-assignment path does not use it. This can split otherwise equivalent Workloads into different classes, reducing optimization opportunities, but it cannot merge different scheduling shapes incorrectly.

So excluding the name is safe by the KEP's own reasoning. SchedulingHash is read only by the queue, to bulk-move equivalent Workloads to the inadmissible pool. Nothing in the scheduler or flavor assigner reads it. Dropping the name cannot change an admission decision. It only lets equivalent Workloads share a class.

Why this is common, not a corner case

The Pod group integration sets each PodSet name to the pod role hash (pod_controller.go#L828), and getRoleHash returns the kueue.x-k8s.io/role-hash annotation as-is when the client sets it (pod_controller.go#L679). A platform that derives that annotation from a per-task identifier, rather than the pod shape, gives every Workload a distinct PodSet name. The role hash only needs to be stable within a pod group, not across tasks, so this is a reasonable choice. StatefulSet and LeaderWorkerSet reuse the same shape, so they can hit this too.

Impact

We saw this on a full, TAS-enabled cluster. Workloads that were identical for scheduling each got their own equivalence class, so the bulk-move to inadmissible never fired. The scheduler kept re-running a full fair-share preemption tournament, with a per-candidate topology placement check, on a rotating set of identical and equally unschedulable heads. The single scheduling goroutine stayed saturated and admission latency climbed. With the name excluded, those Workloads collapse to a few classes, one representative gets evaluated, and the rest wait until cluster state changes.

Proposed fix

Drop ps.Name from the per-PodSet shape in computeSchedulingHash. PodSet order in the array already separates multi-PodSet Workloads. If a consumer needs name-level granularity, gate the name behind a flag instead of hashing it by default.

Reproduce

  1. Enable SchedulingEquivalenceHashing (default on), a BestEffortFIFO LocalQueue, and a full ClusterQueue.
  2. Submit many pods with identical specs and a distinct kueue.x-k8s.io/role-hash on each.
  3. The Bulk-moved equivalent workloads to inadmissible log line does not fire, and admission attempts per cycle stay high.
  4. Repeat with a shared role hash. The bulk-move fires.

Environment

Observed on v0.18.4. Confirmed on main at 6d6df4c.

/kind bug

Metadata

Metadata

Assignees

No one assigned

    Labels

    kind/featureCategorizes issue or PR as related to a new feature.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions