Skip to content

Commit 7df3f08

Browse files
committed
feat: implement caching for task and pipeline resolvers
This commit adds caching for bundle and git resolvers to reduce chances of being rate limited by registries and git forges. - Add cache interface and in-memory implementation - Add cache configuration options for bundle and git resolvers - Add documentation for cache configuration Signed-off-by: Brian Cook <[email protected]>
1 parent b86ab22 commit 7df3f08

File tree

17 files changed

+985
-84
lines changed

17 files changed

+985
-84
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Copyright 2024 The Tekton Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
# -----------------------------------------------------------------------------------
16+
# Resolver Cache Configuration
17+
#
18+
# By default, the resolver cache uses:
19+
# - 5 minutes ("5m") as the time-to-live (TTL) for cache entries
20+
# - 1000 entries as the maximum cache size
21+
#
22+
# You can override these defaults by setting the following keys in this ConfigMap:
23+
# - max-size: Set the maximum number of cache entries (e.g., "500")
24+
# - default-ttl: Set the default TTL for cache entries (e.g., "10m", "30s")
25+
#
26+
# If these values are missing or invalid, the defaults will be used.
27+
# -----------------------------------------------------------------------------------
28+
29+
apiVersion: v1
30+
kind: ConfigMap
31+
metadata:
32+
name: resolver-cache-config
33+
namespace: tekton-pipelines-resolvers
34+
labels:
35+
app.kubernetes.io/component: resolvers
36+
app.kubernetes.io/instance: default
37+
app.kubernetes.io/part-of: tekton-pipelines
38+
data:
39+
# The maximum number of entries in the cache
40+
max-size: "1000"
41+
# The default time-to-live for cache entries
42+
default-ttl: "5m"

docs/bundle-resolver.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ This Resolver responds to type `bundles`.
1919
| `bundle` | The bundle url pointing at the image to fetch | `gcr.io/tekton-releases/catalog/upstream/golang-build:0.1` |
2020
| `name` | The name of the resource to pull out of the bundle | `golang-build` |
2121
| `kind` | The resource kind to pull out of the bundle | `task` |
22+
| `cache` | Controls caching behavior for the resolved resource | `always`, `never`, `auto` |
2223

2324
## Requirements
2425

@@ -45,6 +46,16 @@ for the name, namespace and defaults that the resolver ships with.
4546
| `backoff-cap` | The maxumum backoff duration. If reached, remaining steps are zeroed.| `10s`, `20s` |
4647
| `default-kind` | The default layer kind in the bundle image. | `task`, `pipeline` |
4748

49+
### Caching Options
50+
51+
The bundle resolver supports caching of resolved resources to improve performance. The caching behavior can be configured using the `cache` option:
52+
53+
| Cache Value | Description |
54+
|-------------|-------------|
55+
| `always` | Always cache resolved resources. This is the most aggressive caching strategy and will cache all resolved resources regardless of their source. |
56+
| `never` | Never cache resolved resources. This disables caching completely. |
57+
| `auto` | Caching will only occur for bundles pulled by digest. (default) |
58+
4859
## Usage
4960

5061
### Task Resolution

