From 8db28167c3abb5ee9696ccabcd7ce243f9987cf8 Mon Sep 17 00:00:00 2001 From: MickaelK Date: Thu, 24 Sep 2026 14:52:02 +1000 Subject: [PATCH] feature (plg_backend_local): speed improvements on linux --- .../plugin/plg_backend_local/direct_linux.go | 92 +++++++++++++++++++ .../plugin/plg_backend_local/direct_other.go | 12 +++ server/plugin/plg_backend_local/index.go | 8 +- 3 files changed, 109 insertions(+), 3 deletions(-) create mode 100644 server/plugin/plg_backend_local/direct_linux.go create mode 100644 server/plugin/plg_backend_local/direct_other.go diff --git a/server/plugin/plg_backend_local/direct_linux.go b/server/plugin/plg_backend_local/direct_linux.go new file mode 100644 index 00000000..51efb7b3 --- /dev/null +++ b/server/plugin/plg_backend_local/direct_linux.go @@ -0,0 +1,92 @@ +package plg_backend_local + +import ( + "errors" + "io" + "os" + "unsafe" + + "golang.org/x/sys/unix" +) + +const ( + directChunk = 16 * 1024 * 1024 + directAlign = 4096 + directMinSize = 8 * 1024 * 1024 +) + +type direct struct { + f *os.File + ch chan []byte + done chan struct{} + err error + cur []byte +} + +func NewDirect(f *os.File, fs os.FileInfo) io.ReadCloser { + if fs.Size() < directMinSize { + return f + } + flags, err := unix.FcntlInt(f.Fd(), unix.F_GETFL, 0) + if err != nil { + return f + } else if _, err = unix.FcntlInt(f.Fd(), unix.F_SETFL, flags|unix.O_DIRECT); err != nil { + return f + } + return &direct{f: f, done: make(chan struct{})} +} + +func (this *direct) Seek(offset int64, whence int) (int64, error) { + if this.ch != nil { + return 0, errors.New("seek not supported after read") + } + return this.f.Seek(offset, whence) +} + +func (this *direct) Read(p []byte) (int, error) { + if this.ch == nil { + this.ch = make(chan []byte) + go this.fill() + } + for len(this.cur) == 0 { + b, ok := <-this.ch + if !ok { + return 0, this.err + } + this.cur = b + } + n := copy(p, this.cur) + this.cur = this.cur[n:] + return n, nil +} + +func (this *direct) fill() { + defer close(this.ch) + buf, next := alignedBuffer(directChunk), alignedBuffer(directChunk) + start, err := this.f.Seek(0, io.SeekCurrent) + pos := start - start%directAlign + for err == nil { + var n int + n, err = this.f.ReadAt(buf, pos) + skip := min(max(start-pos, 0), int64(n)) + select { + case this.ch <- buf[skip:n]: + case <-this.done: + return + } + pos += int64(n) + buf, next = next, buf + } + this.err = err +} + +func alignedBuffer(size int) []byte { + b := make([]byte, size+directAlign) + off := directAlign - int(uintptr(unsafe.Pointer(&b[0]))%directAlign) + return b[off : off+size] +} + +func (this *direct) Close() error { + close(this.done) + return this.f.Close() +} diff --git a/server/plugin/plg_backend_local/direct_other.go b/server/plugin/plg_backend_local/direct_other.go new file mode 100644 index 00000000..fbb91c55 --- /dev/null +++ b/server/plugin/plg_backend_local/direct_other.go @@ -0,0 +1,12 @@ +//go:build !linux + +package plg_backend_local + +import ( + "io" + "os" +) + +func NewDirect(f *os.File, fs os.FileInfo) io.ReadCloser { + return f +} diff --git a/server/plugin/plg_backend_local/index.go b/server/plugin/plg_backend_local/index.go index 3b63b535..0ebb2aa6 100644 --- a/server/plugin/plg_backend_local/index.go +++ b/server/plugin/plg_backend_local/index.go @@ -1,11 +1,13 @@ package plg_backend_local import ( - . "github.com/mickael-kerjean/filestash/server/common" - "golang.org/x/crypto/bcrypt" "io" "os" "os/user" + + . "github.com/mickael-kerjean/filestash/server/common" + + "golang.org/x/crypto/bcrypt" ) func init() { @@ -112,7 +114,7 @@ func (this Local) Cat(path string) (io.ReadCloser, error) { f.Close() return nil, ErrNotFound } - return f, nil + return NewDirect(f, fs), nil } func (this Local) Mkdir(path string) error {