inline GC in expiring cache

This allows us to drop the background goroutine with negligable
difference in performance.

Kubernetes-commit: d16dde36a3edf5cdb89c5d5b56d4e3c9af849c1c
This commit is contained in:
Mike Danese 2019-11-15 17:04:28 -08:00 committed by Kubernetes Publisher
parent 5d4d05fd8a
commit 8d1e73feab
7 changed files with 25 additions and 63 deletions

View File

@ -17,7 +17,6 @@ limitations under the License.
package authenticatorfactory
import (
"context"
"errors"
"time"
@ -84,7 +83,7 @@ func (c DelegatingAuthenticatorConfig) New() (authenticator.Request, *spec.Secur
if err != nil {
return nil, nil, err
}
cachingTokenAuth := cache.New(context.TODO(), tokenAuth, false, c.CacheTTL, c.CacheTTL)
cachingTokenAuth := cache.New(tokenAuth, false, c.CacheTTL, c.CacheTTL)
authenticators = append(authenticators, bearertoken.New(cachingTokenAuth), websocket.NewProtocolAuthenticator(cachingTokenAuth))
securityDefinitions["BearerToken"] = &spec.SecurityScheme{

View File

@ -17,7 +17,6 @@ limitations under the License.
package cache
import (
"context"
"time"
utilcache "k8s.io/apimachinery/pkg/util/cache"
@ -28,10 +27,8 @@ type simpleCache struct {
cache *utilcache.Expiring
}
func newSimpleCache(ctx context.Context, clock clock.Clock) cache {
c := &simpleCache{cache: utilcache.NewExpiringWithClock(clock)}
go c.cache.Run(ctx)
return c
func newSimpleCache(clock clock.Clock) cache {
return &simpleCache{cache: utilcache.NewExpiringWithClock(clock)}
}
func (c *simpleCache) get(key string) (*cacheRecord, bool) {

View File

@ -17,7 +17,6 @@ limitations under the License.
package cache
import (
"context"
"fmt"
"math/rand"
"testing"
@ -31,9 +30,7 @@ import (
)
func TestSimpleCache(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
testCache(newSimpleCache(ctx, clock.RealClock{}), t)
testCache(newSimpleCache(clock.RealClock{}), t)
}
// Note: the performance profile of this benchmark may not match that in the production.
@ -42,22 +39,16 @@ func TestSimpleCache(t *testing.T) {
func BenchmarkCacheContentions(b *testing.B) {
for _, numKeys := range []int{1 << 8, 1 << 12, 1 << 16} {
b.Run(fmt.Sprintf("Simple/keys=%d", numKeys), func(b *testing.B) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
benchmarkCache(newSimpleCache(ctx, clock.RealClock{}), b, numKeys)
benchmarkCache(newSimpleCache(clock.RealClock{}), b, numKeys)
})
b.Run(fmt.Sprintf("Striped/keys=%d", numKeys), func(b *testing.B) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
benchmarkCache(newStripedCache(32, fnvHashFunc, func() cache { return newSimpleCache(ctx, clock.RealClock{}) }), b, numKeys)
benchmarkCache(newStripedCache(32, fnvHashFunc, func() cache { return newSimpleCache(clock.RealClock{}) }), b, numKeys)
})
}
}
func TestStripedCache(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
testCache(newStripedCache(32, fnvHashFunc, func() cache { return newSimpleCache(ctx, clock.RealClock{}) }), t)
testCache(newStripedCache(32, fnvHashFunc, func() cache { return newSimpleCache(clock.RealClock{}) }), t)
}
func benchmarkCache(cache cache, b *testing.B, numKeys int) {

View File

@ -64,11 +64,11 @@ type cache interface {
}
// New returns a token authenticator that caches the results of the specified authenticator. A ttl of 0 bypasses the cache.
func New(ctx context.Context, authenticator authenticator.Token, cacheErrs bool, successTTL, failureTTL time.Duration) authenticator.Token {
return newWithClock(ctx, authenticator, cacheErrs, successTTL, failureTTL, utilclock.RealClock{})
func New(authenticator authenticator.Token, cacheErrs bool, successTTL, failureTTL time.Duration) authenticator.Token {
return newWithClock(authenticator, cacheErrs, successTTL, failureTTL, utilclock.RealClock{})
}
func newWithClock(ctx context.Context, authenticator authenticator.Token, cacheErrs bool, successTTL, failureTTL time.Duration, clock utilclock.Clock) authenticator.Token {
func newWithClock(authenticator authenticator.Token, cacheErrs bool, successTTL, failureTTL time.Duration, clock utilclock.Clock) authenticator.Token {
randomCacheKey := make([]byte, 32)
if _, err := rand.Read(randomCacheKey); err != nil {
panic(err) // rand should never fail
@ -86,7 +86,7 @@ func newWithClock(ctx context.Context, authenticator authenticator.Token, cacheE
// used. Currently we advertise support 5k nodes and 10k
// namespaces; a 32k entry cache is therefore a 2x safety
// margin.
cache: newStripedCache(32, fnvHashFunc, func() cache { return newSimpleCache(ctx, clock) }),
cache: newStripedCache(32, fnvHashFunc, func() cache { return newSimpleCache(clock) }),
hashPool: &sync.Pool{
New: func() interface{} {

View File

@ -50,10 +50,7 @@ func TestCachedTokenAuthenticator(t *testing.T) {
})
fakeClock := utilclock.NewFakeClock(time.Now())
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
a := newWithClock(ctx, fakeAuth, true, time.Minute, 0, fakeClock)
a := newWithClock(fakeAuth, true, time.Minute, 0, fakeClock)
calledWithToken, resultUsers, resultOk, resultErr = []string{}, nil, false, nil
a.AuthenticateToken(context.Background(), "bad1")
@ -127,10 +124,7 @@ func TestCachedTokenAuthenticatorWithAudiences(t *testing.T) {
})
fakeClock := utilclock.NewFakeClock(time.Now())
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
a := newWithClock(ctx, fakeAuth, true, time.Minute, 0, fakeClock)
a := newWithClock(fakeAuth, true, time.Minute, 0, fakeClock)
resultUsers["audAusertoken1"] = &user.DefaultInfo{Name: "user1"}
resultUsers["audBusertoken1"] = &user.DefaultInfo{Name: "user1-different"}
@ -276,8 +270,6 @@ func (s *singleBenchmark) run(b *testing.B) {
}
func (s *singleBenchmark) bench(b *testing.B) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// Simulate slowness, qps limit, external service limitation, etc
const maxInFlight = 40
chokepoint := make(chan struct{}, maxInFlight)
@ -285,7 +277,6 @@ func (s *singleBenchmark) bench(b *testing.B) {
var lookups uint64
a := newWithClock(
ctx,
authenticator.TokenFunc(func(ctx context.Context, token string) (*authenticator.Response, bool, error) {
atomic.AddUint64(&lookups, 1)

View File

@ -170,7 +170,7 @@ func (m *mockV1Service) HTTPStatusCode() int { return m.statusCode }
// newV1TokenAuthenticator creates a temporary kubeconfig file from the provided
// arguments and attempts to load a new WebhookTokenAuthenticator from it.
func newV1TokenAuthenticator(ctx context.Context, serverURL string, clientCert, clientKey, ca []byte, cacheTime time.Duration, implicitAuds authenticator.Audiences) (authenticator.Token, error) {
func newV1TokenAuthenticator(serverURL string, clientCert, clientKey, ca []byte, cacheTime time.Duration, implicitAuds authenticator.Audiences) (authenticator.Token, error) {
tempfile, err := ioutil.TempFile("", "")
if err != nil {
return nil, err
@ -203,7 +203,7 @@ func newV1TokenAuthenticator(ctx context.Context, serverURL string, clientCert,
return nil, err
}
return cache.New(ctx, authn, false, cacheTime, cacheTime), nil
return cache.New(authn, false, cacheTime, cacheTime), nil
}
func TestV1TLSConfig(t *testing.T) {
@ -259,10 +259,7 @@ func TestV1TLSConfig(t *testing.T) {
}
defer server.Close()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
wh, err := newV1TokenAuthenticator(ctx, server.URL, tt.clientCert, tt.clientKey, tt.clientCA, 0, nil)
wh, err := newV1TokenAuthenticator(server.URL, tt.clientCert, tt.clientKey, tt.clientCA, 0, nil)
if err != nil {
t.Errorf("%s: failed to create client: %v", tt.test, err)
return
@ -485,14 +482,12 @@ func TestV1WebhookTokenAuthenticator(t *testing.T) {
token := "my-s3cr3t-t0ken"
for _, tt := range tests {
t.Run(tt.description, func(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
wh, err := newV1TokenAuthenticator(ctx, s.URL, clientCert, clientKey, caCert, 0, tt.implicitAuds)
wh, err := newV1TokenAuthenticator(s.URL, clientCert, clientKey, caCert, 0, tt.implicitAuds)
if err != nil {
t.Fatal(err)
}
ctx := context.Background()
if tt.reqAuds != nil {
ctx = authenticator.WithAudiences(ctx, tt.reqAuds)
}
@ -559,11 +554,8 @@ func TestV1WebhookCacheAndRetry(t *testing.T) {
}
defer s.Close()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// Create an authenticator that caches successful responses "forever" (100 days).
wh, err := newV1TokenAuthenticator(ctx, s.URL, clientCert, clientKey, caCert, 2400*time.Hour, nil)
wh, err := newV1TokenAuthenticator(s.URL, clientCert, clientKey, caCert, 2400*time.Hour, nil)
if err != nil {
t.Fatal(err)
}

View File

@ -172,7 +172,7 @@ func (m *mockV1beta1Service) HTTPStatusCode() int { return m.statusCode }
// newV1beta1TokenAuthenticator creates a temporary kubeconfig file from the provided
// arguments and attempts to load a new WebhookTokenAuthenticator from it.
func newV1beta1TokenAuthenticator(ctx context.Context, serverURL string, clientCert, clientKey, ca []byte, cacheTime time.Duration, implicitAuds authenticator.Audiences) (authenticator.Token, error) {
func newV1beta1TokenAuthenticator(serverURL string, clientCert, clientKey, ca []byte, cacheTime time.Duration, implicitAuds authenticator.Audiences) (authenticator.Token, error) {
tempfile, err := ioutil.TempFile("", "")
if err != nil {
return nil, err
@ -205,7 +205,7 @@ func newV1beta1TokenAuthenticator(ctx context.Context, serverURL string, clientC
return nil, err
}
return cache.New(ctx, authn, false, cacheTime, cacheTime), nil
return cache.New(authn, false, cacheTime, cacheTime), nil
}
func TestV1beta1TLSConfig(t *testing.T) {
@ -261,10 +261,7 @@ func TestV1beta1TLSConfig(t *testing.T) {
}
defer server.Close()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
wh, err := newV1beta1TokenAuthenticator(ctx, server.URL, tt.clientCert, tt.clientKey, tt.clientCA, 0, nil)
wh, err := newV1beta1TokenAuthenticator(server.URL, tt.clientCert, tt.clientKey, tt.clientCA, 0, nil)
if err != nil {
t.Errorf("%s: failed to create client: %v", tt.test, err)
return
@ -487,14 +484,12 @@ func TestV1beta1WebhookTokenAuthenticator(t *testing.T) {
token := "my-s3cr3t-t0ken"
for _, tt := range tests {
t.Run(tt.description, func(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
wh, err := newV1beta1TokenAuthenticator(ctx, s.URL, clientCert, clientKey, caCert, 0, tt.implicitAuds)
wh, err := newV1beta1TokenAuthenticator(s.URL, clientCert, clientKey, caCert, 0, tt.implicitAuds)
if err != nil {
t.Fatal(err)
}
ctx := context.Background()
if tt.reqAuds != nil {
ctx = authenticator.WithAudiences(ctx, tt.reqAuds)
}
@ -561,11 +556,8 @@ func TestV1beta1WebhookCacheAndRetry(t *testing.T) {
}
defer s.Close()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// Create an authenticator that caches successful responses "forever" (100 days).
wh, err := newV1beta1TokenAuthenticator(ctx, s.URL, clientCert, clientKey, caCert, 2400*time.Hour, nil)
wh, err := newV1beta1TokenAuthenticator(s.URL, clientCert, clientKey, caCert, 2400*time.Hour, nil)
if err != nil {
t.Fatal(err)
}