Files
zeekayandHanzo Dev 1ed57a3544 an archive carries what it can quiesce, and a limiter counts what it admits
A backup checkpoints one Base's write-ahead log inside one transaction. A
tenant's Base is separate handles with its own log, in neither, so its file
was being copied mid-write beside a log copied at another instant — a torn
copy rather than a smaller one. The archive names what it leaves out, in the
zip's own comment, where every listing tool already prints it.

Restore spares exactly what the archive omits. That symmetry is the whole
thing: an archive that skips the tenants and a restore that does not spare
them deletes every tenant. Both are stated in the package that owns the
directory, so core stays ignorant of tenancy.

The rate limiter refuses a request it cannot key. A forwarded request arrives
with no client address, and a limiter that admits what it cannot count is not
one. The way to be counted already exists: a gateway that sets a forwarded
header restores the client, through the trusted-proxy settings.

Opening a Base holds the registry's lock over the map alone. It had spanned
provision, construct, bootstrap and declare, and a read waits behind a
waiting writer, so one org's first request stalled every other request in the
process — measured at 209ms against a 101ms open.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-13 14:59:41 -07:00

102 lines
2.3 KiB
Go

package archive
import (
"archive/zip"
"compress/flate"
"errors"
"io"
"io/fs"
"os"
"path/filepath"
"strings"
)
// Create creates a new zip archive from src dir content and saves it in dest path.
//
// You can specify skipPaths to skip/ignore certain directories and files (relative to src)
// preventing adding them in the final archive.
//
// What was skipped is written as the archive's comment, because listing a zip
// shows what is in it and nothing shows what is not. Whoever holds the file can
// then tell what it covers without knowing how it was made.
func Create(src string, dest string, skipPaths ...string) error {
if err := os.MkdirAll(filepath.Dir(dest), os.ModePerm); err != nil {
return err
}
zf, err := os.Create(dest)
if err != nil {
return err
}
zw := zip.NewWriter(zf)
// register a custom Deflate compressor
zw.RegisterCompressor(zip.Deflate, func(out io.Writer) (io.WriteCloser, error) {
return flate.NewWriter(out, flate.BestSpeed)
})
if len(skipPaths) > 0 {
if err := zw.SetComment("omits " + strings.Join(skipPaths, ", ")); err != nil {
return errors.Join(err, zw.Close(), zf.Close(), os.Remove(dest))
}
}
err = zipAddFS(zw, os.DirFS(src), skipPaths...)
if err != nil {
// try to cleanup at least the created zip file
return errors.Join(err, zw.Close(), zf.Close(), os.Remove(dest))
}
return errors.Join(zw.Close(), zf.Close())
}
// note remove after similar method is added in the std lib (https://github.com/golang/go/issues/54898)
func zipAddFS(w *zip.Writer, fsys fs.FS, skipPaths ...string) error {
return fs.WalkDir(fsys, ".", func(name string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() {
return nil
}
// skip
for _, ignore := range skipPaths {
if ignore == name ||
strings.HasPrefix(filepath.Clean(name)+string(os.PathSeparator), filepath.Clean(ignore)+string(os.PathSeparator)) {
return nil
}
}
info, err := d.Info()
if err != nil {
return err
}
h, err := zip.FileInfoHeader(info)
if err != nil {
return err
}
h.Name = name
h.Method = zip.Deflate
fw, err := w.CreateHeader(h)
if err != nil {
return err
}
f, err := fsys.Open(name)
if err != nil {
return err
}
defer f.Close()
_, err = io.Copy(fw, f)
return err
})
}