added preload transfer rate

This commit is contained in:
ston1th 2022-10-08 20:35:05 +02:00
commit 470eeb921c
5 changed files with 111 additions and 13 deletions

View file

@ -42,6 +42,7 @@ func (p *preload) Read(data []byte) (n int, err error) {
if p.f.hasChunk(n) { if p.f.hasChunk(n) {
p.skipped++ p.skipped++
_, err = p.f.Seek(int64(n), io.SeekCurrent) _, err = p.f.Seek(int64(n), io.SeekCurrent)
n = 0
return return
} }
n, err = p.f.readToCache(data) n, err = p.f.readToCache(data)
@ -49,8 +50,9 @@ func (p *preload) Read(data []byte) (n int, err error) {
return return
} }
func (f *File) Preload(ctx context.Context, fin func(), errf func(), stop func()) { func (f *File) Preload(ctx context.Context, r *Rate, fin, errf, stop func()) {
log := f.log log := f.log
defer r.Stop()
defer f.Close() defer f.Close()
if f.offline { if f.offline {
log.V(2).Error(errors.New("no preload in offline mode"), "error preloading file") log.V(2).Error(errors.New("no preload in offline mode"), "error preloading file")
@ -65,7 +67,8 @@ func (f *File) Preload(ctx context.Context, fin func(), errf func(), stop func()
log.V(2).Info("preload started") log.V(2).Info("preload started")
p := &preload{f: f, ctx: ctx} p := &preload{f: f, ctx: ctx}
_, err := io.Copy(Discard, p) r.SetReader(p)
_, err := io.Copy(Discard, r)
if err == context.Canceled { if err == context.Canceled {
log.V(2).Info("preload canceled", "skipped", p.skipped, "written", p.written) log.V(2).Info("preload canceled", "skipped", p.skipped, "written", p.written)
stop() stop()

View file

@ -126,12 +126,7 @@ const (
TiB TiB
) )
func (fs *FS) FileSize(name string) (size string) { func FileSize(s int64) string {
fi, err := fs.Stat(name)
if err != nil {
return
}
s := fi.Size()
switch { switch {
case s >= TiB: case s >= TiB:
return strconv.Itoa(int(s/TiB)) + " TiB" return strconv.Itoa(int(s/TiB)) + " TiB"
@ -145,6 +140,14 @@ func (fs *FS) FileSize(name string) (size string) {
return strconv.Itoa(int(s)) + " B" return strconv.Itoa(int(s)) + " B"
} }
func (fs *FS) FileSize(name string) string {
fi, err := fs.Stat(name)
if err != nil {
return ""
}
return FileSize(fi.Size())
}
func (fs *FS) RemoveDst(name string) error { func (fs *FS) RemoveDst(name string) error {
return fs.dst.Remove(name) return fs.dst.Remove(name)
} }

View file

@ -4,7 +4,10 @@ package fs
import ( import (
"context" "context"
"io"
"strconv"
"sync" "sync"
"sync/atomic"
"time" "time"
"github.com/go-logr/logr" "github.com/go-logr/logr"
@ -54,14 +57,89 @@ func (q *queue) GetPreload(name string) *Preload {
return nil return nil
} }
type Rate struct {
r io.Reader
done chan struct{}
rate atomic.Pointer[string]
num int64
last int64
interval int
}
var zeroRate = fmtRate(0)
func newRate() *Rate {
r := &Rate{
done: make(chan struct{}),
interval: 10,
}
r.rate.Store(&zeroRate)
return r
}
func (r *Rate) SetReader(reader io.Reader) {
r.r = reader
go r.sampler()
}
func (r *Rate) Read(b []byte) (n int, err error) {
n, err = r.r.Read(b)
atomic.AddInt64(&r.num, int64(n))
return
}
func fmtRate(r int) string {
switch {
case r >= GiB:
return strconv.FormatFloat(float64(r)/GiB, 'f', 2, 64) + " GiB/s"
case r >= MiB:
return strconv.FormatFloat(float64(r)/MiB, 'f', 2, 64) + " MiB/s"
case r >= KiB:
return strconv.FormatFloat(float64(r)/KiB, 'f', 2, 64) + " KiB/s"
}
return strconv.Itoa(r) + " B/s"
}
func (r *Rate) sampler() {
t := time.NewTicker(time.Duration(r.interval) * time.Second)
for {
select {
case <-r.done:
t.Stop()
return
case <-t.C:
num := atomic.LoadInt64(&r.num)
rate := int(num-r.last) / r.interval
r.last = num
s := fmtRate(rate)
r.rate.Store(&s)
}
}
}
func (r *Rate) String() string {
s := r.rate.Load()
if s == nil {
return ""
}
return *s
}
func (r *Rate) Stop() {
close(r.done)
r.rate.Store(&zeroRate)
}
type Preload struct { type Preload struct {
ph *PreloadHandler ph *PreloadHandler
rate *Rate
delay time.Duration delay time.Duration
Name string Name string
cancel func() cancel func()
Prio int Prio int
Status int Status int
Errc int Errc int
Size int64
Running bool Running bool
} }
@ -132,6 +210,13 @@ func (ph *PreloadHandler) schedule() {
} }
} }
func (p *Preload) Rate() string {
if p.rate != nil {
return p.rate.String()
}
return ""
}
func (p *Preload) start() { func (p *Preload) start() {
if p.Running { if p.Running {
return return
@ -153,7 +238,10 @@ func (p *Preload) start() {
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
p.Running = true p.Running = true
p.cancel = cancel p.cancel = cancel
p.rate = newRate()
p.Size = f.size()
go f.Preload(ctx, go f.Preload(ctx,
p.rate,
func() { func() {
p.ph.fin <- name p.ph.fin <- name
}, },

View file

@ -60,6 +60,7 @@ type file struct {
URI template.HTML URI template.HTML
Anchor string Anchor string
Size string Size string
Rate string
Status int Status int
Prio int Prio int
Errc int Errc int
@ -95,7 +96,7 @@ func (r *responseInterceptor) Status() int {
return r.status return r.status
} }
func (r *responseInterceptor) GetPaths(path string, fs *fs.FS, relative bool) (dc dirContents, err error) { func (r *responseInterceptor) GetPaths(path string, filesystem *fs.FS, relative bool) (dc dirContents, err error) {
buf := r.buf.Bytes() buf := r.buf.Bytes()
buf = bytes.ReplaceAll(buf, []byte{'&'}, []byte("&#38;")) buf = bytes.ReplaceAll(buf, []byte{'&'}, []byte("&#38;"))
err = xml.Unmarshal(buf, &dc) err = xml.Unmarshal(buf, &dc)
@ -123,8 +124,8 @@ func (r *responseInterceptor) GetPaths(path string, fs *fs.FS, relative bool) (d
Name: name, Name: name,
URI: uri, URI: uri,
Anchor: anchor(p), Anchor: anchor(p),
Size: fs.FileSize(full), Size: filesystem.FileSize(full),
Status: fs.CacheStatus(full), Status: filesystem.CacheStatus(full),
}) })
} }
} }
@ -133,12 +134,14 @@ func (r *responseInterceptor) GetPaths(path string, fs *fs.FS, relative bool) (d
return return
} }
func getPreloads(path string, fs *fs.FS) (dc dirContents) { func getPreloads(path string, filesystem *fs.FS) (dc dirContents) {
dc.Base = path dc.Base = path
for _, p := range fs.Preloads() { for _, p := range filesystem.Preloads() {
dc.Files = append(dc.Files, file{ dc.Files = append(dc.Files, file{
Name: template.HTML(p.Name), Name: template.HTML(p.Name),
URI: template.HTML(p.Name), URI: template.HTML(p.Name),
Size: fs.FileSize(p.Size),
Rate: p.Rate(),
Status: p.Status, Status: p.Status,
Prio: p.Prio, Prio: p.Prio,
Errc: p.Errc, Errc: p.Errc,

View file

@ -24,6 +24,7 @@ Quota: {{.QuotaCur}} / {{.QuotaMax}} GiB
<span class="status-yellow">[queued]</span> <span class="status-yellow">[queued]</span>
{{end -}} {{end -}}
{{end -}} {{end -}}
<span>{{$s.Rate}}</span>
<span>{{$s.Size}}</span> <span>{{$s.Size}}</span>
<span>{{$s.Prio}}</span><a href="{{$s.URI}}?o=v">[v]</a><a href="{{$s.Name}}?o=s&r=preloads">[s]</a><a href="{{$s.Name}}?o=i&r=preloads">[+]</a><a href="{{$s.Name}}?o=d&r=preloads">[-]</a> <span>{{$s.Prio}}</span><a href="{{$s.URI}}?o=v">[v]</a><a href="{{$s.Name}}?o=s&r=preloads">[s]</a><a href="{{$s.Name}}?o=i&r=preloads">[+]</a><a href="{{$s.Name}}?o=d&r=preloads">[-]</a>
</td> </td>