mirror of
https://github.com/restic/restic.git
synced 2024-12-25 01:06:39 +00:00
9f3ca97ee8
* uses less memory as common prefix is only stored once * stepping stone for simpler error callback api, which will allow further memory footprint reduction Signed-off-by: Igor Fedorenko <igor@ifedorenko.com>
70 lines
1.7 KiB
Go
70 lines
1.7 KiB
Go
package restorer
|
|
|
|
import (
|
|
"io"
|
|
"os"
|
|
"sync"
|
|
|
|
"github.com/hashicorp/golang-lru/simplelru"
|
|
"github.com/restic/restic/internal/debug"
|
|
"github.com/restic/restic/internal/errors"
|
|
)
|
|
|
|
type filesWriter struct {
|
|
lock sync.Mutex // guards concurrent access
|
|
inprogress map[string]struct{} // (logically) opened file writers
|
|
writers simplelru.LRUCache // key: string, value: *os.File
|
|
}
|
|
|
|
func newFilesWriter(count int) *filesWriter {
|
|
writers, _ := simplelru.NewLRU(count, func(key interface{}, value interface{}) {
|
|
value.(*os.File).Close()
|
|
debug.Log("Closed and purged cached writer for %v", key)
|
|
})
|
|
return &filesWriter{inprogress: make(map[string]struct{}), writers: writers}
|
|
}
|
|
|
|
func (w *filesWriter) writeToFile(path string, buf []byte) error {
|
|
acquireWriter := func() (io.Writer, error) {
|
|
w.lock.Lock()
|
|
defer w.lock.Unlock()
|
|
if wr, ok := w.writers.Get(path); ok {
|
|
debug.Log("Used cached writer for %s", path)
|
|
return wr.(*os.File), nil
|
|
}
|
|
var flags int
|
|
if _, append := w.inprogress[path]; append {
|
|
flags = os.O_APPEND | os.O_WRONLY
|
|
} else {
|
|
w.inprogress[path] = struct{}{}
|
|
flags = os.O_CREATE | os.O_TRUNC | os.O_WRONLY
|
|
}
|
|
wr, err := os.OpenFile(path, flags, 0600)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
w.writers.Add(path, wr)
|
|
debug.Log("Opened and cached writer for %s", path)
|
|
return wr, nil
|
|
}
|
|
|
|
wr, err := acquireWriter()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
n, err := wr.Write(buf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if n != len(buf) {
|
|
return errors.Errorf("error writing file %v: wrong length written, want %d, got %d", path, len(buf), n)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (w *filesWriter) close(path string) {
|
|
w.lock.Lock()
|
|
defer w.lock.Unlock()
|
|
w.writers.Remove(path)
|
|
delete(w.inprogress, path)
|
|
}
|