shithub: hugo

Download patch

ref: 4a366fcfee24b3a5a5045b16c3b87b76147adf5e
parent: 083311d0336ced35909b3375950f7817ecf95ed0
author: Bjørn Erik Pedersen <[email protected]>
date: Wed Oct 17 05:28:04 EDT 2018

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

--- a/commands/commandeer.go
+++ b/commands/commandeer.go
@@ -324,10 +324,7 @@
 			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{
--- a/commands/hugo.go
+++ b/commands/hugo.go
@@ -618,13 +618,20 @@
 	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 @@
 	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 @@
 
 		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 {
--- a/commands/server.go
+++ b/commands/server.go
@@ -345,10 +345,22 @@
 				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)
--- a/common/types/evictingqueue.go
+++ b/common/types/evictingqueue.go
@@ -52,6 +52,13 @@
 	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()
--- a/common/types/evictingqueue_test.go
+++ b/common/types/evictingqueue_test.go
@@ -36,6 +36,9 @@
 	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")
--- a/hugolib/hugo_sites.go
+++ b/hugolib/hugo_sites.go
@@ -368,6 +368,11 @@
 	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
 }
--- a/hugolib/hugo_sites_build.go
+++ b/hugolib/hugo_sites_build.go
@@ -41,29 +41,31 @@
 		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) 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 @@
 
 				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
+					}
 				}
 			}
 		}
--- a/hugolib/site_render.go
+++ b/hugolib/site_render.go
@@ -43,7 +43,7 @@
 		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)
 	}