mirror of
https://github.com/k3s-io/kubernetes.git
synced 2026-07-18 04:54:54 +00:00
Introduce special handling for updates involving a single resource slice.
Typically, a single DRA driver uses only one ResourceSlice per resource pool. Currently, the ResourceSliceController updates this slice by deleting and recreating it when devices are added or removed. For this common single-slice-per-pool scenario, we can improve efficiency by directly updating the existing ResourceSlice instead.
This commit is contained in:
@@ -490,45 +490,51 @@ func (c *Controller) syncPool(ctx context.Context, poolName string) error {
|
||||
logger.V(5).Info("Existing slices", "obsolete", klog.KObjSlice(obsoleteSlices), "current", klog.KObjSlice(currentSlices))
|
||||
|
||||
if pool, ok := resources.Pools[poolName]; ok {
|
||||
// Match each existing slice against the desired slices.
|
||||
// Two slices match if they contain exactly the same
|
||||
// device IDs, in an arbitrary order. Such a matched
|
||||
// slice gets updated with the desired content if
|
||||
// there is a difference.
|
||||
// Match each existing slice against the desired slices. Two slices
|
||||
// "match" if they contain exactly the same device IDs, in an arbitrary
|
||||
// order. As a special case, slices are also considered "matched" in the
|
||||
// scenario where there's a single existing slice and a single desired
|
||||
// slice. Such a "matched" slice gets updated with the desired content
|
||||
// if there is a difference.
|
||||
//
|
||||
// This supports updating the definition of devices
|
||||
// in a slice. Adding or removing devices is done
|
||||
// by deleting the old slice and creating a new one.
|
||||
// In the case where there is more than one existing or desired slices,
|
||||
// adding or removing devices is done by deleting the old slice and
|
||||
// creating a new one. This is primarily a simplification of the code:
|
||||
// to support adding or removing devices from existing slices, we would
|
||||
// have to identify "most similar" slices (= minimal editing distance).
|
||||
//
|
||||
// This is primarily a simplification of the code:
|
||||
// to support adding or removing devices from
|
||||
// existing slices, we would have to identify "most
|
||||
// similar" slices (= minimal editing distance).
|
||||
// In currentSliceForDesiredSlice we keep track of which desired slice
|
||||
// has a matched slice.
|
||||
//
|
||||
// In currentSliceForDesiredSlice we keep track of
|
||||
// which desired slice has a matched slice.
|
||||
//
|
||||
// At the end of the loop, each current slice is either
|
||||
// a match or obsolete.
|
||||
// At the end of the loop, each current slice is either a match or
|
||||
// obsolete.
|
||||
currentSliceForDesiredSlice := make(map[int]*resourceapi.ResourceSlice, len(pool.Slices))
|
||||
for _, currentSlice := range currentSlices {
|
||||
matched := false
|
||||
for i := range pool.Slices {
|
||||
if _, ok := currentSliceForDesiredSlice[i]; ok {
|
||||
// Already has a match.
|
||||
continue
|
||||
if len(currentSlices) == 1 && len(currentSlices) == len(pool.Slices) {
|
||||
// If there's just one existing slice and one desired slice, assume
|
||||
// they "matched" such that if required, it is the existing slice
|
||||
// which gets updated and we avoid an unnecessary deletion and
|
||||
// recreation of the slice.
|
||||
currentSliceForDesiredSlice[0] = currentSlices[0]
|
||||
} else {
|
||||
for _, currentSlice := range currentSlices {
|
||||
matched := false
|
||||
for i := range pool.Slices {
|
||||
if _, ok := currentSliceForDesiredSlice[i]; ok {
|
||||
// Already has a match.
|
||||
continue
|
||||
}
|
||||
if sameSlice(currentSlice, &pool.Slices[i]) {
|
||||
currentSliceForDesiredSlice[i] = currentSlice
|
||||
logger.V(5).Info("Matched existing slice", "slice", klog.KObj(currentSlice), "matchIndex", i)
|
||||
matched = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if sameSlice(currentSlice, &pool.Slices[i]) {
|
||||
currentSliceForDesiredSlice[i] = currentSlice
|
||||
logger.V(5).Info("Matched existing slice", "slice", klog.KObj(currentSlice), "matchIndex", i)
|
||||
matched = true
|
||||
break
|
||||
if !matched {
|
||||
obsoleteSlices = append(obsoleteSlices, currentSlice)
|
||||
logger.V(5).Info("Unmatched existing slice", "slice", klog.KObj(currentSlice))
|
||||
}
|
||||
}
|
||||
if !matched {
|
||||
obsoleteSlices = append(obsoleteSlices, currentSlice)
|
||||
logger.V(5).Info("Unmatched existing slice", "slice", klog.KObj(currentSlice))
|
||||
}
|
||||
}
|
||||
|
||||
// Desired metadata which must be set in each slice.
|
||||
|
||||
@@ -272,7 +272,7 @@ func TestControllerSyncPool(t *testing.T) {
|
||||
},
|
||||
expectedResourceSlices: nil,
|
||||
},
|
||||
"delete-and-add-slice": {
|
||||
"one-existing-and-one-desired-slice-should-be-updated-inplace": {
|
||||
nodeUID: nodeUID,
|
||||
initialObjects: []runtime.Object{
|
||||
// no devices
|
||||
@@ -288,14 +288,52 @@ func TestControllerSyncPool(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expectedStats: Stats{
|
||||
NumDeletes: 1,
|
||||
NumUpdates: 1,
|
||||
},
|
||||
expectedResourceSlices: []resourceapi.ResourceSlice{
|
||||
*MakeResourceSlice().Name(resourceSlice1).UID(resourceSlice1).ResourceVersion("1").
|
||||
NodeOwnerReferences(ownerName, string(nodeUID)).NodeName(ownerName).
|
||||
Driver(driverName).Devices([]resourceapi.Device{{Name: deviceName}}).
|
||||
Pool(resourceapi.ResourcePool{Name: poolName, Generation: 1, ResourceSliceCount: 1}).Obj(),
|
||||
},
|
||||
},
|
||||
"delete-and-add-slice-when-more-than-one-existing-or-desired-slice": {
|
||||
nodeUID: nodeUID,
|
||||
initialObjects: []runtime.Object{
|
||||
// No devices in first ResourceSlice.
|
||||
MakeResourceSlice().Name(resourceSlice1).UID(resourceSlice1).
|
||||
NodeOwnerReferences(ownerName, string(nodeUID)).NodeName(ownerName).
|
||||
Driver(driverName).Devices([]resourceapi.Device{}).
|
||||
Pool(resourceapi.ResourcePool{Name: poolName, Generation: 1, ResourceSliceCount: 2}).Obj(),
|
||||
MakeResourceSlice().Name(resourceSlice2).UID(resourceSlice2).
|
||||
NodeOwnerReferences(ownerName, string(nodeUID)).NodeName(ownerName).
|
||||
Driver(driverName).Devices([]resourceapi.Device{{Name: deviceName2}}).
|
||||
Pool(resourceapi.ResourcePool{Name: poolName, Generation: 1, ResourceSliceCount: 2}).Obj(),
|
||||
},
|
||||
inputDriverResources: &DriverResources{
|
||||
Pools: map[string]Pool{
|
||||
poolName: {
|
||||
Slices: []Slice{
|
||||
{Devices: []resourceapi.Device{{Name: deviceName1}}},
|
||||
{Devices: []resourceapi.Device{{Name: deviceName2}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedStats: Stats{
|
||||
NumCreates: 1,
|
||||
NumUpdates: 1,
|
||||
NumDeletes: 1,
|
||||
},
|
||||
expectedResourceSlices: []resourceapi.ResourceSlice{
|
||||
*MakeResourceSlice().Name(generatedName1).GenerateName(generateName).
|
||||
NodeOwnerReferences(ownerName, string(nodeUID)).NodeName(ownerName).
|
||||
Driver(driverName).Devices([]resourceapi.Device{{Name: deviceName}}).
|
||||
Pool(resourceapi.ResourcePool{Name: poolName, Generation: 2, ResourceSliceCount: 1}).Obj(),
|
||||
Driver(driverName).Devices([]resourceapi.Device{{Name: deviceName1}}).
|
||||
Pool(resourceapi.ResourcePool{Name: poolName, Generation: 2, ResourceSliceCount: 2}).Obj(),
|
||||
*MakeResourceSlice().Name(resourceSlice2).UID(resourceSlice2).ResourceVersion("1").
|
||||
NodeOwnerReferences(ownerName, string(nodeUID)).NodeName(ownerName).
|
||||
Driver(driverName).Devices([]resourceapi.Device{{Name: deviceName2}}).
|
||||
Pool(resourceapi.ResourcePool{Name: poolName, Generation: 2, ResourceSliceCount: 2}).Obj(),
|
||||
},
|
||||
},
|
||||
"delete-redundant-slice": {
|
||||
|
||||
Reference in New Issue
Block a user