OLD | NEW |
1 package syncer | 1 package syncer |
2 | 2 |
3 import ( | 3 import ( |
4 "fmt" | 4 "fmt" |
5 "sort" | 5 "sort" |
6 "strings" | 6 "strings" |
7 "sync" | 7 "sync" |
8 "time" | 8 "time" |
9 | 9 |
10 "github.com/skia-dev/glog" | 10 "github.com/skia-dev/glog" |
11 "go.skia.org/infra/fuzzer/go/common" | 11 "go.skia.org/infra/fuzzer/go/common" |
12 "go.skia.org/infra/fuzzer/go/config" | 12 "go.skia.org/infra/fuzzer/go/config" |
13 "go.skia.org/infra/fuzzer/go/frontend/gsloader" | 13 "go.skia.org/infra/fuzzer/go/frontend/gsloader" |
14 "go.skia.org/infra/go/gs" | 14 "go.skia.org/infra/go/gs" |
| 15 "go.skia.org/infra/go/util" |
15 "google.golang.org/cloud/storage" | 16 "google.golang.org/cloud/storage" |
16 ) | 17 ) |
17 | 18 |
18 // FuzzSyncer is a struct that will handle the syncing of bad/grey fuzzes. | 19 // FuzzSyncer is a struct that will handle the syncing of bad/grey fuzzes. |
19 // Once started, it will occasionally wake up and download any new fuzzes | 20 // Once started, it will occasionally wake up and download any new fuzzes |
20 // Clients should look at LastCount for the last count of fuzzes data. | 21 // Clients should look at LastCount for the last count of fuzzes data. |
21 type FuzzSyncer struct { | 22 type FuzzSyncer struct { |
22 countMutex sync.Mutex | 23 countMutex sync.Mutex |
23 storageClient *storage.Client | 24 storageClient *storage.Client |
24 gsLoader *gsloader.GSLoader | 25 gsLoader *gsloader.GSLoader |
25 » lastCount map[string]FuzzCount | 26 » lastCount map[string]FuzzCount // maps category->FuzzCount |
| 27 » fuzzNameCache map[string]util.StringSet // maps key->FuzzNames |
26 } | 28 } |
27 | 29 |
28 // FuzzCount is a struct that holds the counts of fuzzes. | 30 // FuzzCount is a struct that holds the counts of fuzzes. |
29 type FuzzCount struct { | 31 type FuzzCount struct { |
30 TotalBad int `json:"totalBadCount"` | 32 TotalBad int `json:"totalBadCount"` |
31 TotalGrey int `json:"totalGreyCount"` | 33 TotalGrey int `json:"totalGreyCount"` |
32 // "This" means "newly introduced/fixed in this revision" | 34 // "This" means "newly introduced/fixed in this revision" |
33 » ThisBad int `json:"thisBadCount"` | 35 » ThisBad int `json:"thisBadCount"` |
34 » ThisGrey int `json:"thisGreyCount"` | 36 » ThisRegression int `json:"thisRegressionCount"` |
35 } | 37 } |
36 | 38 |
37 // NewFuzzSyncer creates a FuzzSyncer and returns it. | 39 // NewFuzzSyncer creates a FuzzSyncer and returns it. |
38 func New(s *storage.Client) *FuzzSyncer { | 40 func New(s *storage.Client) *FuzzSyncer { |
39 return &FuzzSyncer{ | 41 return &FuzzSyncer{ |
40 storageClient: s, | 42 storageClient: s, |
41 lastCount: make(map[string]FuzzCount), | 43 lastCount: make(map[string]FuzzCount), |
| 44 fuzzNameCache: make(map[string]util.StringSet), |
42 } | 45 } |
43 } | 46 } |
44 | 47 |
45 // SetGSLoader sets this objects GSLoader, allowing it to fetch the new fuzzes i
t finds | 48 // SetGSLoader sets this objects GSLoader, allowing it to fetch the new fuzzes i
t finds |
46 // and update the fuzzes displayed to users. | 49 // and update the fuzzes displayed to users. |
47 func (f *FuzzSyncer) SetGSLoader(g *gsloader.GSLoader) { | 50 func (f *FuzzSyncer) SetGSLoader(g *gsloader.GSLoader) { |
48 f.gsLoader = g | 51 f.gsLoader = g |
49 } | 52 } |
50 | 53 |
51 // Start updates the LastCount and starts a timer with a period of config.FrontE
nd.FuzzSyncPeriod. | 54 // Start updates the LastCount and starts a timer with a period of config.FrontE
nd.FuzzSyncPeriod. |
(...skipping 13 matching lines...) Expand all Loading... |
65 func (f *FuzzSyncer) Refresh() { | 68 func (f *FuzzSyncer) Refresh() { |
66 glog.Info("Counting bad and grey fuzzes") | 69 glog.Info("Counting bad and grey fuzzes") |
67 currRevision := config.FrontEnd.SkiaVersion.Hash | 70 currRevision := config.FrontEnd.SkiaVersion.Hash |
68 prevRevision, err := f.getMostRecentOldRevision() | 71 prevRevision, err := f.getMostRecentOldRevision() |
69 if err != nil { | 72 if err != nil { |
70 glog.Infof("Problem getting most recent old version: %s", err) | 73 glog.Infof("Problem getting most recent old version: %s", err) |
71 return | 74 return |
72 } | 75 } |
73 f.countMutex.Lock() | 76 f.countMutex.Lock() |
74 defer f.countMutex.Unlock() | 77 defer f.countMutex.Unlock() |
75 » allCurrentNames := []string{} | 78 » allBadFuzzes := util.NewStringSet() |
76 | 79 |
77 for _, cat := range common.FUZZ_CATEGORIES { | 80 for _, cat := range common.FUZZ_CATEGORIES { |
78 lastCount := FuzzCount{} | 81 lastCount := FuzzCount{} |
79 currentBadNames, err := common.GetAllFuzzNamesInFolder(f.storage
Client, fmt.Sprintf("%s/%s/bad", cat, currRevision)) | |
80 if err != nil { | |
81 glog.Errorf("Problem getting total bad fuzz counts: %s",
err) | |
82 } else { | |
83 lastCount.TotalBad = len(currentBadNames) | |
84 allCurrentNames = append(allCurrentNames, currentBadName
s...) | |
85 } | |
86 | 82 |
87 » » if previousBadNames, err := common.GetAllFuzzNamesInFolder(f.sto
rageClient, fmt.Sprintf("%s/%s/bad", cat, prevRevision)); err != nil { | 83 » » // Previous fuzzes and current grey fuzzes can be drawn from the
cache, if they aren't there. |
88 » » » glog.Errorf("Problem getting this bad fuzz counts: %s",
err) | 84 » » previousGreyNames := f.getOrLookUpFuzzNames("grey", cat, prevRev
ision) |
89 » » } else { | 85 » » previousBadNames := f.getOrLookUpFuzzNames("bad", cat, prevRevis
ion) |
90 » » » lastCount.ThisBad = lastCount.TotalBad - len(previousBad
Names) | 86 » » currentGreyNames := f.getOrLookUpFuzzNames("grey", cat, currRevi
sion) |
91 » » } | 87 » » // always fetch current counts |
| 88 » » currentBadNames := f.getFuzzNames("bad", cat, currRevision) |
| 89 » » lastCount.TotalBad = len(currentBadNames) |
| 90 » » lastCount.TotalGrey = len(currentGreyNames) |
| 91 » » lastCount.ThisBad = len(currentBadNames.Complement(previousBadNa
mes).Complement(previousGreyNames)) |
| 92 » » lastCount.ThisRegression = len(previousGreyNames.Intersect(curre
ntBadNames)) |
| 93 » » allBadFuzzes = allBadFuzzes.Union(currentBadNames) |
92 | 94 |
93 if currentGreyNames, err := common.GetAllFuzzNamesInFolder(f.sto
rageClient, fmt.Sprintf("%s/%s/grey", cat, currRevision)); err != nil { | |
94 glog.Errorf("Problem getting total grey fuzz counts: %s"
, err) | |
95 } else { | |
96 lastCount.TotalGrey = len(currentGreyNames) | |
97 } | |
98 | |
99 if previousGreyNames, err := common.GetAllFuzzNamesInFolder(f.st
orageClient, fmt.Sprintf("%s/%s/grey", cat, prevRevision)); err != nil { | |
100 glog.Errorf("Problem getting this grey fuzz counts: %s",
err) | |
101 } else { | |
102 lastCount.ThisGrey = lastCount.TotalGrey - len(previousG
reyNames) | |
103 } | |
104 f.lastCount[cat] = lastCount | 95 f.lastCount[cat] = lastCount |
105 } | 96 } |
106 | 97 |
107 » if err = f.updateLoadedBinaryFuzzes(allCurrentNames); err != nil { | 98 » if err = f.updateLoadedBinaryFuzzes(allBadFuzzes.Keys()); err != nil { |
108 glog.Errorf("Problem updating loaded binary fuzzes: %s", err) | 99 glog.Errorf("Problem updating loaded binary fuzzes: %s", err) |
109 } | 100 } |
110 } | 101 } |
111 | 102 |
112 // getMostRecentOldRevision finds the most recently updated revision used. | 103 // getMostRecentOldRevision finds the most recently updated revision used. |
113 // It searches the GS bucket under skia_version/old/ An error is returned if th
ere is one. | 104 // It searches the GS bucket under skia_version/old/ An error is returned if th
ere is one. |
114 func (f *FuzzSyncer) getMostRecentOldRevision() (string, error) { | 105 func (f *FuzzSyncer) getMostRecentOldRevision() (string, error) { |
115 var newestTime time.Time | 106 var newestTime time.Time |
116 newestHash := "" | 107 newestHash := "" |
117 findNewest := func(item *storage.ObjectAttrs) { | 108 findNewest := func(item *storage.ObjectAttrs) { |
118 glog.Infof("%s: %s", item.Name, item.Updated) | 109 glog.Infof("%s: %s", item.Name, item.Updated) |
119 if newestTime.Before(item.Updated) { | 110 if newestTime.Before(item.Updated) { |
120 newestTime = item.Updated | 111 newestTime = item.Updated |
121 newestHash = item.Name[strings.LastIndex(item.Name, "/")
+1:] | 112 newestHash = item.Name[strings.LastIndex(item.Name, "/")
+1:] |
122 } | 113 } |
123 } | 114 } |
124 if err := gs.AllFilesInDir(f.storageClient, config.GS.Bucket, "skia_vers
ion/old/", findNewest); err != nil { | 115 if err := gs.AllFilesInDir(f.storageClient, config.GS.Bucket, "skia_vers
ion/old/", findNewest); err != nil { |
125 return "", err | 116 return "", err |
126 } | 117 } |
127 glog.Infof("Most recent old version found to be %s", newestHash) | 118 glog.Infof("Most recent old version found to be %s", newestHash) |
128 return newestHash, nil | 119 return newestHash, nil |
129 } | 120 } |
130 | 121 |
| 122 // getOrLookUpFuzzNames first checks the cache for a StringSet of fuzz names lin
ked to a given |
| 123 // tuple of fuzzType (bad or grey), category, revision. If such a thing is not
in the cache, it |
| 124 // fetches it via getFuzzNames() and caches it for next time. |
| 125 func (f *FuzzSyncer) getOrLookUpFuzzNames(fuzzType, category, revision string) u
til.StringSet { |
| 126 key := strings.Join([]string{fuzzType, category, revision}, "|") |
| 127 if s, has := f.fuzzNameCache[key]; has { |
| 128 return s |
| 129 } |
| 130 s := f.getFuzzNames(fuzzType, category, revision) |
| 131 // cache it |
| 132 f.fuzzNameCache[key] = s |
| 133 return s |
| 134 } |
| 135 |
| 136 // getFuzzNames gets all the fuzz names belonging to a fuzzType, category, revis
ion tuple from |
| 137 // Google Storage. It tries two different ways to do this, first by reading a |
| 138 // (bad|grey)_fuzz_names file, which exists in previous revisions. Second, it m
anually counts |
| 139 // all fuzzes in the given GCS folder. |
| 140 func (f *FuzzSyncer) getFuzzNames(fuzzType, category, revision string) util.Stri
ngSet { |
| 141 // The file stored, if it exists, is a pipe seperated list. |
| 142 if names, err := gs.FileContentsFromGS(f.storageClient, config.GS.Bucket
, fmt.Sprintf("%s/%s/%s_fuzz_names.txt", category, revision, fuzzType)); err ==
nil { |
| 143 return util.NewStringSet(strings.Split(string(names), "|")) |
| 144 } else { |
| 145 glog.Infof("Could not find cached names, downloading them the lo
ng way, instead: %s", err) |
| 146 } |
| 147 |
| 148 if names, err := common.GetAllFuzzNamesInFolder(f.storageClient, fmt.Spr
intf("%s/%s/%s", category, revision, fuzzType)); err != nil { |
| 149 glog.Errorf("Problem fetching %s %s fuzzes at revision %s: %s",
fuzzType, category, revision, err) |
| 150 return nil |
| 151 } else { |
| 152 return util.NewStringSet(names) |
| 153 } |
| 154 } |
| 155 |
131 // updateLoadedBinaryFuzzes uses gsLoader to download the fuzzes that are curren
tly not | 156 // updateLoadedBinaryFuzzes uses gsLoader to download the fuzzes that are curren
tly not |
132 // in the fuzz report tree / cache. | 157 // in the fuzz report tree / cache. |
133 func (f *FuzzSyncer) updateLoadedBinaryFuzzes(currentBadFuzzHashes []string) err
or { | 158 func (f *FuzzSyncer) updateLoadedBinaryFuzzes(currentBadFuzzHashes []string) err
or { |
134 if f.gsLoader == nil { | 159 if f.gsLoader == nil { |
135 glog.Info("Skipping update because the cache hasn't been set yet
") | 160 glog.Info("Skipping update because the cache hasn't been set yet
") |
136 return nil | 161 return nil |
137 } | 162 } |
138 prevBadFuzzNames, err := f.gsLoader.Cache.LoadFuzzNames(config.FrontEnd.
SkiaVersion.Hash) | 163 prevBadFuzzNames, err := f.gsLoader.Cache.LoadFuzzNames(config.FrontEnd.
SkiaVersion.Hash) |
139 if err != nil { | 164 if err != nil { |
140 return fmt.Errorf("Could not load previous fuzz hashes from cach
e at revision %s: %s", config.FrontEnd.SkiaVersion.Hash, err) | 165 return fmt.Errorf("Could not load previous fuzz hashes from cach
e at revision %s: %s", config.FrontEnd.SkiaVersion.Hash, err) |
(...skipping 12 matching lines...) Expand all Loading... |
153 glog.Infof("%d newly found fuzzes from Google Storage. Going to load th
em.", len(newBinaryFuzzNames)) | 178 glog.Infof("%d newly found fuzzes from Google Storage. Going to load th
em.", len(newBinaryFuzzNames)) |
154 if len(newBinaryFuzzNames) > 0 { | 179 if len(newBinaryFuzzNames) > 0 { |
155 return f.gsLoader.LoadFuzzesFromGoogleStorage(newBinaryFuzzNames
) | 180 return f.gsLoader.LoadFuzzesFromGoogleStorage(newBinaryFuzzNames
) |
156 } | 181 } |
157 return nil | 182 return nil |
158 } | 183 } |
159 | 184 |
160 func (f *FuzzSyncer) LastCount(category string) FuzzCount { | 185 func (f *FuzzSyncer) LastCount(category string) FuzzCount { |
161 return f.lastCount[category] | 186 return f.lastCount[category] |
162 } | 187 } |
OLD | NEW |