blob: b89b54f040eee732982bd482bc52cad51c27329d [file] [log] [blame]
package sqlignorestore
import (
"context"
"crypto/md5"
"crypto/rand"
"strconv"
"testing"
"time"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.skia.org/infra/go/paramtools"
"go.skia.org/infra/go/testutils/unittest"
"go.skia.org/infra/golden/go/sql/databuilder"
dks "go.skia.org/infra/golden/go/sql/datakitchensink"
"go.skia.org/infra/golden/go/sql/schema"
"go.skia.org/infra/golden/go/sql/sqltest"
"go.skia.org/infra/golden/go/types"
)
func TestConvertIgnoreRules_Success(t *testing.T) {
unittest.SmallTest(t)
condition, args := ConvertIgnoreRules(nil)
assert.Equal(t, "false", condition)
assert.Empty(t, args)
condition, args = ConvertIgnoreRules([]paramtools.ParamSet{
{
"key1": []string{"alpha"},
},
})
assert.Equal(t, `((COALESCE(keys ->> $1::STRING IN ($2), FALSE)))`, condition)
assert.Equal(t, []interface{}{"key1", "alpha"}, args)
condition, args = ConvertIgnoreRules([]paramtools.ParamSet{
{
"key1": []string{"alpha", "beta"},
"key2": []string{"gamma"},
},
{
"key3": []string{"delta", "epsilon", "zeta"},
},
})
const expectedCondition = `((COALESCE(keys ->> $1::STRING IN ($2, $3), FALSE) AND COALESCE(keys ->> $4::STRING IN ($5), FALSE))
OR (COALESCE(keys ->> $6::STRING IN ($7, $8, $9), FALSE)))`
assert.Equal(t, expectedCondition, condition)
assert.Equal(t, []interface{}{"key1", "alpha", "beta", "key2", "gamma", "key3", "delta", "epsilon", "zeta"}, args)
}
func TestUpdateIgnoredTraces_StartsNull_SetToCorrectValue(t *testing.T) {
unittest.LargeTest(t)
existingData := dks.Build()
// Force these all to be null
for i := range existingData.Traces {
existingData.Traces[i].MatchesAnyIgnoreRule = schema.NBNull
}
for i := range existingData.ValuesAtHead {
existingData.ValuesAtHead[i].MatchesAnyIgnoreRule = schema.NBNull
}
ctx := context.Background()
db := sqltest.NewCockroachDBForTestsWithProductionSchema(ctx, t)
require.NoError(t, sqltest.BulkInsertDataTables(ctx, db, existingData))
// Verify things are null
row := db.QueryRow(ctx, `SELECT count(*) FROM Traces WHERE matches_any_ignore_rule IS NULL`)
var count int
require.NoError(t, row.Scan(&count))
assert.Equal(t, 41, count)
assert.Equal(t, 41, len(existingData.Traces))
row = db.QueryRow(ctx, `SELECT count(*) FROM ValuesAtHead WHERE matches_any_ignore_rule IS NULL`)
count = 0
require.NoError(t, row.Scan(&count))
assert.Equal(t, 33, count)
assert.Equal(t, 33, len(existingData.ValuesAtHead))
require.NoError(t, UpdateIgnoredTraces(ctx, db))
// Verify things are not null
row = db.QueryRow(ctx, `SELECT count(*) FROM Traces WHERE matches_any_ignore_rule IS NULL`)
count = -1
require.NoError(t, row.Scan(&count))
assert.Equal(t, 0, count)
row = db.QueryRow(ctx, `SELECT count(*) FROM ValuesAtHead WHERE matches_any_ignore_rule IS NULL`)
count = -1
require.NoError(t, row.Scan(&count))
assert.Equal(t, 0, count)
expectedData := dks.Build()
actualTraces := sqltest.GetAllRows(ctx, t, db, "Traces", &schema.TraceRow{}).([]schema.TraceRow)
assert.ElementsMatch(t, expectedData.Traces, actualTraces)
actualValuesAtHead := sqltest.GetAllRows(ctx, t, db, "ValuesAtHead", &schema.ValueAtHeadRow{}).([]schema.ValueAtHeadRow)
assert.ElementsMatch(t, expectedData.ValuesAtHead, actualValuesAtHead)
}
func TestUpdateIgnoredTraces_StartsNotNull_UpdatedToCorrectValues(t *testing.T) {
unittest.LargeTest(t)
existingData := dks.Build()
// Force these all to be a sentinel value
for i := range existingData.Traces {
existingData.Traces[i].MatchesAnyIgnoreRule = schema.NBFalse
}
for i := range existingData.ValuesAtHead {
existingData.ValuesAtHead[i].MatchesAnyIgnoreRule = schema.NBFalse
}
ctx := context.Background()
db := sqltest.NewCockroachDBForTestsWithProductionSchema(ctx, t)
require.NoError(t, sqltest.BulkInsertDataTables(ctx, db, existingData))
// Verify things are that sentinel value
row := db.QueryRow(ctx, `SELECT count(*) FROM Traces WHERE matches_any_ignore_rule = FALSE`)
var count int
require.NoError(t, row.Scan(&count))
assert.Equal(t, 41, count)
assert.Equal(t, 41, len(existingData.Traces))
row = db.QueryRow(ctx, `SELECT count(*) FROM ValuesAtHead WHERE matches_any_ignore_rule = FALSE`)
count = 0
require.NoError(t, row.Scan(&count))
assert.Equal(t, 33, count)
assert.Equal(t, 33, len(existingData.ValuesAtHead))
require.NoError(t, UpdateIgnoredTraces(ctx, db))
// Verify things were updated to the correct value
expectedData := dks.Build()
actualTraces := sqltest.GetAllRows(ctx, t, db, "Traces", &schema.TraceRow{}).([]schema.TraceRow)
assert.ElementsMatch(t, expectedData.Traces, actualTraces)
actualValuesAtHead := sqltest.GetAllRows(ctx, t, db, "ValuesAtHead", &schema.ValueAtHeadRow{}).([]schema.ValueAtHeadRow)
assert.ElementsMatch(t, expectedData.ValuesAtHead, actualValuesAtHead)
}
func TestUpdateIgnoredTraces_PartiallySet_UpdatedToCorrectValues(t *testing.T) {
unittest.LargeTest(t)
existingData := dks.Build()
// Only ValuesAtHead are incorrectly set, but should be updated anyway
for i := range existingData.ValuesAtHead {
existingData.ValuesAtHead[i].MatchesAnyIgnoreRule = schema.NBFalse
}
ctx := context.Background()
db := sqltest.NewCockroachDBForTestsWithProductionSchema(ctx, t)
require.NoError(t, sqltest.BulkInsertDataTables(ctx, db, existingData))
// Verify things are that sentinel value
row := db.QueryRow(ctx, `SELECT count(*) FROM ValuesAtHead WHERE matches_any_ignore_rule = FALSE`)
var count int
require.NoError(t, row.Scan(&count))
assert.Equal(t, 33, count)
assert.Equal(t, 33, len(existingData.ValuesAtHead))
require.NoError(t, UpdateIgnoredTraces(ctx, db))
// Verify things were updated to the correct value
expectedData := dks.Build()
actualTraces := sqltest.GetAllRows(ctx, t, db, "Traces", &schema.TraceRow{}).([]schema.TraceRow)
assert.ElementsMatch(t, expectedData.Traces, actualTraces)
actualValuesAtHead := sqltest.GetAllRows(ctx, t, db, "ValuesAtHead", &schema.ValueAtHeadRow{}).([]schema.ValueAtHeadRow)
assert.ElementsMatch(t, expectedData.ValuesAtHead, actualValuesAtHead)
}
func TestUpdateIgnoredTraces_MultipleBatches_Success(t *testing.T) {
unittest.LargeTest(t)
const numTraces = 6060 // This is a number bigger than the batch size.
traceIDs := makeRandomTraceIDs(numTraces)
g := md5.Sum([]byte("whatever grouping"))
arbitraryBytes := g[:]
traces := make([]schema.TraceRow, 0, numTraces)
atHead := make([]schema.ValueAtHeadRow, 0, numTraces)
// We'll set 1/3 of the total traces and ValuesAtHead to be ignored (based on the keys
// and the one ignore rule). That way we can make sure the rules were correctly
// applied.
i := 0
for ; i < numTraces/3; i++ {
traces = append(traces, schema.TraceRow{
TraceID: traceIDs[i],
GroupingID: arbitraryBytes,
Keys: paramtools.Params{
types.CorpusField: "corpus",
"should_be_ignored": "true",
},
MatchesAnyIgnoreRule: schema.NBNull,
})
atHead = append(atHead, schema.ValueAtHeadRow{
TraceID: traceIDs[i],
MostRecentCommitID: "does not matter",
Digest: arbitraryBytes,
OptionsID: arbitraryBytes,
GroupingID: arbitraryBytes,
Keys: paramtools.Params{
types.CorpusField: "corpus",
"should_be_ignored": "true",
},
MatchesAnyIgnoreRule: schema.NBNull,
})
}
for ; i < numTraces; i++ {
traces = append(traces, schema.TraceRow{
TraceID: traceIDs[i],
GroupingID: arbitraryBytes,
Keys: paramtools.Params{
types.CorpusField: "corpus",
"should_be_ignored": "false",
},
MatchesAnyIgnoreRule: schema.NBNull,
})
atHead = append(atHead, schema.ValueAtHeadRow{
TraceID: traceIDs[i],
MostRecentCommitID: "does not matter",
Digest: arbitraryBytes,
OptionsID: arbitraryBytes,
GroupingID: arbitraryBytes,
Keys: paramtools.Params{
types.CorpusField: "corpus",
"should_be_ignored": "false",
},
MatchesAnyIgnoreRule: schema.NBNull,
})
}
existingData := schema.Tables{
Traces: traces,
IgnoreRules: []schema.IgnoreRuleRow{{
IgnoreRuleID: uuid.New(),
CreatorEmail: "whomever",
UpdatedEmail: "whomever",
Expires: time.Now(), // arbitrary
Note: "arbitrary",
Query: paramtools.ReadOnlyParamSet{
"should_be_ignored": []string{"true"},
},
}},
ValuesAtHead: atHead,
}
ctx := context.Background()
db := sqltest.NewCockroachDBForTestsWithProductionSchema(ctx, t)
require.NoError(t, sqltest.BulkInsertDataTables(ctx, db, existingData))
// Verify things are null
row := db.QueryRow(ctx, `SELECT count(*) FROM Traces WHERE matches_any_ignore_rule IS NULL`)
var count int
require.NoError(t, row.Scan(&count))
assert.Equal(t, numTraces, count)
row = db.QueryRow(ctx, `SELECT count(*) FROM ValuesAtHead WHERE matches_any_ignore_rule IS NULL`)
count = 0
require.NoError(t, row.Scan(&count))
assert.Equal(t, numTraces, count)
require.NoError(t, UpdateIgnoredTraces(ctx, db))
// Verify things are the right value
row = db.QueryRow(ctx, `SELECT count(*) FROM Traces WHERE matches_any_ignore_rule IS NULL`)
count = -1
require.NoError(t, row.Scan(&count))
assert.Equal(t, 0, count)
row = db.QueryRow(ctx, `SELECT count(*) FROM ValuesAtHead WHERE matches_any_ignore_rule IS NULL`)
count = -1
require.NoError(t, row.Scan(&count))
assert.Equal(t, 0, count)
row = db.QueryRow(ctx, `SELECT count(*) FROM Traces WHERE matches_any_ignore_rule = TRUE`)
count = 0
require.NoError(t, row.Scan(&count))
assert.Equal(t, numTraces/3, count)
row = db.QueryRow(ctx, `SELECT count(*) FROM ValuesAtHead WHERE matches_any_ignore_rule = TRUE`)
count = 0
require.NoError(t, row.Scan(&count))
assert.Equal(t, numTraces/3, count)
}
func TestUpdateIgnoredTraces_NullableRules_SetToCorrectValue(t *testing.T) {
unittest.LargeTest(t)
const whateverTS = "2020-12-01T00:00:00Z"
b := databuilder.TablesBuilder{}
b.CommitsWithData().
Insert("whatever", "whatever", "whatever", whateverTS)
b.SetDigests(map[rune]types.Digest{'A': dks.DigestA01Pos})
b.SetGroupingKeys(types.CorpusField, types.PrimaryKeyField)
// Based on problematic real-world data
// trace_id 2ef4dd282af4c1daa44aafb9eeba6fff 402910fb035102d602cb8331d244af40
b.AddTracesWithCommonKeys(paramtools.Params{
"arch": "x86_64",
"compiler": "Clang",
"config": "gldft",
"cpu_or_gpu": "GPU",
"cpu_or_gpu_value": "GTX660",
"model": "ShuttleA",
"name": "texel_subset_nearest_mipmap_nearest_down",
"os": "Win10",
"source_type": "gm",
"style": "default",
}).
History("A", "A").
Keys([]paramtools.Params{{"configuration": "Debug"}, {"configuration": "Release"}}).
OptionsAll(paramtools.Params{"alpha_type": "Premul", "color_depth": "8888", "color_type": "RGBA_8888", "ext": "png", "gamut": "untagged", "transfer_fn": "untagged"}).
IngestedFrom([]string{"whatever"}, []string{whateverTS})
i := 0
addIgnoreRule := func(ps paramtools.ParamSet) {
b.AddIgnoreRule("whatever", "whatever", whateverTS, "whatever "+strconv.Itoa(i), ps)
i++
}
// Was evaluating to NULL in prod for some traces.
addIgnoreRule(paramtools.ParamSet{
"cpu_or_gpu_value": []string{"GTX660", "GTX960"},
// the above traces lack the extra_config key, and thus this had been returning null
// (before the coalesce was added.
"extra_config": []string{"Vulkan", "Vulkan_ProcDump"},
"name": []string{"texel_subset_nearest_mipmap_linear_down", "texel_subset_nearest_mipmap_nearest_down"},
"os": []string{"Win10"},
})
// emphasize the issue
addIgnoreRule(paramtools.ParamSet{"key does not exist": []string{"whoops", "not", "here"}})
// These rules were fine in production, but problematic when combined with above.
addIgnoreRule(paramtools.ParamSet{
"cpu_or_gpu": []string{"GPU"},
"cpu_or_gpu_value": []string{"Mali400MP2"},
"name": []string{"imagemakewithfilter", "imagemakewithfilter_crop", "imagemakewithfilter_crop_ref", "imagemakewithfilter_ref"}},
)
addIgnoreRule(paramtools.ParamSet{"config": []string{"ep3", "erec2020", "p3", "rec2020"}})
existingData := b.Build()
// Force these all to be null
for i := range existingData.Traces {
existingData.Traces[i].MatchesAnyIgnoreRule = schema.NBNull
}
for i := range existingData.ValuesAtHead {
existingData.ValuesAtHead[i].MatchesAnyIgnoreRule = schema.NBNull
}
ctx := context.Background()
db := sqltest.NewCockroachDBForTestsWithProductionSchema(ctx, t)
require.NoError(t, sqltest.BulkInsertDataTables(ctx, db, existingData))
// Verify things are null
row := db.QueryRow(ctx, `SELECT count(*) FROM Traces WHERE matches_any_ignore_rule IS NULL`)
var count int
require.NoError(t, row.Scan(&count))
assert.Equal(t, 2, count)
assert.Equal(t, 2, len(existingData.Traces))
row = db.QueryRow(ctx, `SELECT count(*) FROM ValuesAtHead WHERE matches_any_ignore_rule IS NULL`)
count = 0
require.NoError(t, row.Scan(&count))
assert.Equal(t, 2, count)
assert.Equal(t, 2, len(existingData.ValuesAtHead))
require.NoError(t, UpdateIgnoredTraces(ctx, db))
// Verify things are not null
row = db.QueryRow(ctx, `SELECT count(*) FROM Traces WHERE matches_any_ignore_rule IS NULL`)
count = -1
require.NoError(t, row.Scan(&count))
assert.Equal(t, 0, count)
row = db.QueryRow(ctx, `SELECT count(*) FROM ValuesAtHead WHERE matches_any_ignore_rule IS NULL`)
count = -1
require.NoError(t, row.Scan(&count))
assert.Equal(t, 0, count)
}
func makeRandomTraceIDs(n int) []schema.TraceID {
rv := make([]schema.TraceID, 0, n)
for i := 0; i < n; i++ {
t := make(schema.TraceID, md5.Size)
_, err := rand.Read(t)
if err != nil {
panic(err)
}
rv = append(rv, t)
}
return rv
}