docs/git-resolver.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ This Resolver responds to type `git`.
2626
| `pathInRepo` | Where to find the file in the repo. | `task/golang-build/0.3/golang-build.yaml` |
2727
| `serverURL` | An optional server URL (that includes the https:// prefix) to connect for API operations | `https:/github.mycompany.com` |
2828
| `scmType` | An optional SCM type to use for API operations | `github`, `gitlab`, `gitea` |
29+
| `cache` | Controls caching behavior for the resolved resource | `always`, `never`, `auto` |
2930

3031
## Requirements
3132

@@ -55,6 +56,16 @@ for the name, namespace and defaults that the resolver ships with.
5556
| `api-token-secret-namespace` | The namespace containing the token secret, if not `default`. | `other-namespace` |
5657
| `default-org` | The default organization to look for repositories under when using the authenticated API, if not specified in the resolver parameters. Optional. | `tektoncd`, `kubernetes` |
5758

59+
### Caching Options
60+
61+
The git resolver supports caching of resolved resources to improve performance. The caching behavior can be configured using the `cache` option:
62+
63+
| Cache Value | Description |
64+
|-------------|-------------|
65+
| `always` | Always cache resolved resources. This is the most aggressive caching strategy and will cache all resolved resources regardless of their source. |
66+
| `never` | Never cache resolved resources. This disables caching completely. |
67+
| `auto` | Caching will only occur when revision is a commit hash. (default) |
68+
5869
## Usage
5970

6071
The `git` resolver has two modes: cloning a repository with `git clone` (with

docs/resolution.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,18 @@ accompanying [resolver-template](./resolver-template).
3434
For a table of the interfaces and methods a resolver must implement
3535
along with those that are optional, see [resolver-reference.md](./resolver-reference.md).
3636

37+
## Resolver Cache Configuration
38+
39+
The resolver cache is used to improve performance by caching resolved resources for bundle and git resolver. By default, the cache uses:
40+
- 5 minutes ("5m") as the time-to-live (TTL) for cache entries
41+
- 1000 entries as the maximum cache size
42+
43+
You can override these defaults by editing the `resolver-cache-config.yaml` ConfigMap in the `tekton-pipelines-resolvers` namespace. Set the following keys:
44+
- `max-size`: Set the maximum number of cache entries (e.g., "500")
45+
- `default-ttl`: Set the default TTL for cache entries (e.g., "10m", "30s")
46+
47+
If these values are missing or invalid, the defaults will be used.
48+
3749
---
3850

3951
Except as otherwise noted, the content of this page is licensed under the
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
/*
2+
Copyright 2024 The Tekton Authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package cache
18+
19+
import (
20+
"crypto/sha256"
21+
"encoding/hex"
22+
"fmt"
23+
"strconv"
24+
"time"
25+
26+
"context"
27+
28+
v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1"
29+
"go.uber.org/zap"
30+
corev1 "k8s.io/api/core/v1"
31+
utilcache "k8s.io/apimachinery/pkg/util/cache"
32+
"knative.dev/pkg/logging"
33+
)
34+
35+
const (
36+
// DefaultMaxSize is the default size for the cache
37+
DefaultMaxSize = 1000
38+
39+
// ConfigMapName is the name of the ConfigMap containing cache configuration
40+
ConfigMapName = "resolver-cache-config"
41+
42+
// ConfigMapNamespace is the namespace of the ConfigMap
43+
ConfigMapNamespace = "tekton-pipelines-resolvers"
44+
)
45+
46+
var (
47+
// DefaultExpiration is the default expiration time for cache entries
48+
DefaultExpiration = 5 * time.Minute
49+
)
50+
51+
// ResolverCache is a wrapper around utilcache.LRUExpireCache that provides
52+
// type-safe methods for caching resolver results.
53+
type ResolverCache struct {
54+
cache *utilcache.LRUExpireCache
55+
logger *zap.SugaredLogger
56+
}
57+
58+
// NewResolverCache creates a new ResolverCache with the given expiration time and max size
59+
func NewResolverCache(maxSize int) *ResolverCache {
60+
return &ResolverCache{
61+
cache: utilcache.NewLRUExpireCache(maxSize),
62+
}
63+
}
64+
65+
// InitializeFromConfigMap initializes the cache with configuration from a ConfigMap
66+
func (c *ResolverCache) InitializeFromConfigMap(configMap *corev1.ConfigMap) {
67+
// Set defaults
68+
maxSize := DefaultMaxSize
69+
ttl := DefaultExpiration
70+
71+
if configMap != nil {
72+
// Parse max size
73+
if maxSizeStr, ok := configMap.Data["max-size"]; ok {
74+
if parsed, err := strconv.Atoi(maxSizeStr); err == nil && parsed > 0 {
75+
maxSize = parsed
76+
}
77+
}
78+
79+
// Parse default TTL
80+
if ttlStr, ok := configMap.Data["default-ttl"]; ok {
81+
if parsed, err := time.ParseDuration(ttlStr); err == nil && parsed > 0 {
82+
ttl = parsed
83+
}
84+
}
85+
}
86+
87+
c.cache = utilcache.NewLRUExpireCache(maxSize)
88+
DefaultExpiration = ttl
89+
}
90+
91+
// InitializeLogger initializes the logger for the cache using the provided context
92+
func (c *ResolverCache) InitializeLogger(ctx context.Context) {
93+
if c.logger == nil {
94+
c.logger = logging.FromContext(ctx)
95+
}
96+
}
97+
98+
// Get retrieves a value from the cache.
99+
func (c *ResolverCache) Get(key string) (interface{}, bool) {
100+
value, found := c.cache.Get(key)
101+
if c.logger != nil {
102+
if found {
103+
c.logger.Infow("Cache hit", "key", key)
104+
} else {
105+
c.logger.Infow("Cache miss", "key", key)
106+
}
107+
}
108+
return value, found
109+
}
110+
111+
// Add adds a value to the cache with the default expiration time.
112+
func (c *ResolverCache) Add(key string, value interface{}) {
113+
if c.logger != nil {
114+
c.logger.Infow("Adding to cache", "key", key, "expiration", DefaultExpiration)
115+
}
116+
c.cache.Add(key, value, DefaultExpiration)
117+
}
118+
119+
// Remove removes a value from the cache.
120+
func (c *ResolverCache) Remove(key string) {
121+
if c.logger != nil {
122+
c.logger.Infow("Removing from cache", "key", key)
123+
}
124+
c.cache.Remove(key)
125+
}
126+
127+
// AddWithExpiration adds a value to the cache with a custom expiration time
128+
func (c *ResolverCache) AddWithExpiration(key string, value interface{}, expiration time.Duration) {
129+
if c.logger != nil {
130+
c.logger.Infow("Adding to cache with custom expiration", "key", key, "expiration", expiration)
131+
}
132+
c.cache.Add(key, value, expiration)
133+
}
134+
135+
// globalCache is the global instance of ResolverCache
136+
var globalCache = NewResolverCache(DefaultMaxSize)
137+
138+
// GetGlobalCache returns the global cache instance.
139+
func GetGlobalCache() *ResolverCache {
140+
return globalCache
141+
}
142+
143+
// GenerateCacheKey generates a cache key for the given resolver type and parameters.
144+
func GenerateCacheKey(resolverType string, params []v1.Param) (string, error) {
145+
// Create a deterministic string representation of the parameters
146+
paramStr := resolverType + ":"
147+
for _, p := range params {
148+
paramStr += fmt.Sprintf("%s=%s;", p.Name, p.Value.StringVal)
149+
}
150+
151+
// Generate a SHA-256 hash of the parameter string
152+
hash := sha256.Sum256([]byte(paramStr))
153+
return hex.EncodeToString(hash[:]), nil
154+
}

0 commit comments

Comments
 (0)