mirror of
https://github.com/gohugoio/hugo.git
synced 2024-12-23 18:41:19 +00:00
Prevent stale content in Fast Render Mode
We do that by re-render visited pages that is not already in the stack. This may potentially do some double work, but that small penalty should be well worth it. Fixes #5281
This commit is contained in:
parent
083311d033
commit
4a366fcfee
8 changed files with 83 additions and 36 deletions
|
@ -324,10 +324,7 @@ func (c *commandeer) loadConfig(mustHaveConfigFile, running bool) error {
|
|||
fs.Destination = new(afero.MemMapFs)
|
||||
}
|
||||
|
||||
doLiveReload := !c.h.buildWatch && !config.GetBool("disableLiveReload")
|
||||
fastRenderMode := doLiveReload && !config.GetBool("disableFastRender")
|
||||
|
||||
if fastRenderMode {
|
||||
if c.fastRenderMode {
|
||||
// For now, fast render mode only. It should, however, be fast enough
|
||||
// for the full variant, too.
|
||||
changeDetector := &fileChangeDetector{
|
||||
|
|
|
@ -618,13 +618,20 @@ func (c *commandeer) buildSites() (err error) {
|
|||
return c.hugo.Build(hugolib.BuildCfg{})
|
||||
}
|
||||
|
||||
func (c *commandeer) handleBuildErr(err error, msg string) {
|
||||
c.buildErr = err
|
||||
c.logger.ERROR.Printf("%s: %s", msg, err)
|
||||
if !c.h.quiet && c.h.verbose {
|
||||
herrors.PrintStackTrace(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *commandeer) rebuildSites(events []fsnotify.Event) error {
|
||||
defer c.timeTrack(time.Now(), "Total")
|
||||
|
||||
c.buildErr = nil
|
||||
visited := c.visitedURLs.PeekAllSet()
|
||||
doLiveReload := !c.h.buildWatch && !c.Cfg.GetBool("disableLiveReload")
|
||||
if doLiveReload && !c.Cfg.GetBool("disableFastRender") {
|
||||
if c.fastRenderMode {
|
||||
|
||||
// Make sure we always render the home pages
|
||||
for _, l := range c.languages {
|
||||
|
@ -640,6 +647,15 @@ func (c *commandeer) rebuildSites(events []fsnotify.Event) error {
|
|||
return c.hugo.Build(hugolib.BuildCfg{RecentlyVisited: visited}, events...)
|
||||
}
|
||||
|
||||
func (c *commandeer) partialReRender(urls ...string) error {
|
||||
c.buildErr = nil
|
||||
visited := make(map[string]bool)
|
||||
for _, url := range urls {
|
||||
visited[url] = true
|
||||
}
|
||||
return c.hugo.Build(hugolib.BuildCfg{RecentlyVisited: visited, PartialReRender: true})
|
||||
}
|
||||
|
||||
func (c *commandeer) fullRebuild() {
|
||||
c.commandeerHugoState = &commandeerHugoState{}
|
||||
err := c.loadConfig(true, true)
|
||||
|
@ -907,15 +923,10 @@ func (c *commandeer) handleEvents(watcher *watcher.Batcher,
|
|||
|
||||
c.changeDetector.PrepareNew()
|
||||
if err := c.rebuildSites(dynamicEvents); err != nil {
|
||||
c.buildErr = err
|
||||
c.logger.ERROR.Printf("Rebuild failed: %s", err)
|
||||
if !c.h.quiet && c.h.verbose {
|
||||
herrors.PrintStackTrace(err)
|
||||
}
|
||||
c.handleBuildErr(err, "Rebuild failed")
|
||||
}
|
||||
|
||||
if doLiveReload {
|
||||
|
||||
if len(partitionedEvents.ContentEvents) == 0 && len(partitionedEvents.AssetEvents) > 0 {
|
||||
changed := c.changeDetector.changed()
|
||||
if c.changeDetector != nil && len(changed) == 0 {
|
||||
|
|
|
@ -345,10 +345,22 @@ func (f *fileServer) createEndpoint(i int) (*http.ServeMux, string, string, erro
|
|||
w.Header().Set("Pragma", "no-cache")
|
||||
}
|
||||
|
||||
if f.c.fastRenderMode {
|
||||
if f.c.fastRenderMode && f.c.buildErr == nil {
|
||||
p := r.RequestURI
|
||||
if strings.HasSuffix(p, "/") || strings.HasSuffix(p, "html") || strings.HasSuffix(p, "htm") {
|
||||
if !f.c.visitedURLs.Contains(p) {
|
||||
// If not already on stack, re-render that single page.
|
||||
if err := f.c.partialReRender(p); err != nil {
|
||||
f.c.handleBuildErr(err, fmt.Sprintf("Failed to render %q", p))
|
||||
if f.c.showErrorInBrowser {
|
||||
http.Redirect(w, r, p, 301)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
f.c.visitedURLs.Add(p)
|
||||
|
||||
}
|
||||
}
|
||||
h.ServeHTTP(w, r)
|
||||
|
|
|
@ -52,6 +52,13 @@ func (q *EvictingStringQueue) Add(v string) {
|
|||
q.mu.Unlock()
|
||||
}
|
||||
|
||||
// Contains returns whether the queue contains v.
|
||||
func (q *EvictingStringQueue) Contains(v string) bool {
|
||||
q.mu.Lock()
|
||||
defer q.mu.Unlock()
|
||||
return q.set[v]
|
||||
}
|
||||
|
||||
// Peek looks at the last element added to the queue.
|
||||
func (q *EvictingStringQueue) Peek() string {
|
||||
q.mu.Lock()
|
||||
|
|
|
@ -36,6 +36,9 @@ func TestEvictingStringQueue(t *testing.T) {
|
|||
queue.Add("a")
|
||||
queue.Add("b")
|
||||
|
||||
assert.True(queue.Contains("a"))
|
||||
assert.False(queue.Contains("foo"))
|
||||
|
||||
assert.Equal([]string{"b", "a"}, queue.PeekAll())
|
||||
assert.Equal("b", queue.Peek())
|
||||
queue.Add("c")
|
||||
|
|
|
@ -368,6 +368,11 @@ type BuildCfg struct {
|
|||
SkipRender bool
|
||||
// Use this to indicate what changed (for rebuilds).
|
||||
whatChanged *whatChanged
|
||||
|
||||
// This is a partial re-render of some selected pages. This means
|
||||
// we should skip most of the processing.
|
||||
PartialReRender bool
|
||||
|
||||
// Recently visited URLs. This is used for partial re-rendering.
|
||||
RecentlyVisited map[string]bool
|
||||
}
|
||||
|
|
|
@ -41,29 +41,31 @@ func (h *HugoSites) Build(config BuildCfg, events ...fsnotify.Event) error {
|
|||
conf.whatChanged = &whatChanged{source: true, other: true}
|
||||
}
|
||||
|
||||
for _, s := range h.Sites {
|
||||
s.Deps.BuildStartListeners.Notify()
|
||||
}
|
||||
if !config.PartialReRender {
|
||||
for _, s := range h.Sites {
|
||||
s.Deps.BuildStartListeners.Notify()
|
||||
}
|
||||
|
||||
if len(events) > 0 {
|
||||
// Rebuild
|
||||
if err := h.initRebuild(conf); err != nil {
|
||||
if len(events) > 0 {
|
||||
// Rebuild
|
||||
if err := h.initRebuild(conf); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := h.init(conf); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := h.process(conf, events...); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := h.init(conf); err != nil {
|
||||
|
||||
if err := h.assemble(conf); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := h.process(conf, events...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := h.assemble(conf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := h.render(conf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -226,8 +228,10 @@ func (h *HugoSites) assemble(config *BuildCfg) error {
|
|||
}
|
||||
|
||||
func (h *HugoSites) render(config *BuildCfg) error {
|
||||
for _, s := range h.Sites {
|
||||
s.initRenderFormats()
|
||||
if !config.PartialReRender {
|
||||
for _, s := range h.Sites {
|
||||
s.initRenderFormats()
|
||||
}
|
||||
}
|
||||
|
||||
for _, s := range h.Sites {
|
||||
|
@ -240,15 +244,23 @@ func (h *HugoSites) render(config *BuildCfg) error {
|
|||
|
||||
isRenderingSite := s == s2
|
||||
|
||||
if err := s2.preparePagesForRender(isRenderingSite && i == 0); err != nil {
|
||||
return err
|
||||
if !config.PartialReRender {
|
||||
if err := s2.preparePagesForRender(isRenderingSite && i == 0); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if !config.SkipRender {
|
||||
if err := s.render(config, i); err != nil {
|
||||
return err
|
||||
if config.PartialReRender {
|
||||
if err := s.renderPages(config); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := s.render(config, i); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ func (s *Site) renderPages(cfg *BuildCfg) error {
|
|||
go pageRenderer(s, pages, results, wg)
|
||||
}
|
||||
|
||||
if len(s.headlessPages) > 0 {
|
||||
if !cfg.PartialReRender && len(s.headlessPages) > 0 {
|
||||
wg.Add(1)
|
||||
go headlessPagesPublisher(s, wg)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue