istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pilot/pkg/model/xds_cache.go (about) 1 // Copyright Istio 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 // http://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 package model 16 17 import ( 18 "time" 19 20 discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" 21 22 "istio.io/istio/pilot/pkg/features" 23 "istio.io/istio/pkg/config/schema/kind" 24 "istio.io/istio/pkg/util/sets" 25 ) 26 27 type XdsCacheImpl struct { 28 cds typedXdsCache[uint64] 29 eds typedXdsCache[uint64] 30 rds typedXdsCache[uint64] 31 sds typedXdsCache[string] 32 } 33 34 // XdsCache interface defines a store for caching XDS responses. 35 // All operations are thread safe. 36 type XdsCache interface { 37 // Run starts a background thread to flush evicted indexes periodically. 38 Run(stop <-chan struct{}) 39 // Add adds the given XdsCacheEntry with the value for the given pushContext to the cache. 40 // If the cache has been updated to a newer push context, the write will be dropped silently. 41 // This ensures stale data does not overwrite fresh data when dealing with concurrent 42 // writers. 43 Add(entry XdsCacheEntry, pushRequest *PushRequest, value *discovery.Resource) 44 // Get retrieves the cached value if it exists. 45 Get(entry XdsCacheEntry) *discovery.Resource 46 // Clear removes the cache entries that are dependent on the configs passed. 47 Clear(sets.Set[ConfigKey]) 48 // ClearAll clears the entire cache. 49 ClearAll() 50 // Keys returns all currently configured keys for the type. This is for testing/debug only 51 Keys(t string) []any 52 // Snapshot returns a snapshot of all values. This is for testing/debug only 53 Snapshot() []*discovery.Resource 54 } 55 56 // XdsCacheEntry interface defines functions that should be implemented by 57 // resources that can be cached. 58 type XdsCacheEntry interface { 59 // Type indicates the type of Xds resource being cached like CDS. 60 Type() string 61 // Key is the key to be used in cache. 62 Key() any 63 // DependentConfigs is config items that this cache key is dependent on. 64 // Whenever these configs change, we should invalidate this cache entry. 65 DependentConfigs() []ConfigHash 66 // Cacheable indicates whether this entry is valid for cache. For example 67 // for EDS to be cacheable, the Endpoint should have corresponding service. 68 Cacheable() bool 69 } 70 71 const ( 72 CDSType = "cds" 73 EDSType = "eds" 74 RDSType = "rds" 75 SDSType = "sds" 76 ) 77 78 // NewXdsCache returns an instance of a cache. 79 func NewXdsCache() XdsCache { 80 cache := XdsCacheImpl{ 81 eds: newTypedXdsCache[uint64](), 82 } 83 if features.EnableCDSCaching { 84 cache.cds = newTypedXdsCache[uint64]() 85 } else { 86 cache.cds = disabledCache[uint64]{} 87 } 88 if features.EnableRDSCaching { 89 cache.rds = newTypedXdsCache[uint64]() 90 } else { 91 cache.rds = disabledCache[uint64]{} 92 } 93 94 cache.sds = newTypedXdsCache[string]() 95 96 return cache 97 } 98 99 func (x XdsCacheImpl) Run(stop <-chan struct{}) { 100 interval := features.XDSCacheIndexClearInterval 101 go func() { 102 ticker := time.NewTicker(interval) 103 defer ticker.Stop() 104 for { 105 select { 106 case <-ticker.C: 107 x.cds.Flush() 108 x.eds.Flush() 109 x.rds.Flush() 110 x.sds.Flush() 111 case <-stop: 112 return 113 } 114 } 115 }() 116 } 117 118 func (x XdsCacheImpl) Add(entry XdsCacheEntry, pushRequest *PushRequest, value *discovery.Resource) { 119 if !entry.Cacheable() { 120 return 121 } 122 k := entry.Key() 123 switch entry.Type() { 124 case CDSType: 125 key := k.(uint64) 126 x.cds.Add(key, entry, pushRequest, value) 127 case EDSType: 128 key := k.(uint64) 129 x.eds.Add(key, entry, pushRequest, value) 130 case SDSType: 131 key := k.(string) 132 x.sds.Add(key, entry, pushRequest, value) 133 case RDSType: 134 key := k.(uint64) 135 x.rds.Add(key, entry, pushRequest, value) 136 default: 137 log.Errorf("unknown type %s", entry.Type()) 138 } 139 } 140 141 func (x XdsCacheImpl) Get(entry XdsCacheEntry) *discovery.Resource { 142 if !entry.Cacheable() { 143 return nil 144 } 145 146 k := entry.Key() 147 switch entry.Type() { 148 case CDSType: 149 key := k.(uint64) 150 return x.cds.Get(key) 151 case EDSType: 152 key := k.(uint64) 153 return x.eds.Get(key) 154 case SDSType: 155 key := k.(string) 156 return x.sds.Get(key) 157 case RDSType: 158 key := k.(uint64) 159 return x.rds.Get(key) 160 default: 161 log.Errorf("unknown type %s", entry.Type()) 162 return nil 163 } 164 } 165 166 func (x XdsCacheImpl) Clear(s sets.Set[ConfigKey]) { 167 x.cds.Clear(s) 168 // clear all EDS cache for PA change 169 if HasConfigsOfKind(s, kind.PeerAuthentication) { 170 x.eds.ClearAll() 171 } else { 172 x.eds.Clear(s) 173 } 174 x.rds.Clear(s) 175 x.sds.Clear(s) 176 } 177 178 func (x XdsCacheImpl) ClearAll() { 179 x.cds.ClearAll() 180 x.eds.ClearAll() 181 x.rds.ClearAll() 182 x.sds.ClearAll() 183 } 184 185 func (x XdsCacheImpl) Keys(t string) []any { 186 switch t { 187 case CDSType: 188 keys := x.cds.Keys() 189 return convertToAnySlices(keys) 190 case EDSType: 191 keys := x.eds.Keys() 192 return convertToAnySlices(keys) 193 case SDSType: 194 keys := x.sds.Keys() 195 return convertToAnySlices(keys) 196 case RDSType: 197 keys := x.rds.Keys() 198 return convertToAnySlices(keys) 199 default: 200 return nil 201 } 202 } 203 204 func convertToAnySlices[K comparable](in []K) []any { 205 out := make([]any, len(in)) 206 for i, k := range in { 207 out[i] = k 208 } 209 return out 210 } 211 212 func (x XdsCacheImpl) Snapshot() []*discovery.Resource { 213 var out []*discovery.Resource 214 out = append(out, x.cds.Snapshot()...) 215 out = append(out, x.eds.Snapshot()...) 216 out = append(out, x.rds.Snapshot()...) 217 out = append(out, x.sds.Snapshot()...) 218 return out 219 } 220 221 // DisabledCache is a cache that is always empty 222 type DisabledCache struct{} 223 224 func (d DisabledCache) Run(stop <-chan struct{}) { 225 } 226 227 func (d DisabledCache) Add(entry XdsCacheEntry, pushRequest *PushRequest, value *discovery.Resource) { 228 } 229 230 func (d DisabledCache) Get(entry XdsCacheEntry) *discovery.Resource { 231 return nil 232 } 233 234 func (d DisabledCache) Clear(s sets.Set[ConfigKey]) { 235 } 236 237 func (d DisabledCache) ClearAll() { 238 } 239 240 func (d DisabledCache) Keys(t string) []any { 241 return nil 242 } 243 244 func (d DisabledCache) Snapshot() []*discovery.Resource { 245 return nil 246 } 247 248 var _ XdsCache = &DisabledCache{}