in internal/buildstats/buildstats.go [179:313]
func SyncSpans(ctx context.Context, env *buildenv.Environment) error {
bq, err := bigquery.NewClient(ctx, env.ProjectName)
if err != nil {
log.Fatal(err)
}
defer bq.Close()
table := bq.Dataset("builds").Table("Spans")
meta, err := table.Metadata(ctx)
if ae, ok := err.(*googleapi.Error); ok && ae.Code == 404 {
log.Printf("Creating table Spans...")
err = table.Create(ctx, nil)
if err == nil {
meta, err = table.Metadata(ctx)
}
}
if err != nil {
return fmt.Errorf("Metadata: %#v", err)
}
if Verbose {
log.Printf("buildstats: Spans metadata: %#v", meta)
}
schema := meta.Schema
if len(schema) == 0 {
if Verbose {
log.Printf("EMPTY SCHEMA")
}
schema, err = bigquery.InferSchema(types.SpanRecord{})
if err != nil {
return fmt.Errorf("InferSchema: %v", err)
}
blindWrite := ""
meta, err := table.Update(ctx, bigquery.TableMetadataToUpdate{Schema: schema}, blindWrite)
if err != nil {
return fmt.Errorf("table.Update schema: %v", err)
}
schema = meta.Schema
}
if Verbose {
for i, fs := range schema {
log.Printf(" schema[%v]: %+v", i, fs)
for j, fs := range fs.Schema {
log.Printf(" .. schema[%v]: %+v", j, fs)
}
}
}
q := bq.Query("SELECT MAX(EndTime) FROM builds.Spans")
it, err := q.Read(ctx)
if err != nil {
return fmt.Errorf("Read: %v", err)
}
var since time.Time
var values []bigquery.Value
if err := it.Next(&values); err != nil {
if err == iterator.Done {
return fmt.Errorf("Expected at least one row fro MAX(EndTime) query; got none.")
}
return fmt.Errorf("Next: %v", err)
}
switch t := values[0].(type) {
case nil:
// NULL. No rows.
log.Printf("starting from the beginning...")
case time.Time:
since = values[0].(time.Time)
default:
return fmt.Errorf("MAX(EndType) = %T: want nil or time.Time", t)
}
if since.IsZero() {
since = time.Unix(1, 0) // arbitrary
}
ds, err := datastore.NewClient(ctx, env.ProjectName)
if err != nil {
return fmt.Errorf("datastore.NewClient: %v", err)
}
defer ds.Close()
up := table.Uploader()
if Verbose {
log.Printf("buildstats: Span max time: %v", since)
}
dsit := ds.Run(ctx, datastore.NewQuery("Span").Filter("EndTime >", since).Order("EndTime"))
var maxPut time.Time
for {
n := 0
var rows []*bigquery.ValuesSaver
for {
var s types.SpanRecord
key, err := dsit.Next(&s)
if err == iterator.Done {
break
}
n++
if err != nil {
log.Fatal(err)
}
if s.EndTime.IsZero() {
return fmt.Errorf("got zero endtime")
}
var row []bigquery.Value
var putSchema bigquery.Schema
rv := reflect.ValueOf(s)
for _, fs := range meta.Schema {
if fs.Name[0] == '_' {
continue
}
putSchema = append(putSchema, fs)
row = append(row, rv.FieldByName(fs.Name).Interface())
maxPut = s.EndTime
}
rows = append(rows, &bigquery.ValuesSaver{
Schema: putSchema,
InsertID: key.Encode(),
Row: row,
})
if len(rows) == 1000 {
break
}
}
if n == 0 {
return nil
}
err = up.Put(ctx, rows)
log.Printf("buildstats: Spans sync put %d rows, up to %v. error = %v", len(rows), maxPut, err)
if err != nil {
return err
}
}
}