Fix Processed images count regression for multiple languages

Fixes #11002
This commit is contained in:
Bjørn Erik Pedersen 2023-05-28 11:35:00 +02:00
parent 43f1282e73
commit fd099331ec
8 changed files with 83 additions and 24 deletions

5
deps/deps.go vendored
View file

@ -189,10 +189,13 @@ func (d *Deps) Init() error {
} }
var common *resources.SpecCommon var common *resources.SpecCommon
var imageCache *resources.ImageCache
if d.ResourceSpec != nil { if d.ResourceSpec != nil {
common = d.ResourceSpec.SpecCommon common = d.ResourceSpec.SpecCommon
imageCache = d.ResourceSpec.ImageCache
} }
resourceSpec, err := resources.NewSpec(d.PathSpec, common, d.BuildState, d.Log, d, d.ExecHelper)
resourceSpec, err := resources.NewSpec(d.PathSpec, common, imageCache, d.BuildState, d.Log, d, d.ExecHelper)
if err != nil { if err != nil {
return fmt.Errorf("failed to create resource spec: %w", err) return fmt.Errorf("failed to create resource spec: %w", err)
} }

View file

@ -370,12 +370,15 @@ var commonTestScriptsParam = testscript.Params{
} }
func testSetupFunc() func(env *testscript.Env) error { func testSetupFunc() func(env *testscript.Env) error {
sourceDir, _ := os.Getwd()
return func(env *testscript.Env) error { return func(env *testscript.Env) error {
var keyVals []string var keyVals []string
keyVals = append(keyVals, "HUGO_TESTRUN", "true") keyVals = append(keyVals, "HUGO_TESTRUN", "true")
hugoCachedDir := filepath.Join(env.WorkDir, "hugocache") hugoCachedDir := filepath.Join(env.WorkDir, "hugocache")
keyVals = append(keyVals, "HUGO_CACHEDIR", hugoCachedDir) keyVals = append(keyVals, "HUGO_CACHEDIR", hugoCachedDir)
keyVals = append(keyVals, "SOURCE", sourceDir)
goVersion := runtime.Version() goVersion := runtime.Version()
// Strip all but the major and minor version. // Strip all but the major and minor version.
goVersion = regexp.MustCompile(`^go(\d+\.\d+)`).FindStringSubmatch(goVersion)[1] goVersion = regexp.MustCompile(`^go(\d+\.\d+)`).FindStringSubmatch(goVersion)[1]

View file

@ -126,7 +126,7 @@ func (i *imageResource) getExif() *exif.ExifInfo {
return enc.Encode(i.meta) return enc.Encode(i.meta)
} }
_, i.metaInitErr = i.getSpec().imageCache.fileCache.ReadOrCreate(key, read, create) _, i.metaInitErr = i.getSpec().ImageCache.fileCache.ReadOrCreate(key, read, create)
}) })
if i.metaInitErr != nil { if i.metaInitErr != nil {
@ -296,7 +296,7 @@ const imageProcWorkers = 1
var imageProcSem = make(chan bool, imageProcWorkers) var imageProcSem = make(chan bool, imageProcWorkers)
func (i *imageResource) doWithImageConfig(conf images.ImageConfig, f func(src image.Image) (image.Image, error)) (images.ImageResource, error) { func (i *imageResource) doWithImageConfig(conf images.ImageConfig, f func(src image.Image) (image.Image, error)) (images.ImageResource, error) {
img, err := i.getSpec().imageCache.getOrCreate(i, conf, func() (*imageResource, image.Image, error) { img, err := i.getSpec().ImageCache.getOrCreate(i, conf, func() (*imageResource, image.Image, error) {
imageProcSem <- true imageProcSem <- true
defer func() { defer func() {
<-imageProcSem <-imageProcSem

View file

@ -26,16 +26,27 @@ import (
"github.com/gohugoio/hugo/helpers" "github.com/gohugoio/hugo/helpers"
) )
type imageCache struct { // ImageCache is a cache for image resources. The backing caches are shared between all sites.
type ImageCache struct {
pathSpec *helpers.PathSpec pathSpec *helpers.PathSpec
fileCache *filecache.Cache fileCache *filecache.Cache
*imageCacheStore
}
type imageCacheStore struct {
mu sync.RWMutex mu sync.RWMutex
store map[string]*resourceAdapter store map[string]*resourceAdapter
} }
func (c *imageCache) deleteIfContains(s string) { // WithPathSpec returns a copy of the ImageCache with the given PathSpec set.
func (c ImageCache) WithPathSpec(ps *helpers.PathSpec) *ImageCache {
c.pathSpec = ps
return &c
}
func (c *ImageCache) deleteIfContains(s string) {
c.mu.Lock() c.mu.Lock()
defer c.mu.Unlock() defer c.mu.Unlock()
s = c.normalizeKeyBase(s) s = c.normalizeKeyBase(s)
@ -48,21 +59,21 @@ func (c *imageCache) deleteIfContains(s string) {
// The cache key is a lowercase path with Unix style slashes and it always starts with // The cache key is a lowercase path with Unix style slashes and it always starts with
// a leading slash. // a leading slash.
func (c *imageCache) normalizeKey(key string) string { func (c *ImageCache) normalizeKey(key string) string {
return "/" + c.normalizeKeyBase(key) return "/" + c.normalizeKeyBase(key)
} }
func (c *imageCache) normalizeKeyBase(key string) string { func (c *ImageCache) normalizeKeyBase(key string) string {
return strings.Trim(strings.ToLower(filepath.ToSlash(key)), "/") return strings.Trim(strings.ToLower(filepath.ToSlash(key)), "/")
} }
func (c *imageCache) clear() { func (c *ImageCache) clear() {
c.mu.Lock() c.mu.Lock()
defer c.mu.Unlock() defer c.mu.Unlock()
c.store = make(map[string]*resourceAdapter) c.store = make(map[string]*resourceAdapter)
} }
func (c *imageCache) getOrCreate( func (c *ImageCache) getOrCreate(
parent *imageResource, conf images.ImageConfig, parent *imageResource, conf images.ImageConfig,
createImage func() (*imageResource, image.Image, error)) (*resourceAdapter, error) { createImage func() (*imageResource, image.Image, error)) (*resourceAdapter, error) {
relTarget := parent.relTargetPathFromConfig(conf) relTarget := parent.relTargetPathFromConfig(conf)
@ -163,6 +174,6 @@ func (c *imageCache) getOrCreate(
return imgAdapter, nil return imgAdapter, nil
} }
func newImageCache(fileCache *filecache.Cache, ps *helpers.PathSpec) *imageCache { func newImageCache(fileCache *filecache.Cache, ps *helpers.PathSpec) *ImageCache {
return &imageCache{fileCache: fileCache, pathSpec: ps, store: make(map[string]*resourceAdapter)} return &ImageCache{fileCache: fileCache, pathSpec: ps, imageCacheStore: &imageCacheStore{store: make(map[string]*resourceAdapter)}}
} }

View file

@ -51,6 +51,7 @@ import (
func NewSpec( func NewSpec(
s *helpers.PathSpec, s *helpers.PathSpec,
common *SpecCommon, // may be nil common *SpecCommon, // may be nil
imageCache *ImageCache, // may be nil
incr identity.Incrementer, incr identity.Incrementer,
logger loggers.Logger, logger loggers.Logger,
errorHandler herrors.ErrorSender, errorHandler herrors.ErrorSender,
@ -90,11 +91,6 @@ func NewSpec(
PostProcessResources: make(map[string]postpub.PostPublishedResource), PostProcessResources: make(map[string]postpub.PostPublishedResource),
JSConfigBuilder: jsconfig.NewBuilder(), JSConfigBuilder: jsconfig.NewBuilder(),
}, },
imageCache: newImageCache(
fileCaches.ImageCache(),
s,
),
ResourceCache: &ResourceCache{ ResourceCache: &ResourceCache{
fileCache: fileCaches.AssetsCache(), fileCache: fileCaches.AssetsCache(),
cache: make(map[string]any), cache: make(map[string]any),
@ -103,11 +99,22 @@ func NewSpec(
} }
} }
if imageCache == nil {
imageCache = newImageCache(
fileCaches.ImageCache(),
s,
)
} else {
imageCache = imageCache.WithPathSpec(s)
}
rs := &Spec{ rs := &Spec{
PathSpec: s, PathSpec: s,
Logger: logger, Logger: logger,
ErrorSender: errorHandler, ErrorSender: errorHandler,
imaging: imaging, imaging: imaging,
ImageCache: imageCache,
ExecHelper: execHelper, ExecHelper: execHelper,
Permalinks: permalinks, Permalinks: permalinks,
@ -128,6 +135,8 @@ type Spec struct {
Permalinks page.PermalinkExpander Permalinks page.PermalinkExpander
ImageCache *ImageCache
// Holds default filter settings etc. // Holds default filter settings etc.
imaging *images.ImageProcessor imaging *images.ImageProcessor
@ -139,7 +148,6 @@ type Spec struct {
// The parts of Spec that's comoon for all sites. // The parts of Spec that's comoon for all sites.
type SpecCommon struct { type SpecCommon struct {
incr identity.Incrementer incr identity.Incrementer
imageCache *imageCache
ResourceCache *ResourceCache ResourceCache *ResourceCache
FileCaches filecache.Caches FileCaches filecache.Caches
@ -171,13 +179,13 @@ func (r *Spec) BuildConfig() config.BuildConfig {
} }
func (r *Spec) CacheStats() string { func (r *Spec) CacheStats() string {
r.imageCache.mu.RLock() r.ImageCache.mu.RLock()
defer r.imageCache.mu.RUnlock() defer r.ImageCache.mu.RUnlock()
s := fmt.Sprintf("Cache entries: %d", len(r.imageCache.store)) s := fmt.Sprintf("Cache entries: %d", len(r.ImageCache.store))
count := 0 count := 0
for k := range r.imageCache.store { for k := range r.ImageCache.store {
if count > 5 { if count > 5 {
break break
} }
@ -189,12 +197,12 @@ func (r *Spec) CacheStats() string {
} }
func (r *Spec) ClearCaches() { func (r *Spec) ClearCaches() {
r.imageCache.clear() r.ImageCache.clear()
r.ResourceCache.clear() r.ResourceCache.clear()
} }
func (r *Spec) DeleteBySubstring(s string) { func (r *Spec) DeleteBySubstring(s string) {
r.imageCache.deleteIfContains(s) r.ImageCache.deleteIfContains(s)
} }
func (s *Spec) String() string { func (s *Spec) String() string {

View file

@ -43,7 +43,7 @@ func NewTestResourceSpec() (*resources.Spec, error) {
return nil, err return nil, err
} }
spec, err := resources.NewSpec(s, nil, nil, nil, nil, nil) spec, err := resources.NewSpec(s, nil, nil, nil, nil, nil, nil)
return spec, err return spec, err
} }

BIN
resources/testdata/pix.gif vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 B

View file

@ -0,0 +1,34 @@
cp $SOURCE/resources/testdata/pix.gif content/en/bundle1/pix.gif
cp $SOURCE/resources/testdata/pix.gif content/en/bundle2/pix.gif
cp $SOURCE/resources/testdata/pix.gif content/fr/bundle1/pix.gif
hugo
stdout 'Pages.*3.*2'
stdout 'Processed images.*2.*1'
-- content/en/bundle1/index.md --
-- content/en/bundle2/index.md --
-- content/fr/bundle1/index.md --
-- hugo.toml --
disableKinds = ["taxonomy", "term", "RSS", "sitemap", "robotsTXT", "404"]
baseURL = "https://example.com/"
[languages]
[languages.en]
languageName = "English"
weight = 1
title = "English Title"
contentDir = "content/en"
[languages.fr]
languageName = "French"
weight = 2
title = "French Title"
contentDir = "content/fr"
-- layouts/index.html --
Home.
-- layouts/_default/single.html --
Single.
{{ range .Resources }}
{{ $img := .Resize "3x" }}
Resized: {{ $img.RelPermalink }}
{{ end }}