Skip to content

Commit 0bdc0df

Browse files
committed
add RWMutex to ensure that the template cache is locked when being written to
Signed-off-by: malpou <[email protected]>
1 parent 16b34a9 commit 0bdc0df

File tree

2 files changed

+309
-14
lines changed

2 files changed

+309
-14
lines changed

interceptor/handler/placeholder.go

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"fmt"
77
"html/template"
88
"net/http"
9+
"sync"
910
"time"
1011

1112
"github.com/kedacore/http-add-on/operator/apis/http/v1alpha1"
@@ -76,11 +77,19 @@ const defaultPlaceholderTemplate = `<!DOCTYPE html>
7677
</body>
7778
</html>`
7879

80+
// cacheEntry stores a template along with resource generation info for cache invalidation
81+
type cacheEntry struct {
82+
template *template.Template
83+
hsoGeneration int64
84+
configMapVersion string
85+
}
86+
7987
// PlaceholderHandler handles serving placeholder pages during scale-from-zero
8088
type PlaceholderHandler struct {
8189
k8sClient kubernetes.Interface
8290
routingTable routing.Table
83-
templateCache map[string]*template.Template
91+
templateCache map[string]*cacheEntry
92+
cacheMutex sync.RWMutex
8493
defaultTmpl *template.Template
8594
}
8695

@@ -103,7 +112,7 @@ func NewPlaceholderHandler(k8sClient kubernetes.Interface, routingTable routing.
103112
return &PlaceholderHandler{
104113
k8sClient: k8sClient,
105114
routingTable: routingTable,
106-
templateCache: make(map[string]*template.Template),
115+
templateCache: make(map[string]*cacheEntry),
107116
defaultTmpl: defaultTmpl,
108117
}, nil
109118
}
@@ -165,29 +174,45 @@ func (h *PlaceholderHandler) getTemplate(ctx context.Context, hso *v1alpha1.HTTP
165174

166175
if config.Content != "" {
167176
cacheKey := fmt.Sprintf("%s/%s/inline", hso.Namespace, hso.Name)
168-
if tmpl, ok := h.templateCache[cacheKey]; ok {
169-
return tmpl, nil
177+
178+
h.cacheMutex.RLock()
179+
entry, ok := h.templateCache[cacheKey]
180+
if ok && entry.hsoGeneration == hso.Generation {
181+
h.cacheMutex.RUnlock()
182+
return entry.template, nil
170183
}
184+
h.cacheMutex.RUnlock()
171185

172186
tmpl, err := template.New("inline").Parse(config.Content)
173187
if err != nil {
174188
return nil, err
175189
}
176-
h.templateCache[cacheKey] = tmpl
190+
191+
h.cacheMutex.Lock()
192+
h.templateCache[cacheKey] = &cacheEntry{
193+
template: tmpl,
194+
hsoGeneration: hso.Generation,
195+
}
196+
h.cacheMutex.Unlock()
177197
return tmpl, nil
178198
}
179199

180200
if config.ContentConfigMap != "" {
181201
cacheKey := fmt.Sprintf("%s/%s/cm/%s", hso.Namespace, hso.Name, config.ContentConfigMap)
182-
if tmpl, ok := h.templateCache[cacheKey]; ok {
183-
return tmpl, nil
184-
}
185202

186203
cm, err := h.k8sClient.CoreV1().ConfigMaps(hso.Namespace).Get(ctx, config.ContentConfigMap, metav1.GetOptions{})
187204
if err != nil {
188205
return nil, fmt.Errorf("failed to get ConfigMap %s: %w", config.ContentConfigMap, err)
189206
}
190207

208+
h.cacheMutex.RLock()
209+
entry, ok := h.templateCache[cacheKey]
210+
if ok && entry.hsoGeneration == hso.Generation && entry.configMapVersion == cm.ResourceVersion {
211+
h.cacheMutex.RUnlock()
212+
return entry.template, nil
213+
}
214+
h.cacheMutex.RUnlock()
215+
191216
key := config.ContentConfigMapKey
192217
if key == "" {
193218
key = "template.html"
@@ -202,7 +227,14 @@ func (h *PlaceholderHandler) getTemplate(ctx context.Context, hso *v1alpha1.HTTP
202227
if err != nil {
203228
return nil, err
204229
}
205-
h.templateCache[cacheKey] = tmpl
230+
231+
h.cacheMutex.Lock()
232+
h.templateCache[cacheKey] = &cacheEntry{
233+
template: tmpl,
234+
hsoGeneration: hso.Generation,
235+
configMapVersion: cm.ResourceVersion,
236+
}
237+
h.cacheMutex.Unlock()
206238
return tmpl, nil
207239
}
208240

0 commit comments

Comments
 (0)