github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/syz-cluster/pkg/db/report_repo.go (about) 1 // Copyright 2025 syzkaller project authors. All rights reserved. 2 // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4 package db 5 6 import ( 7 "context" 8 "crypto/rand" 9 "encoding/hex" 10 "fmt" 11 12 "cloud.google.com/go/spanner" 13 ) 14 15 type ReportRepository struct { 16 client *spanner.Client 17 *genericEntityOps[SessionReport, string] 18 } 19 20 func NewReportRepository(client *spanner.Client) *ReportRepository { 21 return &ReportRepository{ 22 client: client, 23 genericEntityOps: &genericEntityOps[SessionReport, string]{ 24 client: client, 25 keyField: "ID", 26 table: "SessionReports", 27 }, 28 } 29 } 30 31 func (repo *ReportRepository) Insert(ctx context.Context, rep *SessionReport) error { 32 if rep.ID != "" { 33 return repo.genericEntityOps.Insert(ctx, rep) 34 } 35 const attempts = 3 36 for i := 0; i < attempts; i++ { 37 var err error 38 rep.ID, err = randomReportID() 39 if err != nil { 40 return err 41 } 42 err = repo.genericEntityOps.Insert(ctx, rep) 43 if err == errEntityExists { 44 continue 45 } 46 return err 47 } 48 // We shouldn't be getting here until we have sent out billions of reports. 49 // But let's return some error to still exit gracefully. 50 return fmt.Errorf("failed to pick a non-existing report ID") 51 } 52 53 // nolint: dupl 54 func (repo *ReportRepository) ListNotReported(ctx context.Context, reporter string, 55 limit int) ([]*SessionReport, error) { 56 stmt := spanner.Statement{ 57 SQL: "SELECT * FROM `SessionReports` WHERE `Reporter` = @reporter AND `ReportedAt` IS NULL", 58 Params: map[string]interface{}{ 59 "reporter": reporter, 60 }, 61 } 62 addLimit(&stmt, limit) 63 return repo.readEntities(ctx, stmt) 64 } 65 66 // As report ID may be included in the email address, we'd prefer it to be shorter than a typical UUID. 67 // A 16 byte hex ID should be good enough. 68 func randomReportID() (string, error) { 69 data := make([]byte, 8) 70 if _, err := rand.Read(data); err != nil { 71 return "", err 72 } 73 return hex.EncodeToString(data), nil 74 }