accounting: cull the old time ranges when possible to save memory

This commit is contained in:
Nick Craig-Wood 2019-10-17 11:43:32 +01:00
parent d40972bf1a
commit 13e46c4b3f
2 changed files with 113 additions and 9 deletions

View file

@ -37,8 +37,9 @@ type StatsInfo struct {
renameQueueSize int64
deletes int64
inProgress *inProgress
startedTransfers []*Transfer // currently active transfers
oldTimeRanges timeRanges // a merged list of time ranges for the transfers
startedTransfers []*Transfer // currently active transfers
oldTimeRanges timeRanges // a merged list of time ranges for the transfers
oldDuration time.Duration // duration of transfers we have culled
}
// NewStats creates an initialised StatsInfo
@ -155,6 +156,21 @@ func (trs *timeRanges) merge() {
*trs = newTrs
}
// cull remove any ranges whose start and end are before cutoff
// returning their duration sum
func (trs *timeRanges) cull(cutoff time.Time) (d time.Duration) {
var newTrs = (*trs)[:0]
for _, tr := range *trs {
if cutoff.Before(tr.start) || cutoff.Before(tr.end) {
newTrs = append(newTrs, tr)
} else {
d += tr.end.Sub(tr.start)
}
}
*trs = newTrs
return d
}
// total the time out of the time ranges
func (trs timeRanges) total() (total time.Duration) {
for _, tr := range trs {
@ -182,7 +198,7 @@ func (s *StatsInfo) totalDuration() time.Duration {
}
timeRanges.merge()
return timeRanges.total()
return s.oldDuration + timeRanges.total()
}
// eta returns the ETA of the current operation,
@ -436,6 +452,7 @@ func (s *StatsInfo) ResetCounters() {
s.transfers = 0
s.deletes = 0
s.startedTransfers = nil
s.oldDuration = 0
}
// ResetErrors sets the errors count to 0 and resets lastError, fatalError and retryError
@ -568,16 +585,30 @@ func (s *StatsInfo) AddTransfer(transfer *Transfer) {
//
// Must be called with the lock held
func (s *StatsInfo) removeTransfer(transfer *Transfer, i int) {
now := time.Now()
// add finished transfer onto old time ranges
start, end := transfer.TimeRange()
if end.IsZero() {
end = time.Now()
end = now
}
s.oldTimeRanges = append(s.oldTimeRanges, timeRange{start, end})
s.oldTimeRanges.merge()
// remove the found entry
s.startedTransfers = append(s.startedTransfers[:i], s.startedTransfers[i+1:]...)
// Find youngest active transfer
oldestStart := now
for i := range s.startedTransfers {
start, _ := s.startedTransfers[i].TimeRange()
if start.Before(oldestStart) {
oldestStart = start
}
}
// remove old entries older than that
s.oldDuration += s.oldTimeRanges.cull(oldestStart)
}
// RemoveTransfer removes a reference to the started transfer.

View file

@ -254,6 +254,14 @@ func makeTimeRanges(t *testing.T, in []string) timeRanges {
return trs
}
func (trs timeRanges) toStrings() (out []string) {
out = []string{}
for _, tr := range trs {
out = append(out, fmt.Sprintf("%d-%d", tr.start.Unix(), tr.end.Unix()))
}
return out
}
func TestTimeRangeMerge(t *testing.T) {
for _, test := range []struct {
in []string
@ -293,15 +301,80 @@ func TestTimeRangeMerge(t *testing.T) {
in := makeTimeRanges(t, test.in)
in.merge()
got := []string{}
for _, tr := range in {
got = append(got, fmt.Sprintf("%d-%d", tr.start.Unix(), tr.end.Unix()))
}
got := in.toStrings()
assert.Equal(t, test.want, got)
}
}
func TestTimeRangeCull(t *testing.T) {
for _, test := range []struct {
in []string
cutoff int64
want []string
wantDuration time.Duration
}{{
in: []string{},
cutoff: 1,
want: []string{},
wantDuration: 0 * time.Second,
}, {
in: []string{"1-2"},
cutoff: 1,
want: []string{"1-2"},
wantDuration: 0 * time.Second,
}, {
in: []string{"2-5", "7-9"},
cutoff: 1,
want: []string{"2-5", "7-9"},
wantDuration: 0 * time.Second,
}, {
in: []string{"2-5", "7-9"},
cutoff: 4,
want: []string{"2-5", "7-9"},
wantDuration: 0 * time.Second,
}, {
in: []string{"2-5", "7-9"},
cutoff: 5,
want: []string{"7-9"},
wantDuration: 3 * time.Second,
}, {
in: []string{"2-5", "7-9", "2-5", "2-5"},
cutoff: 6,
want: []string{"7-9"},
wantDuration: 9 * time.Second,
}, {
in: []string{"7-9", "3-3", "2-5"},
cutoff: 7,
want: []string{"7-9"},
wantDuration: 3 * time.Second,
}, {
in: []string{"2-5", "7-9"},
cutoff: 8,
want: []string{"7-9"},
wantDuration: 3 * time.Second,
}, {
in: []string{"2-5", "7-9"},
cutoff: 9,
want: []string{},
wantDuration: 5 * time.Second,
}, {
in: []string{"2-5", "7-9"},
cutoff: 10,
want: []string{},
wantDuration: 5 * time.Second,
}} {
in := makeTimeRanges(t, test.in)
cutoff := time.Unix(test.cutoff, 0)
gotDuration := in.cull(cutoff)
what := fmt.Sprintf("in=%q, cutoff=%d", test.in, test.cutoff)
got := in.toStrings()
assert.Equal(t, test.want, got, what)
assert.Equal(t, test.wantDuration, gotDuration, what)
}
}
func TestTimeRangeDuration(t *testing.T) {
assert.Equal(t, 0*time.Second, timeRanges{}.total())
assert.Equal(t, 1*time.Second, makeTimeRanges(t, []string{"1-2"}).total())