blob: 4a7208bddeb4507b0a196384529d8290faeb5a4c [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package mesh
import (
"context"
"time"
)
import (
"github.com/patrickmn/go-cache"
)
import (
"github.com/apache/dubbo-kubernetes/pkg/xds/cache/once"
xds_context "github.com/apache/dubbo-kubernetes/pkg/xds/context"
)
// Cache is needed to share and cache Hashes among goroutines which
// reconcile Dataplane's state. Calculating hash is a heavy operation
// that requires fetching all the resources belonging to the Mesh.
type Cache struct {
// cache is used for caching a context and ignoring mesh changes for up to a
// short expiration time.
cache *once.Cache
// hashCache keeps a cached context, for a much longer time, that is only reused
// when the mesh hasn't changed.
hashCache *cache.Cache
meshContextBuilder xds_context.MeshContextBuilder
}
// cleanupTime is the time after which the mesh context is removed from
// the longer TTL cache.
// It exists to ensure contexts of deleted Meshes are eventually cleaned up.
const cleanupTime = time.Minute
func NewCache(
expirationTime time.Duration,
meshContextBuilder xds_context.MeshContextBuilder,
) (*Cache, error) {
c, err := once.New(expirationTime, "mesh_cache")
if err != nil {
return nil, err
}
return &Cache{
cache: c,
meshContextBuilder: meshContextBuilder,
hashCache: cache.New(cleanupTime, time.Duration(int64(float64(cleanupTime)*0.9))),
}, nil
}
func (c *Cache) GetMeshContext(ctx context.Context, mesh string) (xds_context.MeshContext, error) {
// Check our short TTL cache for a context, ignoring whether there have been
// changes since it was generated.
elt, err := c.cache.GetOrRetrieve(ctx, mesh, once.RetrieverFunc(func(ctx context.Context, key string) (interface{}, error) {
// Check hashCache first for an existing mesh latestContext
var latestContext *xds_context.MeshContext
if cached, ok := c.hashCache.Get(mesh); ok {
latestContext = cached.(*xds_context.MeshContext)
}
// Rebuild the context only if the hash has changed
var err error
latestContext, err = c.meshContextBuilder.BuildIfChanged(ctx, mesh, latestContext)
if err != nil {
return xds_context.MeshContext{}, err
}
// By always setting the mesh context, we refresh the TTL
// with the effect that often used contexts remain in the cache while no
// longer used contexts are evicted.
c.hashCache.SetDefault(mesh, latestContext)
return *latestContext, nil
}))
if err != nil {
return xds_context.MeshContext{}, err
}
return elt.(xds_context.MeshContext), nil
}