Skip to content

Commit

Permalink
fix(progress): Make progress bars more responsive
Browse files Browse the repository at this point in the history
  • Loading branch information
gabe565 committed Oct 19, 2023
1 parent e6902bf commit ea42e12
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 78 deletions.
1 change: 1 addition & 0 deletions internal/actions/dump/dump.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ func (action Dump) Run(ctx context.Context) (err error) {
startTime := time.Now()

bar := progressbar.New(-1, "downloading", action.Spinner)
defer bar.Close()
plogger := progressbar.NewBarSafeLogger(os.Stderr, bar)
log.SetOutput(plogger)

Expand Down
1 change: 1 addition & 0 deletions internal/actions/restore/restore.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ func (action Restore) Run(ctx context.Context) (err error) {
startTime := time.Now()

bar := progressbar.New(-1, "uploading", action.Spinner)
defer bar.Close()
errLog := progressbar.NewBarSafeLogger(os.Stderr, bar)
outLog := progressbar.NewBarSafeLogger(os.Stdout, bar)
log.SetOutput(errLog)
Expand Down
37 changes: 37 additions & 0 deletions internal/progressbar/logger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package progressbar

import (
"bytes"
"io"
)

func NewBarSafeLogger(w io.Writer, bar *ProgressBar) *BarSafeLogger {
return &BarSafeLogger{
out: w,
bar: bar,
}
}

type BarSafeLogger struct {
out io.Writer
bar *ProgressBar
buf bytes.Buffer
}

func (l *BarSafeLogger) Write(p []byte) (int, error) {
if l.bar.IsFinished() {
return l.out.Write(p)
}

l.buf.Write([]byte("\r\x1B[K"))
l.buf.Write(p)
if p[len(p)-1] == '\n' {
l.buf.WriteString(l.bar.String())
}
l.bar.mu.Lock()
defer l.bar.mu.Unlock()
if n, err := io.Copy(l.out, &l.buf); err != nil {
return int(n), err
}
return len(p), nil
}
78 changes: 0 additions & 78 deletions internal/progressbar/new.go

This file was deleted.

92 changes: 92 additions & 0 deletions internal/progressbar/progressbar.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package progressbar

import (
"fmt"
"io"
"os"
"sync"
"time"

"github.com/clevyr/kubedb/internal/config/flags"
"github.com/gabe565/go-spinners"
"github.com/mattn/go-isatty"
"github.com/schollz/progressbar/v3"
log "github.com/sirupsen/logrus"
)

func New(max int64, label string, spinnerKey string) *ProgressBar {
s, ok := spinner.Map[spinnerKey]
if !ok {
log.WithField("spinner", spinnerKey).Warn("invalid spinner")
s = spinner.Map[flags.DefaultSpinner]
}

options := []progressbar.Option{
progressbar.OptionSetDescription(label),
progressbar.OptionSetWriter(io.Discard),
progressbar.OptionShowBytes(true),
progressbar.OptionSetWidth(10),
progressbar.OptionShowCount(),
progressbar.OptionSpinnerCustom(s.Frames),
progressbar.OptionFullWidth(),
}

var throttle time.Duration
if isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd()) {
throttle = 65 * time.Millisecond
options = append(options,
progressbar.OptionSetRenderBlankState(true),
progressbar.OptionOnCompletion(func() {
_, _ = fmt.Fprint(os.Stderr, "\r\x1B[K")
}),
)
} else {
throttle = 2 * time.Second
}
options = append(options,
progressbar.OptionThrottle(throttle),
)

cancelChan := make(chan struct{})
bar := &ProgressBar{
ProgressBar: progressbar.NewOptions64(max, options...),
cancelChan: cancelChan,
}
go func() {
for {
select {
case <-cancelChan:
return
case <-time.After(throttle):
if bar.IsFinished() {
return
}
if bar.mu.TryLock() {
_ = bar.RenderBlank()
_, _ = os.Stderr.Write([]byte(bar.String()))
bar.mu.Unlock()
}
}
}
}()

return bar
}

type ProgressBar struct {
*progressbar.ProgressBar
mu sync.Mutex
cancelChan chan struct{}
cancelOnce sync.Once
}

func (p *ProgressBar) Finish() error {
p.Close()
return p.ProgressBar.Finish()
}

func (p *ProgressBar) Close() {
p.cancelOnce.Do(func() {
close(p.cancelChan)
})
}

0 comments on commit ea42e12

Please sign in to comment.