Skip to content

Commit

Permalink
Fix /static performance regression from Hugo 0.103.0
Browse files Browse the repository at this point in the history
In `v0.103.0` we added support for `resources.PostProcess` for all file types, not just HTML. We had benchmarks that said we were fine in that department, but those did not consider the static file syncing.

This fixes that by:

* Making sure that the /static syncer always gets its own file system without any checks for the post process token.
* For dynamic files (e.g. rendered HTML files) we add an additional check to make sure that we skip binary files (e.g. images)

Fixes #10328
  • Loading branch information
bep committed Sep 26, 2022
1 parent d8aba18 commit 29ccb36
Show file tree
Hide file tree
Showing 6 changed files with 31 additions and 22 deletions.
5 changes: 1 addition & 4 deletions commands/hugo.go
Expand Up @@ -653,10 +653,7 @@ func (c *commandeer) copyStaticTo(sourceFs *filesystems.SourceFilesystem) (uint6
syncer.NoChmod = c.Cfg.GetBool("noChmod")
syncer.ChmodFilter = chmodFilter
syncer.SrcFs = fs
syncer.DestFs = c.Fs.PublishDir
if c.renderStaticToDisk {
syncer.DestFs = c.Fs.PublishDirStatic
}
syncer.DestFs = c.Fs.PublishDirStatic
// Now that we are using a unionFs for the static directories
// We can effectively clean the publishDir on initial sync
syncer.Delete = c.Cfg.GetBool("cleanDestinationDir")
Expand Down
20 changes: 18 additions & 2 deletions deps/deps.go
Expand Up @@ -2,6 +2,8 @@ package deps

import (
"fmt"
"path/filepath"
"strings"
"sync"
"sync/atomic"
"time"
Expand Down Expand Up @@ -246,16 +248,30 @@ func New(cfg DepsCfg) (*Deps, error) {
execHelper := hexec.New(securityConfig)

var filenameHasPostProcessPrefixMu sync.Mutex
cb := func(name string, match bool) {
hashBytesReceiverFunc := func(name string, match bool) {
if !match {
return
}
filenameHasPostProcessPrefixMu.Lock()
d.FilenameHasPostProcessPrefix = append(d.FilenameHasPostProcessPrefix, name)
filenameHasPostProcessPrefixMu.Unlock()
}

// Skip binary files.
hashBytesSHouldCheck := func(name string) bool {
ext := strings.TrimPrefix(filepath.Ext(name), ".")
mime, _, found := cfg.MediaTypes.GetBySuffix(ext)
if !found {
return false
}
switch mime.MainType {
case "text", "application":
return true
default:
return false
}
}
fs.PublishDir = hugofs.NewHasBytesReceiver(fs.PublishDir, cb, []byte(postpub.PostProcessPrefix))
fs.PublishDir = hugofs.NewHasBytesReceiver(fs.PublishDir, hashBytesSHouldCheck, hashBytesReceiverFunc, []byte(postpub.PostProcessPrefix))

ps, err := helpers.NewPathSpec(fs, cfg.Language, logger)
if err != nil {
Expand Down
4 changes: 1 addition & 3 deletions hugofs/fs.go
Expand Up @@ -40,8 +40,7 @@ type Fs struct {
// It's mounted inside publishDir (default /public).
PublishDir afero.Fs

// PublishDirStatic is the file system used for static files when --renderStaticToDisk is set.
// When this is set, PublishDir is set to write to memory.
// PublishDirStatic is the file system used for static files.
PublishDirStatic afero.Fs

// PublishDirServer is the file system used for serving the public directory with Hugo's development server.
Expand Down Expand Up @@ -142,7 +141,6 @@ func isWrite(flag int) bool {
// MakeReadableAndRemoveAllModulePkgDir makes any subdir in dir readable and then
// removes the root.
// TODO(bep) move this to a more suitable place.
//
func MakeReadableAndRemoveAllModulePkgDir(fs afero.Fs, dir string) (int, error) {
// Safe guard
if !strings.Contains(dir, "pkg") {
Expand Down
8 changes: 6 additions & 2 deletions hugofs/hasbytes_fs.go
Expand Up @@ -27,12 +27,13 @@ var (

type hasBytesFs struct {
afero.Fs
shouldCheck func(name string) bool
hasBytesCallback func(name string, match bool)
pattern []byte
}

func NewHasBytesReceiver(delegate afero.Fs, hasBytesCallback func(name string, match bool), pattern []byte) afero.Fs {
return &hasBytesFs{Fs: delegate, hasBytesCallback: hasBytesCallback, pattern: pattern}
func NewHasBytesReceiver(delegate afero.Fs, shouldCheck func(name string) bool, hasBytesCallback func(name string, match bool), pattern []byte) afero.Fs {
return &hasBytesFs{Fs: delegate, shouldCheck: shouldCheck, hasBytesCallback: hasBytesCallback, pattern: pattern}
}

func (fs *hasBytesFs) UnwrapFilesystem() afero.Fs {
Expand All @@ -56,6 +57,9 @@ func (fs *hasBytesFs) OpenFile(name string, flag int, perm os.FileMode) (afero.F
}

func (fs *hasBytesFs) wrapFile(f afero.File) afero.File {
if !fs.shouldCheck(f.Name()) {
return f
}
return &hasBytesFile{
File: f,
hbw: &hugio.HasBytesWriter{
Expand Down
2 changes: 1 addition & 1 deletion hugolib/filesystems/basefs.go
Expand Up @@ -67,7 +67,7 @@ type BaseFs struct {
// This usually maps to /my-project/public.
PublishFs afero.Fs

// The filesystem used for renderStaticToDisk.
// The filesystem used for static files.
PublishFsStatic afero.Fs

// A read-only filesystem starting from the project workDir.
Expand Down
14 changes: 4 additions & 10 deletions hugolib/pages_process.go
Expand Up @@ -32,10 +32,9 @@ func newPagesProcessor(h *HugoSites, sp *source.SourceSpec) *pagesProcessor {
procs := make(map[string]pagesCollectorProcessorProvider)
for _, s := range h.Sites {
procs[s.Lang()] = &sitePagesProcessor{
m: s.pageMap,
errorSender: s.h,
itemChan: make(chan interface{}, config.GetNumWorkerMultiplier()*2),
renderStaticToDisk: h.Cfg.GetBool("renderStaticToDisk"),
m: s.pageMap,
errorSender: s.h,
itemChan: make(chan interface{}, config.GetNumWorkerMultiplier()*2),
}
}
return &pagesProcessor{
Expand Down Expand Up @@ -118,8 +117,6 @@ type sitePagesProcessor struct {
ctx context.Context
itemChan chan any
itemGroup *errgroup.Group

renderStaticToDisk bool
}

func (p *sitePagesProcessor) Process(item any) error {
Expand Down Expand Up @@ -164,10 +161,7 @@ func (p *sitePagesProcessor) copyFile(fim hugofs.FileMetaInfo) error {

defer f.Close()

fs := s.PublishFs
if p.renderStaticToDisk {
fs = s.PublishFsStatic
}
fs := s.PublishFsStatic

return s.publish(&s.PathSpec.ProcessingStats.Files, target, f, fs)
}
Expand Down

0 comments on commit 29ccb36

Please sign in to comment.