git/githistory/log: enforce throttle per log line

This commit is contained in:
Taylor Blau 2017-06-16 10:39:17 -06:00
parent 930c1510e0
commit fc1faca25a
2 changed files with 77 additions and 5 deletions

@ -6,11 +6,16 @@ import (
"io/ioutil"
"strings"
"sync"
"time"
"github.com/git-lfs/git-lfs/tools"
"github.com/olekukonko/ts"
)
const (
DefaultLoggingThrottle = 200 * time.Millisecond
)
// Logger logs a series of tasks to an io.Writer, processing each task in order
// until completion .
type Logger struct {
@ -21,6 +26,10 @@ type Logger struct {
// this logger is running within.
widthFn func() int
// throttle is the minimum amount of time that must pass between each
// instant data is logged.
throttle time.Duration
// queue is the incoming, unbuffered queue of tasks to enqueue.
queue chan Task
// tasks is the set of tasks to process.
@ -38,7 +47,8 @@ func NewLogger(sink io.Writer) *Logger {
}
l := &Logger{
sink: sink,
sink: sink,
throttle: DefaultLoggingThrottle,
widthFn: func() int {
size, err := ts.GetSize()
if err != nil {
@ -168,12 +178,19 @@ func (l *Logger) consume() {
func (l *Logger) logTask(task Task) {
defer l.wg.Done()
var last string
for last = range task.Updates() {
l.logLine(last)
var last time.Time
var msg string
for msg = range task.Updates() {
now := time.Now()
if now.After(last.Add(l.throttle)) {
l.logLine(msg)
last = now
}
}
l.log(fmt.Sprintf("%s, done\n", last))
l.log(fmt.Sprintf("%s, done\n", msg))
}
// logLine writes a complete line and moves the cursor to the beginning of the

@ -4,6 +4,7 @@ import (
"bytes"
"strings"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
@ -23,6 +24,7 @@ func TestLoggerLogsTasks(t *testing.T) {
}()
l := NewLogger(&buf)
l.throttle = 0
l.widthFn = func() int { return 0 }
l.enqueue(ChanTask(task))
l.Close()
@ -47,6 +49,7 @@ func TestLoggerLogsMultipleTasksInOrder(t *testing.T) {
}()
l := NewLogger(&buf)
l.throttle = 0
l.widthFn = func() int { return 0 }
l.enqueue(ChanTask(t1), ChanTask(t2))
l.Close()
@ -65,6 +68,7 @@ func TestLoggerLogsMultipleTasksWithoutBlocking(t *testing.T) {
var buf bytes.Buffer
l := NewLogger(&buf)
l.throttle = 0
t1, t2 := make(chan string), make(chan string)
l.widthFn = func() int { return 0 }
@ -85,3 +89,54 @@ func TestLoggerLogsMultipleTasksWithoutBlocking(t *testing.T) {
"second, done\n",
}, ""), buf.String())
}
func TestLoggerThrottlesWrites(t *testing.T) {
var buf bytes.Buffer
t1 := make(chan string)
go func() {
t1 <- "first" // t = 0 ms, throttle was open
time.Sleep(3 * time.Millisecond) // t = 3 ms, throttle is closed
t1 <- "second" // t = 3+ε ms, throttle is closed
time.Sleep(3 * time.Millisecond) // t = 6 ms, throttle is open
t1 <- "third" // t = 6+ε ms, throttle was open
close(t1) // t = 6+2ε ms, throttle is closed
}()
l := NewLogger(&buf)
l.widthFn = func() int { return 0 }
l.throttle = 5 * time.Millisecond
l.enqueue(ChanTask(t1))
l.Close()
assert.Equal(t, strings.Join([]string{
"first\r",
"third\r",
"third, done\n",
}, ""), buf.String())
}
func TestLoggerThrottlesLastWrite(t *testing.T) {
var buf bytes.Buffer
t1 := make(chan string)
go func() {
t1 <- "first" // t = 0 ms, throttle was open
time.Sleep(3 * time.Millisecond) // t = 3 ms, throttle is closed
t1 <- "second" // t = 3+ε ms, throttle is closed
close(t1) // t = 3+2ε ms, throttle is closed
}()
l := NewLogger(&buf)
l.widthFn = func() int { return 0 }
l.throttle = 5 * time.Millisecond
l.enqueue(ChanTask(t1))
l.Close()
assert.Equal(t, strings.Join([]string{
"first\r",
"second, done\n",
}, ""), buf.String())
}