Commit 0d06714a92
Verified · cmc
Layout: unified · split
internal/render/render.go +8
| @@ -8,6 +8,7 @@ import ( | |||
| 8 | "path/filepath" | 8 | "path/filepath" |
| 9 | "slices" | 9 | "slices" |
| 10 | "strconv" | 10 | "strconv" |
| 11 | "time" | ||
| 11 | 12 | ||
| 12 | "golang.org/x/sync/singleflight" | 13 | "golang.org/x/sync/singleflight" |
| 13 | 14 | ||
| @@ -84,6 +85,10 @@ func (r *Renderer) generate(it *library.Item, w int, dst string) error { | |||
| 84 | return os.Rename(tmp.Name(), dst) | 85 | return os.Rename(tmp.Name(), dst) |
| 85 | } | 86 | } |
| 86 | 87 | ||
| 88 | // pruneGrace keeps stale directories modified this recently, since a request | ||
| 89 | // holding an older library snapshot may still be writing into them. | ||
| 90 | const pruneGrace = 5 * time.Minute | ||
| 91 | |||
| 87 | // Prune removes derivative directories for items no longer in lib or whose source changed. | 92 | // Prune removes derivative directories for items no longer in lib or whose source changed. |
| 88 | func (r *Renderer) Prune(lib *library.Library) error { | 93 | func (r *Renderer) Prune(lib *library.Library) error { |
| 89 | keep := map[string]bool{} | 94 | keep := map[string]bool{} |
| @@ -99,6 +104,9 @@ func (r *Renderer) Prune(lib *library.Library) error { | |||
| 99 | var firstErr error | 104 | var firstErr error |
| 100 | for _, v := range versions { | 105 | for _, v := range versions { |
| 101 | if !keep[v] { | 106 | if !keep[v] { |
| 107 | if fi, err := os.Stat(v); err == nil && time.Since(fi.ModTime()) < pruneGrace { | ||
| 108 | continue | ||
| 109 | } | ||
| 102 | if err := os.RemoveAll(v); err != nil { | 110 | if err := os.RemoveAll(v); err != nil { |
| 103 | log.Printf("prune %s: %v", v, err) | 111 | log.Printf("prune %s: %v", v, err) |
| 104 | if firstErr == nil { | 112 | if firstErr == nil { |
internal/render/render_test.go +19
| @@ -151,6 +151,10 @@ func TestPrune(t *testing.T) { | |||
| 151 | t.Fatal(err) | 151 | t.Fatal(err) |
| 152 | } | 152 | } |
| 153 | } | 153 | } |
| 154 | past := time.Now().Add(-2 * pruneGrace) | ||
| 155 | for _, it := range []*library.Item{cur, old, gone} { | ||
| 156 | os.Chtimes(filepath.Dir(r.Path(it, 480)), past, past) | ||
| 157 | } | ||
| 154 | lib := &library.Library{Albums: []*library.Album{{Slug: "tokyo", Items: []*library.Item{cur}}}} | 158 | lib := &library.Library{Albums: []*library.Album{{Slug: "tokyo", Items: []*library.Item{cur}}}} |
| 155 | if err := r.Prune(lib); err != nil { | 159 | if err := r.Prune(lib); err != nil { |
| 156 | t.Fatal(err) | 160 | t.Fatal(err) |
| @@ -164,3 +168,18 @@ func TestPrune(t *testing.T) { | |||
| 164 | } | 168 | } |
| 165 | } | 169 | } |
| 166 | } | 170 | } |
| 171 | |||
| 172 | func TestPruneKeepsRecentlyModifiedStaleDir(t *testing.T) { | ||
| 173 | f := &fakeFormat{} | ||
| 174 | r := New(t.TempDir(), 1) | ||
| 175 | old := item(f) | ||
| 176 | if _, err := r.Get(old, 480); err != nil { | ||
| 177 | t.Fatal(err) | ||
| 178 | } | ||
| 179 | if err := r.Prune(&library.Library{}); err != nil { | ||
| 180 | t.Fatal(err) | ||
| 181 | } | ||
| 182 | if _, err := os.Stat(r.Path(old, 480)); err != nil { | ||
| 183 | t.Fatalf("recently written stale dir removed: %v", err) | ||
| 184 | } | ||
| 185 | } | ||