Skip to content

Commit fccc2fa

Browse files
committed
Avoid unnecessary ListManagedInstances calls
1 parent 4e52ae7 commit fccc2fa

File tree

5 files changed

+61
-16
lines changed

5 files changed

+61
-16
lines changed

cluster-autoscaler/cloudprovider/gce/cache.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,15 @@ func (gc *GceCache) GetMigInstancesUpdateTime(migRef GceRef) (time.Time, bool) {
181181
return timestamp, found
182182
}
183183

184+
// IsMigInstancesCacheEmpty returns true if instances cache for the given MIG is empty.
185+
func (gc *GceCache) IsMigInstancesCacheEmpty(migRef GceRef) bool {
186+
gc.cacheMutex.Lock()
187+
defer gc.cacheMutex.Unlock()
188+
189+
_, found := gc.instances[migRef]
190+
return !found
191+
}
192+
184193
// GetMigForInstance returns the cached MIG for instance GceRef
185194
func (gc *GceCache) GetMigForInstance(instanceRef GceRef) (GceRef, bool) {
186195
gc.cacheMutex.Lock()

cluster-autoscaler/cloudprovider/gce/cache_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ package gce
1818

1919
import (
2020
"testing"
21+
"time"
22+
23+
"github.com/stretchr/testify/assert"
2124
)
2225

2326
func TestMachineCache(t *testing.T) {
@@ -113,3 +116,35 @@ func TestListManagedInstancesResultsCache(t *testing.T) {
113116
t.Errorf("Expected listManagedInstancesResultsCache to be empty, but it still contains %d entries", cacheSize)
114117
}
115118
}
119+
120+
func TestMigInstancesCache(t *testing.T) {
121+
c := NewGceCache()
122+
migRef := GceRef{
123+
Project: "project",
124+
Zone: "us-test1",
125+
Name: "mig",
126+
}
127+
128+
// Pecondition: Cache is empty before it is set
129+
assert.True(t, c.IsMigInstancesCacheEmpty(migRef), "Expected MIG instaces cache to be empty before SetMigInstances was called")
130+
131+
// When: set MIG instances cache
132+
instances := make([]GceInstance, 0)
133+
instances = append(instances, GceInstance{NumericId: 123})
134+
instances = append(instances, GceInstance{NumericId: 456})
135+
c.SetMigInstances(migRef, instances, time.Now())
136+
137+
// Then: can retrieve instances from cache by MIG reference
138+
gotInstances, found := c.GetMigInstances(migRef)
139+
assert.False(t, c.IsMigInstancesCacheEmpty(migRef), "Expected MIG instaces cache to not be empty after SetMigInstances was called")
140+
assert.True(t, found, "Expected MIG insatcnes cache to be populated")
141+
assert.Equal(t, gotInstances, instances)
142+
143+
// When: Invalidate the cache
144+
c.InvalidateAllMigInstances()
145+
146+
// Then: Cache is empty
147+
_, found = c.GetMigInstances(migRef)
148+
assert.False(t, found, "Expected MIG instaces cache to be empty after InvalidateAllMigInstances was called")
149+
assert.True(t, c.IsMigInstancesCacheEmpty(migRef))
150+
}

cluster-autoscaler/cloudprovider/gce/gce_manager_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -686,13 +686,15 @@ func TestGetMigForInstance(t *testing.T) {
686686

687687
setupTestDefaultPool(g, false)
688688
g.cache.InvalidateAllMigBasenames()
689+
g.cache.InvalidateAllInstancesToMig()
690+
g.cache.InvalidateAllMigInstances()
689691

690692
server.On("handle", "/projects/project1/zones/us-central1-b/instanceGroupManagers").Return(
691693
buildListInstanceGroupManagersResponse(
692694
buildListInstanceGroupManagersResponsePart(defaultPoolMigName, zoneB, 7),
693695
buildListInstanceGroupManagersResponsePart(extraPoolMigName, zoneB, 8),
694696
)).Once()
695-
server.On("handle", "/projects/project1/zones/us-central1-b/instanceGroupManagers/gke-cluster-1-default-pool/listManagedInstances").Return(buildFourRunningInstancesOnDefaultMigManagedInstancesResponse(zoneB)).Twice()
697+
server.On("handle", "/projects/project1/zones/us-central1-b/instanceGroupManagers/gke-cluster-1-default-pool/listManagedInstances").Return(buildFourRunningInstancesOnDefaultMigManagedInstancesResponse(zoneB)).Once()
696698
gceRef1 := GceRef{
697699
Project: projectId,
698700
Zone: zoneB,

cluster-autoscaler/cloudprovider/gce/mig_info_provider.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ type MigInfoProvider interface {
4141
GetMigForInstance(instanceRef GceRef) (Mig, error)
4242
// RegenerateMigInstancesCache regenerates MIGs to instances mapping cache
4343
RegenerateMigInstancesCache() error
44-
// GetMigTargetSize returns target size for given MIG ref
44+
// GetMigTargetSize returns target size of a MIG
4545
GetMigTargetSize(migRef GceRef) (int64, error)
4646
// GetMigActualSize returns current number of instances in a given MIG
4747
GetMigActualSize(migRef GceRef) (int64, error)
@@ -112,6 +112,7 @@ func (c *cachingMigInfoProvider) GetMigInstances(migRef GceRef) ([]GceInstance,
112112
return instances, nil
113113
}
114114

115+
// MIG is not in the cache.
115116
err := c.fillMigInstances(migRef)
116117
if err != nil {
117118
return nil, err
@@ -135,16 +136,24 @@ func (c *cachingMigInfoProvider) GetMigForInstance(instanceRef GceRef) (Mig, err
135136
return nil, nil
136137
}
137138

139+
// Cache is cleared every loop.
140+
// If it's not empty, it's been refreshed this loop, and we don't want to refresh it again.
141+
if !c.cache.IsMigInstancesCacheEmpty(mig.GceRef()) {
142+
c.cache.MarkInstanceMigUnknown(instanceRef)
143+
return nil, nil
144+
}
145+
138146
err = c.fillMigInstances(mig.GceRef())
139147
if err != nil {
140148
return nil, err
141149
}
142-
150+
// Check in the cache again after it's been refilled
143151
mig, found, err = c.getCachedMigForInstance(instanceRef)
144152
if !found {
145153
c.cache.MarkInstanceMigUnknown(instanceRef)
146154
}
147155
return mig, err
156+
148157
}
149158

150159
func (c *cachingMigInfoProvider) getCachedMigForInstance(instanceRef GceRef) (Mig, bool, error) {

cluster-autoscaler/cloudprovider/gce/mig_info_provider_test.go

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1551,24 +1551,14 @@ func TestGetMigMachineType(t *testing.T) {
15511551
}
15521552
}
15531553

1554-
func TestMultipleGetMigInstanceCallsLimited(t *testing.T) {
1554+
func TestMultipleFillMigInstancesCallsLimited(t *testing.T) {
15551555
mig := &gceMig{
15561556
gceRef: GceRef{
15571557
Project: "project",
15581558
Zone: "zone",
15591559
Name: "base-instance-name",
15601560
},
15611561
}
1562-
instance := GceInstance{
1563-
Instance: cloudprovider.Instance{Id: "gce://project/zone/base-instance-name-abcd"}, NumericId: 1111,
1564-
}
1565-
instanceRef, err := GceRefFromProviderId(instance.Id)
1566-
assert.Nil(t, err)
1567-
instance2 := GceInstance{
1568-
Instance: cloudprovider.Instance{Id: "gce://project/zone/base-instance-name-abcd2"}, NumericId: 222,
1569-
}
1570-
instanceRef2, err := GceRefFromProviderId(instance2.Id)
1571-
assert.Nil(t, err)
15721562
now := time.Now()
15731563
for name, tc := range map[string]struct {
15741564
refreshRateDuration time.Duration
@@ -1617,10 +1607,10 @@ func TestMultipleGetMigInstanceCallsLimited(t *testing.T) {
16171607
timeProvider: ft,
16181608
}
16191609
ft.now = tc.firstCallTime
1620-
_, err = provider.GetMigForInstance(instanceRef)
1610+
err := provider.fillMigInstances(mig.GceRef())
16211611
assert.NoError(t, err)
16221612
ft.now = tc.secondCallTime
1623-
_, err = provider.GetMigForInstance(instanceRef2)
1613+
err = provider.fillMigInstances(mig.GceRef())
16241614
assert.NoError(t, err)
16251615
assert.Equal(t, tc.expectedCallsToFetchMigInstances, callCounter[mig.GceRef()])
16261616
})

0 commit comments

Comments
 (0)