Optimize walkdir for pages

This commit is contained in:
2026-04-21 20:04:13 +02:00
parent e0d2fb0b41
commit e8992235d4
+41 -10
View File
@@ -1,8 +1,8 @@
package main package main
import ( import (
"bytes"
"fmt" "fmt"
"io/fs"
"log" "log"
"net/http" "net/http"
"os" "os"
@@ -44,20 +44,21 @@ func (h *handler) handleMove(w http.ResponseWriter, r *http.Request, srcURL, src
return return
} }
// Phase 1: walk the tree and rewrite every index.md whose content changes. // Phase 1: walk the tree and rewrite every index.md that references the
// Keep the pre-rewrite bytes in memory so we can revert on failure. // moved path. Keep the pre-rewrite bytes in memory so we can revert on
// failure. The walker only reads directory listings and files literally
// named index.md; hidden directories are pruned. A cheap substring check
// skips parsing files that cannot contain a relevant link.
rewritten := map[string][]byte{} rewritten := map[string][]byte{}
walkErr := filepath.WalkDir(h.root, func(fsPath string, d fs.DirEntry, err error) error { needle := []byte("[[" + oldPath)
if err != nil { walkErr := walkIndexFiles(h.root, func(fsPath string) error {
return err
}
if d.IsDir() || d.Name() != "index.md" {
return nil
}
orig, err := os.ReadFile(fsPath) orig, err := os.ReadFile(fsPath)
if err != nil { if err != nil {
return err return err
} }
if !bytes.Contains(orig, needle) {
return nil
}
updated, changed := rewriteWikiLinks(orig, oldPath, newPath) updated, changed := rewriteWikiLinks(orig, oldPath, newPath)
if !changed { if !changed {
return nil return nil
@@ -186,6 +187,36 @@ func rollbackRewrites(rewritten map[string][]byte) {
} }
} }
// walkIndexFiles visits every `index.md` under root, skipping hidden
// directories (names beginning with `.`). Unlike filepath.WalkDir this does
// not stat each regular file — on spinning disks that saves the bulk of the
// traversal cost when folders contain many non-page files (photos, archives).
func walkIndexFiles(root string, visit func(fsPath string) error) error {
entries, err := os.ReadDir(root)
if err != nil {
return err
}
for _, e := range entries {
name := e.Name()
if strings.HasPrefix(name, ".") {
continue
}
full := filepath.Join(root, name)
if e.IsDir() {
if err := walkIndexFiles(full, visit); err != nil {
return err
}
continue
}
if name == "index.md" {
if err := visit(full); err != nil {
return err
}
}
}
return nil
}
// writeFileAtomic writes data to a temp file in the same directory as path // writeFileAtomic writes data to a temp file in the same directory as path
// and renames it into place so readers never observe a partial file. // and renames it into place so readers never observe a partial file.
func writeFileAtomic(path string, data []byte, perm os.FileMode) error { func writeFileAtomic(path string, data []byte, perm os.FileMode) error {