Only invoke a given cached partial once

Note that this is backed by a LRU cache (which we soon shall see more usage of), so if you're a heavy user of cached partials it may be evicted and
refreshed if needed. But in most cases every partial is only invoked once.

This commit also adds a timeout (the global `timeout` config option) to make infinite recursion in partials
easier to reason about.

```
name              old time/op    new time/op    delta
IncludeCached-10    8.92ms ± 0%    8.48ms ± 1%   -4.87%  (p=0.016 n=4+5)

name              old alloc/op   new alloc/op   delta
IncludeCached-10    6.65MB ± 0%    5.17MB ± 0%  -22.32%  (p=0.002 n=6+6)

name              old allocs/op  new allocs/op  delta
IncludeCached-10      117k ± 0%       71k ± 0%  -39.44%  (p=0.002 n=6+6)
```

Closes #4086
Updates #9588
This commit is contained in:
Bjørn Erik Pedersen
2023-01-24 20:57:15 +01:00
parent 93ed6e447a
commit 4ef9baf5bd
21 changed files with 346 additions and 203 deletions

View File

@@ -33,6 +33,7 @@ import (
color_extractor "github.com/marekm4/color-extractor"
"github.com/gohugoio/hugo/common/paths"
"github.com/gohugoio/hugo/identity"
"github.com/disintegration/gift"
@@ -278,7 +279,7 @@ func (i *imageResource) Filter(filters ...any) (images.ImageResource, error) {
gfilters = append(gfilters, images.ToFilters(f)...)
}
conf.Key = helpers.HashString(gfilters)
conf.Key = identity.HashString(gfilters)
conf.TargetFormat = i.Format
return i.doWithImageConfig(conf, func(src image.Image) (image.Image, error) {
@@ -430,7 +431,7 @@ func (i *imageResource) getImageMetaCacheTargetPath() string {
}
p1, _ := paths.FileAndExt(df.file)
h, _ := i.hash()
idStr := helpers.HashString(h, i.size(), imageMetaVersionNumber, cfgHash)
idStr := identity.HashString(h, i.size(), imageMetaVersionNumber, cfgHash)
p := path.Join(df.dir, fmt.Sprintf("%s_%s.json", p1, idStr))
return p
}

View File

@@ -19,7 +19,7 @@ import (
"strconv"
"strings"
"github.com/gohugoio/hugo/helpers"
"github.com/gohugoio/hugo/identity"
"github.com/gohugoio/hugo/media"
"errors"
@@ -139,7 +139,7 @@ func DecodeConfig(m map[string]any) (ImagingConfig, error) {
i := ImagingConfig{
Cfg: defaultImaging,
CfgHash: helpers.HashString(m),
CfgHash: identity.HashString(m),
}
if err := mapstructure.WeakDecode(m, &i.Cfg); err != nil {

View File

@@ -16,9 +16,8 @@ package images
import (
"testing"
"github.com/gohugoio/hugo/helpers"
qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/identity"
)
func TestFilterHash(t *testing.T) {
@@ -26,8 +25,8 @@ func TestFilterHash(t *testing.T) {
f := &Filters{}
c.Assert(helpers.HashString(f.Grayscale()), qt.Equals, helpers.HashString(f.Grayscale()))
c.Assert(helpers.HashString(f.Grayscale()), qt.Not(qt.Equals), helpers.HashString(f.Invert()))
c.Assert(helpers.HashString(f.Gamma(32)), qt.Not(qt.Equals), helpers.HashString(f.Gamma(33)))
c.Assert(helpers.HashString(f.Gamma(32)), qt.Equals, helpers.HashString(f.Gamma(32)))
c.Assert(identity.HashString(f.Grayscale()), qt.Equals, identity.HashString(f.Grayscale()))
c.Assert(identity.HashString(f.Grayscale()), qt.Not(qt.Equals), identity.HashString(f.Invert()))
c.Assert(identity.HashString(f.Gamma(32)), qt.Not(qt.Equals), identity.HashString(f.Gamma(33)))
c.Assert(identity.HashString(f.Gamma(32)), qt.Equals, identity.HashString(f.Gamma(32)))
}

View File

@@ -13,7 +13,7 @@
package internal
import "github.com/gohugoio/hugo/helpers"
import "github.com/gohugoio/hugo/identity"
// ResourceTransformationKey are provided by the different transformation implementations.
// It identifies the transformation (name) and its configuration (elements).
@@ -38,5 +38,5 @@ func (k ResourceTransformationKey) Value() string {
return k.Name
}
return k.Name + "_" + helpers.HashString(k.elements...)
return k.Name + "_" + identity.HashString(k.elements...)
}

View File

@@ -18,6 +18,7 @@ import (
"time"
"github.com/gohugoio/hugo/common/maps"
"github.com/gohugoio/hugo/identity"
"github.com/gohugoio/hugo/config"
@@ -75,6 +76,9 @@ type Site interface {
// Returns a map of all the data inside /data.
Data() map[string]any
// Returns the identity of this site.
GetIdentity() identity.Identity
}
// Sites represents an ordered list of sites (languages).
@@ -117,6 +121,10 @@ func (t testSite) Current() Site {
return t
}
func (t testSite) GetIdentity() identity.Identity {
return identity.KeyValueIdentity{Key: "site", Value: t.l.Lang}
}
func (t testSite) IsServer() bool {
return false
}

View File

@@ -30,7 +30,7 @@ import (
"github.com/gohugoio/hugo/common/hugio"
"github.com/gohugoio/hugo/common/maps"
"github.com/gohugoio/hugo/common/types"
"github.com/gohugoio/hugo/helpers"
"github.com/gohugoio/hugo/identity"
"github.com/gohugoio/hugo/media"
"github.com/gohugoio/hugo/resources"
"github.com/gohugoio/hugo/resources/resource"
@@ -235,9 +235,9 @@ func (c *Client) validateFromRemoteArgs(uri string, options fromRemoteOptions) e
func calculateResourceID(uri string, optionsm map[string]any) string {
if key, found := maps.LookupEqualFold(optionsm, "key"); found {
return helpers.HashString(key)
return identity.HashString(key)
}
return helpers.HashString(uri, optionsm)
return identity.HashString(uri, optionsm)
}
func addDefaultHeaders(req *http.Request) {