blob: 3ed574ab7c0fbf1ee074754b70e0094686d3fb9e [file] [log] [blame]
// Copyright 2023 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package bqexporter
import (
"time"
"cloud.google.com/go/bigquery"
"cloud.google.com/go/bigquery/storage/managedwriter/adapt"
"github.com/golang/protobuf/descriptor"
desc "github.com/golang/protobuf/protoc-gen-go/descriptor"
"google.golang.org/protobuf/types/descriptorpb"
bqpb "go.chromium.org/luci/bisection/proto/bq"
pb "go.chromium.org/luci/bisection/proto/v1"
"go.chromium.org/luci/bisection/util/bqutil"
bbpb "go.chromium.org/luci/buildbucket/proto"
"go.chromium.org/luci/common/bq"
)
// The table containing test analyses.
const testFailureAnalysesTableName = "test_failure_analyses"
const partitionExpirationTime = 540 * 24 * time.Hour // 540 days (~1.5 years).
// schemaApplyer ensures BQ schema matches the row proto definitions.
var schemaApplyer = bq.NewSchemaApplyer(bq.RegisterSchemaApplyerCache(5))
const rowMessage = "luci.bisection.proto.bq.TestAnalysisRow"
var tableMetadata *bigquery.TableMetadata
// tableSchemaDescriptor is a self-contained DescriptorProto for describing
// row protocol buffers sent to the BigQuery Write API.
var tableSchemaDescriptor *descriptorpb.DescriptorProto
func init() {
var err error
var schema bigquery.Schema
if schema, err = generateRowSchema(); err != nil {
panic(err)
}
if tableSchemaDescriptor, err = generateRowSchemaDescriptor(); err != nil {
panic(err)
}
tableMetadata = &bigquery.TableMetadata{
TimePartitioning: &bigquery.TimePartitioning{
Type: bigquery.DayPartitioningType,
Expiration: partitionExpirationTime,
Field: "created_time",
},
// Relax ensures no fields are marked "required".
Schema: schema.Relax(),
}
}
func generateRowSchema() (schema bigquery.Schema, err error) {
fd, _ := descriptor.MessageDescriptorProto(&bqpb.TestAnalysisRow{})
// We also need to get FileDescriptorProto for:
// - TestCulprit
// - BuilderID
// - TestFailure
// - GitilesCommit
// - TestNthSectionAnalysisResult
// - CulpritAction
// - Variant
// - RegressionRange
// because they are defined in different files.
fdtc, _ := descriptor.MessageDescriptorProto(&pb.TestCulprit{})
fdbid, _ := descriptor.MessageDescriptorProto(&bbpb.BuilderID{})
fdtf, _ := descriptor.MessageDescriptorProto(&pb.TestFailure{})
fdgc, _ := descriptor.MessageDescriptorProto(&bbpb.GitilesCommit{})
fdtns, _ := descriptor.MessageDescriptorProto(&pb.TestNthSectionAnalysisResult{})
fdca, _ := descriptor.MessageDescriptorProto(&pb.CulpritAction{})
fdvr, _ := descriptor.MessageDescriptorProto(&pb.Variant{})
fdrr, _ := descriptor.MessageDescriptorProto(&pb.RegressionRange{})
fdset := &desc.FileDescriptorSet{File: []*desc.FileDescriptorProto{fd, fdtc, fdbid, fdtf, fdgc, fdtns, fdca, fdvr, fdrr}}
return bqutil.GenerateSchema(fdset, rowMessage)
}
func generateRowSchemaDescriptor() (*desc.DescriptorProto, error) {
m := &bqpb.TestAnalysisRow{}
descriptorProto, err := adapt.NormalizeDescriptor(m.ProtoReflect().Descriptor())
if err != nil {
return nil, err
}
return descriptorProto, nil
}