-
Notifications
You must be signed in to change notification settings - Fork 390
/
audithistory.go
169 lines (148 loc) · 5.58 KB
/
audithistory.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
// Copyright (C) 2021 Storj Labs, Inc.
// See LICENSE for copying information.
package reputation
import (
"time"
"storj.io/common/pb"
)
// UpdateAuditHistoryResponse contains information returned by UpdateAuditHistory.
type UpdateAuditHistoryResponse struct {
NewScore float64
TrackingPeriodFull bool
History []byte
}
// DuplicateAuditHistory creates a duplicate (deep copy) of an AuditHistory object.
func DuplicateAuditHistory(auditHistory *pb.AuditHistory) *pb.AuditHistory {
// argument is not a pointer type, so auditHistory is already a copy.
// Just need to copy the slice.
if auditHistory == nil {
return nil
}
windows := make([]*pb.AuditWindow, len(auditHistory.Windows))
for i := range windows {
windows[i] = &pb.AuditWindow{}
*windows[i] = *auditHistory.Windows[i]
}
auditHistory.Windows = windows
return auditHistory
}
// AddAuditToHistory adds a single online/not-online event to an AuditHistory.
// If the AuditHistory contains windows that are now outside the tracking
// period, those windows will be trimmed.
func AddAuditToHistory(a *pb.AuditHistory, online bool, auditTime time.Time, config AuditHistoryConfig) error {
newAuditWindowStartTime := auditTime.Truncate(config.WindowSize)
earliestWindow := newAuditWindowStartTime.Add(-config.TrackingPeriod)
// windowsModified is used to determine whether we will need to recalculate the score because windows have been added or removed.
windowsModified := false
// delete windows outside of tracking period scope
updatedWindows := a.Windows
for i, window := range a.Windows {
if window.WindowStart.Before(earliestWindow) {
updatedWindows = a.Windows[i+1:]
windowsModified = true
} else {
// windows are in order, so if this window is in the tracking period, we are done deleting windows
break
}
}
a.Windows = updatedWindows
// if there are no windows or the latest window has passed, add another window
if len(a.Windows) == 0 || a.Windows[len(a.Windows)-1].WindowStart.Before(newAuditWindowStartTime) {
windowsModified = true
a.Windows = append(a.Windows, &pb.AuditWindow{WindowStart: newAuditWindowStartTime})
}
latestIndex := len(a.Windows) - 1
if a.Windows[latestIndex].WindowStart.After(newAuditWindowStartTime) {
return Error.New("cannot add audit to audit history; window already passed")
}
// add new audit to latest window
if online {
a.Windows[latestIndex].OnlineCount++
}
a.Windows[latestIndex].TotalCount++
// if no windows were added or removed, score does not change
if !windowsModified {
return nil
}
if len(a.Windows) <= 1 {
a.Score = 1
return nil
}
totalWindowScores := 0.0
for i, window := range a.Windows {
// do not include last window in score
if i+1 == len(a.Windows) {
break
}
totalWindowScores += float64(window.OnlineCount) / float64(window.TotalCount)
}
// divide by number of windows-1 because last window is not included
a.Score = totalWindowScores / float64(len(a.Windows)-1)
return nil
}
// MergeAuditHistories merges two audit histories into one, including all
// windows that are present in either input and summing counts for
// any windows that appear in _both_ inputs. Any windows that are now outside
// the tracking period will be trimmed.
//
// The history parameter will be mutated to include the windows passed as
// addHistory.
//
// Returns true if the number of windows in the new history is the maximum
// possible for the tracking config.
func MergeAuditHistories(history *pb.AuditHistory, addHistory []*pb.AuditWindow, config AuditHistoryConfig) (trackingPeriodFull bool) {
windows := history.Windows
for addIndex, windowIndex := 0, 0; addIndex < len(addHistory); {
switch {
case windowIndex == len(windows):
windows = append(windows, &pb.AuditWindow{
WindowStart: addHistory[addIndex].WindowStart,
})
fallthrough
case windows[windowIndex].WindowStart.Equal(addHistory[addIndex].WindowStart):
windows[windowIndex].TotalCount += addHistory[addIndex].TotalCount
windows[windowIndex].OnlineCount += addHistory[addIndex].OnlineCount
addIndex++
case windows[windowIndex].WindowStart.Before(addHistory[addIndex].WindowStart):
windowIndex++
case windows[windowIndex].WindowStart.After(addHistory[addIndex].WindowStart):
windows = append(windows[:windowIndex+1], windows[windowIndex:]...)
windows[windowIndex] = &pb.AuditWindow{
WindowStart: addHistory[addIndex].WindowStart,
TotalCount: addHistory[addIndex].TotalCount,
OnlineCount: addHistory[addIndex].OnlineCount,
}
addIndex++
}
}
// trim off windows that are too old
if len(windows) > 0 {
cutoffTime := windows[len(windows)-1].WindowStart.Add(-config.TrackingPeriod)
for len(windows) > 0 && windows[0].WindowStart.Before(cutoffTime) {
windows = windows[1:]
}
}
history.Windows = windows
RecalculateScore(history)
windowsPerTrackingPeriod := int(config.TrackingPeriod.Seconds() / config.WindowSize.Seconds())
trackingPeriodFull = len(history.Windows)-1 >= windowsPerTrackingPeriod
return trackingPeriodFull
}
// RecalculateScore calculates and assigns the Score field in a pb.AuditHistory object.
// The score is calculated by averaging the online percentage in each window
// (not including the last).
func RecalculateScore(history *pb.AuditHistory) {
if len(history.Windows) <= 1 {
history.Score = 1
return
}
totalWindowScores := float64(0)
for i, window := range history.Windows {
// do not include last window in score
if i+1 == len(history.Windows) {
break
}
totalWindowScores += float64(window.OnlineCount) / float64(window.TotalCount)
}
history.Score = totalWindowScores / float64(len(history.Windows)-1)
}