Skip to content

Commit 7156a95

Browse files
committed
Avoid unnecessary ListManagedInstances calls
1 parent 9e62173 commit 7156a95

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
// GetMigBasename returns basename for given MIG ref
4747
GetMigBasename(migRef GceRef) (string, error)
@@ -110,6 +110,7 @@ func (c *cachingMigInfoProvider) GetMigInstances(migRef GceRef) ([]GceInstance,
110110
return instances, nil
111111
}
112112

113+
// MIG is not in the cache.
113114
err := c.fillMigInstances(migRef)
114115
if err != nil {
115116
return nil, err
@@ -133,16 +134,24 @@ func (c *cachingMigInfoProvider) GetMigForInstance(instanceRef GceRef) (Mig, err
133134
return nil, nil
134135
}
135136

137+
// Cache is cleared every loop.
138+
// If it's not empty, it's been refreshed this loop, and we don't want to refresh it again.
139+
if !c.cache.IsMigInstancesCacheEmpty(mig.GceRef()) {
140+
c.cache.MarkInstanceMigUnknown(instanceRef)
141+
return nil, nil
142+
}
143+
136144
err = c.fillMigInstances(mig.GceRef())
137145
if err != nil {
138146
return nil, err
139147
}
140-
148+
// Check in the cache again after it's been refilled
141149
mig, found, err = c.getCachedMigForInstance(instanceRef)
142150
if !found {
143151
c.cache.MarkInstanceMigUnknown(instanceRef)
144152
}
145153
return mig, err
154+
146155
}
147156

148157
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
@@ -1562,24 +1562,14 @@ func TestGetMigMachineType(t *testing.T) {
15621562
}
15631563
}
15641564

1565-
func TestMultipleGetMigInstanceCallsLimited(t *testing.T) {
1565+
func TestMultipleFillMigInstancesCallsLimited(t *testing.T) {
15661566
mig := &gceMig{
15671567
gceRef: GceRef{
15681568
Project: "project",
15691569
Zone: "zone",
15701570
Name: "base-instance-name",
15711571
},
15721572
}
1573-
instance := GceInstance{
1574-
Instance: cloudprovider.Instance{Id: "gce://project/zone/base-instance-name-abcd"}, NumericId: 1111,
1575-
}
1576-
instanceRef, err := GceRefFromProviderId(instance.Id)
1577-
assert.Nil(t, err)
1578-
instance2 := GceInstance{
1579-
Instance: cloudprovider.Instance{Id: "gce://project/zone/base-instance-name-abcd2"}, NumericId: 222,
1580-
}
1581-
instanceRef2, err := GceRefFromProviderId(instance2.Id)
1582-
assert.Nil(t, err)
15831573
now := time.Now()
15841574
for name, tc := range map[string]struct {
15851575
refreshRateDuration time.Duration
@@ -1628,10 +1618,10 @@ func TestMultipleGetMigInstanceCallsLimited(t *testing.T) {
16281618
timeProvider: ft,
16291619
}
16301620
ft.now = tc.firstCallTime
1631-
_, err = provider.GetMigForInstance(instanceRef)
1621+
err := provider.fillMigInstances(mig.GceRef())
16321622
assert.NoError(t, err)
16331623
ft.now = tc.secondCallTime
1634-
_, err = provider.GetMigForInstance(instanceRef2)
1624+
err = provider.fillMigInstances(mig.GceRef())
16351625
assert.NoError(t, err)
16361626
assert.Equal(t, tc.expectedCallsToFetchMigInstances, callCounter[mig.GceRef()])
16371627
})

0 commit comments

Comments
 (0)