From c7efcf1b753b67ff290ef23c406a165c904080b4 Mon Sep 17 00:00:00 2001 From: soup Date: Sun, 27 Apr 2025 14:02:39 +0200 Subject: [PATCH] feat(macros): implement template cache (#2049) * feat(macros): implement template cache * fix: typo in error * fix(macros): set DefaultTTL * fix: accidentally removed SetTimerResolution * revert: set NoTTL in MustParse --- internal/domain/macros.go | 39 +++++++++++++++++++++++----------- internal/domain/macros_test.go | 25 ++++++++++++++++++++++ 2 files changed, 52 insertions(+), 12 deletions(-) diff --git a/internal/domain/macros.go b/internal/domain/macros.go index d5aaf4b..e2789b1 100644 --- a/internal/domain/macros.go +++ b/internal/domain/macros.go @@ -10,11 +10,18 @@ import ( "time" "github.com/autobrr/autobrr/pkg/errors" + "github.com/autobrr/autobrr/pkg/ttlcache" "github.com/Masterminds/sprig/v3" "github.com/dustin/go-humanize" ) +var templateCache = ttlcache.New( + ttlcache.Options[string, *template.Template]{}. + SetTimerResolution(5 * time.Minute). + SetDefaultTTL(15 * time.Minute), +) + type Macro struct { Artists string Audio []string @@ -175,16 +182,19 @@ func (m Macro) Parse(text string) (string, error) { return "", nil } - // TODO implement template cache - - // setup template - tmpl, err := template.New("macro").Funcs(sprig.TxtFuncMap()).Parse(text) - if err != nil { - return "", errors.Wrap(err, "could parse macro template") + // get template from cache or create new + tmpl, ok := templateCache.Get(text) + if !ok { + var err error + tmpl, err = template.New("macro").Funcs(sprig.TxtFuncMap()).Parse(text) + if err != nil { + return "", errors.Wrap(err, "could not parse macro template") + } + templateCache.Set(text, tmpl, ttlcache.DefaultTTL) } var tpl bytes.Buffer - err = tmpl.Execute(&tpl, m) + err := tmpl.Execute(&tpl, m) if err != nil { return "", errors.Wrap(err, "could not parse macro") } @@ -198,14 +208,19 @@ func (m Macro) MustParse(text string) string { return "" } - // setup template - tmpl, err := template.New("macro").Funcs(sprig.TxtFuncMap()).Parse(text) - if err != nil { - return "" + // get template from cache or create new + tmpl, ok := templateCache.Get(text) + if !ok { + var err error + tmpl, err = template.New("macro").Funcs(sprig.TxtFuncMap()).Parse(text) + if err != nil { + return "" + } + templateCache.Set(text, tmpl, ttlcache.NoTTL) } var tpl bytes.Buffer - err = tmpl.Execute(&tpl, m) + err := tmpl.Execute(&tpl, m) if err != nil { return "" } diff --git a/internal/domain/macros_test.go b/internal/domain/macros_test.go index c20cdc3..741a8ec 100644 --- a/internal/domain/macros_test.go +++ b/internal/domain/macros_test.go @@ -290,3 +290,28 @@ func TestMacros_Parse(t *testing.T) { }) } } + +func TestMacros_TemplateCache(t *testing.T) { + t.Parallel() + + release := Release{ + TorrentName: "Test Movie 2024", + Year: 2024, + } + + m := NewMacro(release) + template := "{{.TorrentName}} ({{.Year}})" + + // parse and cache + got1, err := m.Parse(template) + assert.NoError(t, err) + assert.Equal(t, "Test Movie 2024 (2024)", got1) + + // use cached template + got2, err := m.Parse(template) + assert.NoError(t, err) + assert.Equal(t, "Test Movie 2024 (2024)", got2) + + _, ok := templateCache.Get(template) + assert.True(t, ok, "template should be in cache") +}