in internal/worker/update.go [62:143]
func (u *updater) update(ctx context.Context) (ur *store.CommitUpdateRecord, err error) {
// We want the action of reading the old DB record, updating it and
// writing it back to be atomic. It would be too expensive to do that one
// record at a time. Ideally we'd process the whole repo commit in one
// transaction, but Firestore has a limit on how many writes one
// transaction can do, so the CVE files in the repo are processed in
// batches, one transaction per batch.
defer derrors.Wrap(&err, "updater.update(%s)", u.commit.Hash)
ctx = event.Start(ctx, "updater.update")
defer event.End(ctx)
defer func() {
if err != nil {
log.Errorf(ctx, "update failed: %v", err)
} else {
var nAdded, nModified int64
if ur != nil {
nAdded = int64(ur.NumAdded)
nModified = int64(ur.NumModified)
}
log.Infof(ctx, "update succeeded on %s: added %d, modified %d",
u.commit.Hash, nAdded, nModified)
}
}()
log.Infof(ctx, "update starting on %s", u.commit.Hash)
// Get all the CVE files.
// It is cheaper to read all the files from the repo and compare
// them to the DB in bulk, than to walk the repo and process
// each file individually.
files, err := cvelistrepo.Files(u.repo, u.commit)
if err != nil {
return nil, err
}
// Process files in the same directory together, so we can easily skip
// the entire directory if it hasn't changed.
filesByDir, err := groupFilesByDirectory(files)
if err != nil {
return nil, err
}
// Create a new CommitUpdateRecord to describe this run of doUpdate.
ur = &store.CommitUpdateRecord{
StartedAt: time.Now(),
CommitHash: u.commit.Hash.String(),
CommitTime: u.commit.Committer.When,
NumTotal: len(files),
}
if err := u.st.CreateCommitUpdateRecord(ctx, ur); err != nil {
return ur, err
}
var skippedDirs []string
const logSkippedEvery = 20 // Log a message every this many skipped directories.
for _, dirFiles := range filesByDir {
stats, err := u.updateDirectory(ctx, dirFiles)
// Change the CommitUpdateRecord in the Store to reflect the results of the directory update.
if err != nil {
ur.Error = err.Error()
if err2 := u.st.SetCommitUpdateRecord(ctx, ur); err2 != nil {
return ur, fmt.Errorf("update failed with %w, could not set update record: %v", err, err2)
}
}
if stats.skipped {
skippedDirs = append(skippedDirs, dirFiles[0].DirPath)
if len(skippedDirs) >= logSkippedEvery {
log.Infof(ctx, "skipping directory %s and %d others because the hashes match",
skippedDirs[0], len(skippedDirs)-1)
skippedDirs = nil
}
}
ur.NumProcessed += stats.numProcessed
ur.NumAdded += stats.numAdded
ur.NumModified += stats.numModified
if err := u.st.SetCommitUpdateRecord(ctx, ur); err != nil {
return ur, err
}
}
ur.EndedAt = time.Now()
return ur, u.st.SetCommitUpdateRecord(ctx, ur)
}