80 lines
1.9 KiB
Go
80 lines
1.9 KiB
Go
package bsdaily
|
|
|
|
import (
|
|
"fmt"
|
|
"log/slog"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
func DumpAndCompress(dbPath, outputPath string) (err error) {
|
|
for _, tool := range []string{"sqlite3", "zstdmt"} {
|
|
if _, err := exec.LookPath(tool); err != nil {
|
|
return fmt.Errorf("required tool %q not found in PATH: %w", tool, err)
|
|
}
|
|
}
|
|
|
|
if err := CheckFreeSpace(filepath.Dir(outputPath), MinDailiesFreeBytes, "dailiesBase (pre-dump)"); err != nil {
|
|
return err
|
|
}
|
|
|
|
outFile, err := os.Create(outputPath)
|
|
if err != nil {
|
|
return fmt.Errorf("creating output file: %w", err)
|
|
}
|
|
defer func() {
|
|
if cerr := outFile.Close(); cerr != nil && err == nil {
|
|
err = fmt.Errorf("closing output: %w", cerr)
|
|
}
|
|
}()
|
|
|
|
dumpCmd := exec.Command("sqlite3", dbPath, ".dump")
|
|
zstdCmd := exec.Command("zstdmt", "-15")
|
|
|
|
pipe, err := dumpCmd.StdoutPipe()
|
|
if err != nil {
|
|
return fmt.Errorf("creating dump stdout pipe: %w", err)
|
|
}
|
|
zstdCmd.Stdin = pipe
|
|
zstdCmd.Stdout = outFile
|
|
|
|
var dumpStderr, zstdStderr strings.Builder
|
|
dumpCmd.Stderr = &dumpStderr
|
|
zstdCmd.Stderr = &zstdStderr
|
|
|
|
slog.Info("starting sqlite3 dump and zstdmt compression")
|
|
|
|
if err := zstdCmd.Start(); err != nil {
|
|
return fmt.Errorf("starting zstdmt: %w", err)
|
|
}
|
|
if err := dumpCmd.Start(); err != nil {
|
|
return fmt.Errorf("starting sqlite3 dump: %w", err)
|
|
}
|
|
|
|
if err := dumpCmd.Wait(); err != nil {
|
|
return fmt.Errorf("sqlite3 dump failed: %w; stderr: %s", err, dumpStderr.String())
|
|
}
|
|
if err := zstdCmd.Wait(); err != nil {
|
|
return fmt.Errorf("zstdmt failed: %w; stderr: %s", err, zstdStderr.String())
|
|
}
|
|
|
|
if err := outFile.Sync(); err != nil {
|
|
return fmt.Errorf("syncing output: %w", err)
|
|
}
|
|
|
|
info, err := os.Stat(outputPath)
|
|
if err != nil {
|
|
return fmt.Errorf("stat output: %w", err)
|
|
}
|
|
slog.Info("compressed output written", "path", outputPath,
|
|
"size_bytes", info.Size(), "size_mb", info.Size()/(1024*1024))
|
|
|
|
if info.Size() == 0 {
|
|
return fmt.Errorf("compressed output is empty")
|
|
}
|
|
|
|
return nil
|
|
}
|