package documents import ( "fmt" "os" "path/filepath" "strings" "syscall" ) // minFreeBytes ist die Untergrenze, unter der die Texterkennung pausiert. // // OCR ist das einzige Stück dieser Anwendung, das von sich aus und ohne // Zutun eines Menschen Platz verbraucht: Es arbeitet eine Warteschlange ab, // solange etwas darin liegt. Eine Cloud, die ihre eigene Platte vollschreibt, // nimmt Uploads nicht mehr an – und das wäre dann kein OCR-Problem mehr, // sondern ein Ausfall. // // 2 GiB ist mit Absicht großzügig: Der Sidecar einer Erkennung ist kaum größer // als ein Megabyte, tesseract und ghostscript brauchen währenddessen aber // Arbeitsdateien, deren Größe von der Auflösung der Vorlage abhängt. Lieber // eine Weile nichts erkennen als eine volle Platte. const minFreeBytes = 2 << 30 // 2 GiB // freeBytes meldet den freien Platz auf dem Dateisystem, auf dem path liegt. func freeBytes(path string) (uint64, error) { var st syscall.Statfs_t if err := syscall.Statfs(path, &st); err != nil { return 0, fmt.Errorf("statfs %s: %w", path, err) } // Bavail, nicht Bfree: Bfree zählt auch die Reserve mit, die nur root // benutzen darf – der Server läuft aber als unprivilegierter Nutzer und // käme daran nie heran. return st.Bavail * uint64(st.Bsize), nil } // sweepTemp räumt Zwischendateien früherer Läufe weg. // // Im Normalfall räumt der Arbeiter selbst auf. Wird der Container aber hart // beendet – `docker kill`, ein Absturz, Stromausfall –, läuft kein defer mehr, // und die angefangene Datei bleibt liegen. Ohne diesen Durchgang sammelt sich // das über Monate an, ausgerechnet auf der Platte, die auch die Dateien trägt. // // Es wird nur weggeräumt, was diese Anwendung selbst angelegt hat: das Präfix // "ocr-" im Zwischenverzeichnis. Fremde Dateien bleiben unangetastet. func sweepTemp(dir string) (int, int64) { entries, err := os.ReadDir(dir) if err != nil { return 0, 0 } var count int var bytes int64 for _, e := range entries { if e.IsDir() || !strings.HasPrefix(e.Name(), "ocr-") { continue } full := filepath.Join(dir, e.Name()) if info, err := e.Info(); err == nil { bytes += info.Size() } if err := os.Remove(full); err == nil { count++ } } return count, bytes }