Prevent stale content in Fast Render Mode
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Wed, 17 Oct 2018 07:28:04 +0000 (09:28 +0200)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Wed, 17 Oct 2018 08:15:22 +0000 (10:15 +0200)
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

commands/commandeer.go
commands/hugo.go
commands/server.go
common/types/evictingqueue.go
common/types/evictingqueue_test.go
hugolib/hugo_sites.go
hugolib/hugo_sites_build.go
hugolib/site_render.go

index 2b76462fe43e33aef94c842d2278d8e57e25a5fc..94b2c65536fc7766a9454f9b8c7eca8d74db645e 100644 (file)
@@ -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{
index 6cb2ec012a4567b1d479bb38c23edd3202db338d..deaa1f7ff022702a6322f5c990685a8a2cc2e65f 100644 (file)
@@ -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 {
index ffdbc95c98d7688cdeef9994e621696ab3d16bc9..7b7164eeab604676aa154fbac91caf56d957ed41 100644 (file)
@@ -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)
index 152dc4c418924854240ed98f8ce02b68baa64fbf..8847624265d4272dda52792a243fd4c3f57fc18e 100644 (file)
@@ -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()
index a33f1a34452b3cd7739a1f17f48114bf943b503f..a7b1e1d54eb9dda2b24d31b2edc259b2d7a867e8 100644 (file)
@@ -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")
index d9eb9f57d1a71e8e3a7098baaa9d9d141c2c0da6..7f70967d65ca68afa1460a1b8d09a227000bd0cf 100644 (file)
@@ -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
 }
index 5bb328aa2864d5e5b655bda2104c1e8263571e79..13fbfd57eff52157adec2162effb4a43cbe5b84b 100644 (file)
@@ -41,27 +41,29 @@ 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 {
-                       return err
+               if len(events) > 0 {
+                       // Rebuild
+                       if err := h.initRebuild(conf); err != nil {
+                               return err
+                       }
+               } else {
+                       if err := h.init(conf); err != nil {
+                               return err
+                       }
                }
-       } else {
-               if err := h.init(conf); err != nil {
+
+               if err := h.process(conf, events...); 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.assemble(conf); err != nil {
+                       return err
+               }
        }
 
        if err := h.render(conf); err != nil {
@@ -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
+                                       }
                                }
                        }
                }
index 13fbb43cd072d9e06c4af5ce41662eaed00bdbb6..6583acd06aa5639d544a6e47ffd41dce2375b01b 100644 (file)
@@ -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)
        }