diff --git a/enterprise/app/tap/grid.tsx b/enterprise/app/tap/grid.tsx index 97b4b15d70d..4a7cc8afdb3 100644 --- a/enterprise/app/tap/grid.tsx +++ b/enterprise/app/tap/grid.tsx @@ -79,6 +79,15 @@ interface Stat { cached: number; } +interface CacheInfo { + fullyCached: boolean; + partiallyCached: boolean; + cachedCount: number; + cachedLocallyCount: number; + cachedRemotelyCount: number; + totalRunCount: number; +} + const Status = api.v1.Status; const MIN_OPACITY = 0.1; @@ -174,6 +183,7 @@ export default class TestGridComponent extends React.Component { for (let targetHistory of histories) { let stats: Stat = { count: 0, pass: 0, totalDuration: 0, maxDuration: 0, avgDuration: 0, flake: 0, cached: 0 }; for (let status of targetHistory.targetStatus) { + const cacheInfo = getCacheInfo(status); stats.count += 1; let duration = this.durationToNum(status.timing?.duration || undefined); stats.totalDuration += duration; @@ -183,7 +193,7 @@ export default class TestGridComponent extends React.Component { } else if (status.status == Status.FLAKY) { stats.flake += 1; } - if (isCached(status)) { + if (cacheInfo.fullyCached) { stats.cached += 1; } } @@ -525,6 +535,7 @@ export default class TestGridComponent extends React.Component { return (
{statuses.map((status) => { + const cacheInfo = getCacheInfo(status); let destinationUrl = `/invocation/${status.invocationId}?${new URLSearchParams({ target: targetHistory.target?.label || "", targetStatus: String(status), @@ -536,9 +547,9 @@ export default class TestGridComponent extends React.Component { title += ` at commit ${commitStatus.commitSha}`; } - let cached = isCached(status); - if (cached) { - title += ` (cached)`; + const cacheLabel = describeCacheStatus(cacheInfo); + if (cacheLabel) { + title += ` (${cacheLabel})`; } return ( @@ -560,7 +571,7 @@ export default class TestGridComponent extends React.Component { className={`tap-block ${ this.getColorMode() == "status" ? `status-${status.status}` : "timing" } - ${cached ? `cached` : ""} + ${cacheInfo.fullyCached ? `cached` : ""} clickable`}> {this.statusToIcon(status.status || Status.STATUS_UNSPECIFIED)} @@ -584,7 +595,65 @@ export default class TestGridComponent extends React.Component { } } -function isCached(status: target.ITargetStatus) { +function getCacheInfo(status: target.ITargetStatus): CacheInfo { + const cachedLocallyCount = Number(status.cachedLocallyCount || 0); + const cachedRemotelyCount = Number(status.cachedRemotelyCount || 0); + const cachedCount = Math.max(Number(status.cachedCount || 0), cachedLocallyCount + cachedRemotelyCount); + const totalRunCount = Math.max(Number(status.totalRunCount || 0), cachedCount); + + if (totalRunCount > 0 || cachedCount > 0 || cachedLocallyCount > 0 || cachedRemotelyCount > 0) { + const fullyCached = Boolean(status.cached) || (totalRunCount > 0 && cachedCount === totalRunCount); + return { + fullyCached, + partiallyCached: !fullyCached && cachedCount > 0, + cachedCount, + cachedLocallyCount, + cachedRemotelyCount, + totalRunCount, + }; + } + + // Older ClickHouse rows only have the legacy cached bool, so preserve the + // historical fallback instead of treating missing counts as uncached. + const fullyCached = Boolean(status.cached) || isHeuristicallyCached(status); + return { + fullyCached, + partiallyCached: false, + cachedCount: fullyCached ? 1 : 0, + cachedLocallyCount: 0, + cachedRemotelyCount: 0, + totalRunCount: fullyCached ? 1 : 0, + }; +} + +function describeCacheStatus(cacheInfo: CacheInfo): string { + if (!cacheInfo.fullyCached && !cacheInfo.partiallyCached) { + return ""; + } + + const originParts = []; + if (cacheInfo.cachedLocallyCount > 0) { + originParts.push(`${cacheInfo.cachedLocallyCount} local`); + } + if (cacheInfo.cachedRemotelyCount > 0) { + originParts.push(`${cacheInfo.cachedRemotelyCount} remote`); + } + const originSuffix = originParts.length ? ` (${originParts.join(", ")})` : ""; + + if (cacheInfo.fullyCached) { + if (cacheInfo.cachedLocallyCount > 0 && cacheInfo.cachedLocallyCount === cacheInfo.totalRunCount) { + return "cached locally"; + } + if (cacheInfo.cachedRemotelyCount > 0 && cacheInfo.cachedRemotelyCount === cacheInfo.totalRunCount) { + return "cached remotely"; + } + return `cached${originSuffix}`; + } + + return `${cacheInfo.cachedCount}/${cacheInfo.totalRunCount} cached${originSuffix}`; +} + +function isHeuristicallyCached(status: target.ITargetStatus) { return ( +(status.timing?.startTime?.seconds || 0) > 0 && Math.floor(+(status.invocationCreatedAtUsec || 0) / 1000000) > +(status.timing?.startTime?.seconds || 0) diff --git a/proto/target.proto b/proto/target.proto index bc09aa87107..f2e45aa1c4f 100644 --- a/proto/target.proto +++ b/proto/target.proto @@ -31,6 +31,9 @@ message TargetMetadata { api.v1.TestSize test_size = 5; } +// Status for a target within target history. Today GetTargetHistory only +// stores test targets, so the cache fields below describe test attempts rather +// than generic build actions. message TargetStatus { // The invocation identifier itself. string invocation_id = 1; @@ -51,6 +54,25 @@ message TargetStatus { // When the invocation was created. int64 invocation_created_at_usec = 5; + + // Whether all recorded test attempts for this target invocation were served + // from cache. Older ClickHouse rows may only populate this legacy field + // without the explicit cache counts below. + bool cached = 6; + + // The number of cached test attempts recorded for this target invocation, + // including both local and remote cache hits. A non-zero cached_count does + // not imply that all attempts were cached; compare against total_run_count. + int32 cached_count = 7; + + // The number of cached test attempts served from the local cache. + int32 cached_locally_count = 8; + + // The number of cached test attempts served from the remote cache. + int32 cached_remotely_count = 9; + + // The total number of recorded test attempts for this target invocation. + int32 total_run_count = 10; } message TargetHistory { diff --git a/server/build_event_protocol/target_tracker/target_tracker.go b/server/build_event_protocol/target_tracker/target_tracker.go index 3c161a7550f..78d189b8ca6 100644 --- a/server/build_event_protocol/target_tracker/target_tracker.go +++ b/server/build_event_protocol/target_tracker/target_tracker.go @@ -49,18 +49,23 @@ const ( ) type target struct { - label string - aspect string - ruleType string - firstStartTime time.Time - totalDuration time.Duration - state targetState - id int64 - overallStatus build_event_stream.TestStatus - cached bool - targetType cmpb.TargetType - testSize build_event_stream.TestSize - buildSuccess bool + label string + aspect string + ruleType string + firstStartTime time.Time + totalDuration time.Duration + state targetState + id int64 + overallStatus build_event_stream.TestStatus + cached bool + cachedCount int32 + cachedLocallyCount int32 + cachedRemotelyCount int32 + totalRunCount int32 + observedResultCount int32 + targetType cmpb.TargetType + testSize build_event_stream.TestSize + buildSuccess bool } func md5Int64(text string) int64 { @@ -76,6 +81,27 @@ func newTarget(label string, aspect string) *target { } } +func (t *target) effectiveCachedCount() int32 { + count := t.cachedCount + if observed := t.cachedLocallyCount + t.cachedRemotelyCount; observed > count { + count = observed + } + return count +} + +func (t *target) effectiveTotalRunCount() int32 { + count := t.totalRunCount + if t.observedResultCount > count { + count = t.observedResultCount + } + return count +} + +func (t *target) fullyCached() bool { + totalRunCount := t.effectiveTotalRunCount() + return totalRunCount > 0 && t.effectiveCachedCount() == totalRunCount +} + func getTestStatus(aborted *build_event_stream.Aborted) build_event_stream.TestStatus { switch aborted.GetReason() { case build_event_stream.Aborted_USER_INTERRUPTED: @@ -99,11 +125,22 @@ func (t *target) updateFromEvent(event *build_event_stream.BuildEvent) { } t.state = targetStateCompleted case *build_event_stream.BuildEvent_TestResult: - t.cached = p.TestResult.GetCachedLocally() || p.TestResult.GetExecutionInfo().GetCachedRemotely() + cachedLocally := p.TestResult.GetCachedLocally() + cachedRemotely := p.TestResult.GetExecutionInfo().GetCachedRemotely() + t.cached = cachedLocally || cachedRemotely + t.observedResultCount++ + if cachedLocally { + t.cachedLocallyCount++ + } + if cachedRemotely { + t.cachedRemotelyCount++ + } t.state = targetStateResult case *build_event_stream.BuildEvent_TestSummary: ts := p.TestSummary t.overallStatus = ts.GetOverallStatus() + t.cachedCount = ts.GetTotalNumCached() + t.totalRunCount = ts.GetTotalRunCount() t.firstStartTime = timeutil.GetTimeWithFallback(ts.GetFirstStartTime(), ts.GetFirstStartTimeMillis()) t.totalDuration = timeutil.GetDurationWithFallback(ts.GetTotalRunDuration(), ts.GetTotalRunDurationMillis()) t.state = targetStateSummary @@ -363,18 +400,22 @@ func (t *TargetTracker) writeTestTargetStatusesToOLAPDB(ctx context.Context, per Label: target.label, InvocationStartTimeUsec: invocationStartTime.UnixMicro(), - RuleType: target.ruleType, - UserID: permissions.UserID, - InvocationUUID: invocationUUID, - TargetType: int32(target.targetType), - TestSize: int32(target.testSize), - Status: int32(target.overallStatus), - Cached: target.cached, - StartTimeUsec: testStartTimeUsec, - DurationUsec: target.totalDuration.Microseconds(), - BranchName: t.buildEventAccumulator.Invocation().GetBranchName(), - Role: t.buildEventAccumulator.Invocation().GetRole(), - Command: t.buildEventAccumulator.Invocation().GetCommand(), + RuleType: target.ruleType, + UserID: permissions.UserID, + InvocationUUID: invocationUUID, + TargetType: int32(target.targetType), + TestSize: int32(target.testSize), + Status: int32(target.overallStatus), + Cached: target.fullyCached(), + CachedCount: target.effectiveCachedCount(), + CachedLocallyCount: target.cachedLocallyCount, + CachedRemotelyCount: target.cachedRemotelyCount, + TotalRunCount: target.effectiveTotalRunCount(), + StartTimeUsec: testStartTimeUsec, + DurationUsec: target.totalDuration.Microseconds(), + BranchName: t.buildEventAccumulator.Invocation().GetBranchName(), + Role: t.buildEventAccumulator.Invocation().GetRole(), + Command: t.buildEventAccumulator.Invocation().GetCommand(), }) } err := t.env.GetOLAPDBHandle().FlushTestTargetStatuses(ctx, entries) diff --git a/server/build_event_protocol/target_tracker/target_tracker_test.go b/server/build_event_protocol/target_tracker/target_tracker_test.go index 9bdb9351421..d2445d21728 100644 --- a/server/build_event_protocol/target_tracker/target_tracker_test.go +++ b/server/build_event_protocol/target_tracker/target_tracker_test.go @@ -2,6 +2,7 @@ package target_tracker_test import ( "context" + "slices" "testing" "time" @@ -21,17 +22,21 @@ import ( ) type Row struct { - GroupID string - CommitSHA string - TestSize int32 - Status int32 - Cached bool - TargetType int32 - RuleType string - Label string - RepoURL string - Role string - Command string + GroupID string + CommitSHA string + TestSize int32 + Status int32 + Cached bool + CachedCount int32 + CachedLocallyCount int32 + CachedRemotelyCount int32 + TotalRunCount int32 + TargetType int32 + RuleType string + Label string + RepoURL string + Role string + Command string } func targetConfiguredId(label string) *build_event_stream.BuildEventId { @@ -178,6 +183,7 @@ func runTrackTargetsForEventsTest(t *testing.T) { targetConfiguredId("//server:foo_test"), targetConfiguredId("//server:foo_local_cache_test"), targetConfiguredId("//server:foo_remote_cache_test"), + targetConfiguredId("//server:foo_partially_cached_test"), targetConfiguredId("//server:bar_test"), }, Payload: &build_event_stream.BuildEvent_Expanded{}, @@ -230,6 +236,18 @@ func runTrackTargetsForEventsTest(t *testing.T) { }, }, }, + &build_event_stream.BuildEvent{ + Id: targetConfiguredId("//server:foo_partially_cached_test"), + Children: []*build_event_stream.BuildEventId{ + targetCompletedId("//server:foo_partially_cached_test"), + }, + Payload: &build_event_stream.BuildEvent_Configured{ + Configured: &build_event_stream.TargetConfigured{ + TargetKind: "go_test rule", + TestSize: build_event_stream.TestSize_MEDIUM, + }, + }, + }, &build_event_stream.BuildEvent{ Id: targetConfiguredId("//server:baz_test"), Children: []*build_event_stream.BuildEventId{ @@ -303,6 +321,18 @@ func runTrackTargetsForEventsTest(t *testing.T) { }, }, }, + &build_event_stream.BuildEvent{ + Id: targetCompletedId("//server:foo_partially_cached_test"), + Children: []*build_event_stream.BuildEventId{ + testResultId("//server:foo_partially_cached_test"), + testSummaryId("//server:foo_partially_cached_test"), + }, + Payload: &build_event_stream.BuildEvent_Completed{ + Completed: &build_event_stream.TargetComplete{ + Success: true, + }, + }, + }, &build_event_stream.BuildEvent{ Id: targetCompletedId("//server:baz_test"), Children: []*build_event_stream.BuildEventId{ @@ -354,6 +384,23 @@ func runTrackTargetsForEventsTest(t *testing.T) { }, }, }, + &build_event_stream.BuildEvent{ + Id: testResultId("//server:foo_partially_cached_test"), + Payload: &build_event_stream.BuildEvent_TestResult{ + TestResult: &build_event_stream.TestResult{ + Status: build_event_stream.TestStatus_PASSED, + CachedLocally: true, + }, + }, + }, + &build_event_stream.BuildEvent{ + Id: testResultId("//server:foo_partially_cached_test"), + Payload: &build_event_stream.BuildEvent_TestResult{ + TestResult: &build_event_stream.TestResult{ + Status: build_event_stream.TestStatus_PASSED, + }, + }, + }, &build_event_stream.BuildEvent{ Id: testResultId("//server:baz_test"), Payload: &build_event_stream.BuildEvent_TestResult{ @@ -375,7 +422,9 @@ func runTrackTargetsForEventsTest(t *testing.T) { Id: testSummaryId("//server:foo_local_cache_test"), Payload: &build_event_stream.BuildEvent_TestSummary{ TestSummary: &build_event_stream.TestSummary{ - OverallStatus: build_event_stream.TestStatus_PASSED, + OverallStatus: build_event_stream.TestStatus_PASSED, + TotalNumCached: 1, + TotalRunCount: 1, }, }, }, @@ -383,7 +432,19 @@ func runTrackTargetsForEventsTest(t *testing.T) { Id: testSummaryId("//server:foo_remote_cache_test"), Payload: &build_event_stream.BuildEvent_TestSummary{ TestSummary: &build_event_stream.TestSummary{ - OverallStatus: build_event_stream.TestStatus_PASSED, + OverallStatus: build_event_stream.TestStatus_PASSED, + TotalNumCached: 1, + TotalRunCount: 1, + }, + }, + }, + &build_event_stream.BuildEvent{ + Id: testSummaryId("//server:foo_partially_cached_test"), + Payload: &build_event_stream.BuildEvent_TestSummary{ + TestSummary: &build_event_stream.TestSummary{ + OverallStatus: build_event_stream.TestStatus_PASSED, + TotalNumCached: 1, + TotalRunCount: 2, }, }, }, @@ -407,54 +468,77 @@ func runTrackTargetsForEventsTest(t *testing.T) { expected := []Row{ { - Role: "CI", - GroupID: "GROUP1", - CommitSHA: "abcdef", - Command: "test", - RuleType: "go_test rule", - Label: "//server:baz_test", - RepoURL: "bb/foo", - TestSize: int32(cmpb.TestSize_SMALL), - Status: int32(build_event_stream.TestStatus_FAILED), - TargetType: int32(cmpb.TargetType_TEST), + Role: "CI", + GroupID: "GROUP1", + CommitSHA: "abcdef", + Command: "test", + RuleType: "go_test rule", + Label: "//server:baz_test", + RepoURL: "bb/foo", + TestSize: int32(cmpb.TestSize_SMALL), + Status: int32(build_event_stream.TestStatus_FAILED), + TotalRunCount: 1, + TargetType: int32(cmpb.TargetType_TEST), }, { - Role: "CI", - GroupID: "GROUP1", - CommitSHA: "abcdef", - Command: "test", - RuleType: "go_test rule", - Label: "//server:foo_test", - RepoURL: "bb/foo", - TestSize: int32(cmpb.TestSize_MEDIUM), - Status: int32(build_event_stream.TestStatus_PASSED), - TargetType: int32(cmpb.TargetType_TEST), + Role: "CI", + GroupID: "GROUP1", + CommitSHA: "abcdef", + Command: "test", + RuleType: "go_test rule", + Label: "//server:foo_test", + RepoURL: "bb/foo", + TestSize: int32(cmpb.TestSize_MEDIUM), + Status: int32(build_event_stream.TestStatus_PASSED), + TotalRunCount: 1, + TargetType: int32(cmpb.TargetType_TEST), }, { - Role: "CI", - GroupID: "GROUP1", - CommitSHA: "abcdef", - Command: "test", - RuleType: "go_test rule", - Label: "//server:foo_local_cache_test", - RepoURL: "bb/foo", - TestSize: int32(cmpb.TestSize_MEDIUM), - Status: int32(build_event_stream.TestStatus_PASSED), - Cached: true, - TargetType: int32(cmpb.TargetType_TEST), + Role: "CI", + GroupID: "GROUP1", + CommitSHA: "abcdef", + Command: "test", + RuleType: "go_test rule", + Label: "//server:foo_local_cache_test", + RepoURL: "bb/foo", + TestSize: int32(cmpb.TestSize_MEDIUM), + Status: int32(build_event_stream.TestStatus_PASSED), + Cached: true, + CachedCount: 1, + CachedLocallyCount: 1, + TotalRunCount: 1, + TargetType: int32(cmpb.TargetType_TEST), }, { - Role: "CI", - GroupID: "GROUP1", - CommitSHA: "abcdef", - Command: "test", - RuleType: "go_test rule", - Label: "//server:foo_remote_cache_test", - RepoURL: "bb/foo", - TestSize: int32(cmpb.TestSize_MEDIUM), - Status: int32(build_event_stream.TestStatus_PASSED), - Cached: true, - TargetType: int32(cmpb.TargetType_TEST), + Role: "CI", + GroupID: "GROUP1", + CommitSHA: "abcdef", + Command: "test", + RuleType: "go_test rule", + Label: "//server:foo_remote_cache_test", + RepoURL: "bb/foo", + TestSize: int32(cmpb.TestSize_MEDIUM), + Status: int32(build_event_stream.TestStatus_PASSED), + Cached: true, + CachedCount: 1, + CachedRemotelyCount: 1, + TotalRunCount: 1, + TargetType: int32(cmpb.TargetType_TEST), + }, + { + Role: "CI", + GroupID: "GROUP1", + CommitSHA: "abcdef", + Command: "test", + RuleType: "go_test rule", + Label: "//server:foo_partially_cached_test", + RepoURL: "bb/foo", + TestSize: int32(cmpb.TestSize_MEDIUM), + Status: int32(build_event_stream.TestStatus_PASSED), + CachedCount: 1, + CachedLocallyCount: 1, + TotalRunCount: 2, + TargetType: int32(cmpb.TargetType_TEST), }, { Role: "CI", @@ -472,7 +556,16 @@ func runTrackTargetsForEventsTest(t *testing.T) { if tracker.WriteToOLAPDBEnabled() { assertTestTargetStatusesMatchOLAPDB(t, te, expected) } else { - assertTestTargetStatusesMatchPrimaryDB(t, ctx, te, testUUID, expected) + // The primary DB path only persists the legacy cached bool, not the OLAP-only + // per-attempt cache counts. + legacyExpected := slices.Clone(expected) + for i := range legacyExpected { + legacyExpected[i].CachedCount = 0 + legacyExpected[i].CachedLocallyCount = 0 + legacyExpected[i].CachedRemotelyCount = 0 + legacyExpected[i].TotalRunCount = 0 + } + assertTestTargetStatusesMatchPrimaryDB(t, ctx, te, testUUID, legacyExpected) } } @@ -775,7 +868,7 @@ func TestTrackTargetsForEventsAborted(t *testing.T) { func assertTestTargetStatusesMatchOLAPDB(t *testing.T, te *testenv.TestEnv, expected []Row) { var got []Row - query := `SELECT group_id, commit_sha, rule_type, label, repo_url, branch_name, role, command, test_size, status, cached, target_type FROM "TestTargetStatuses"` + query := `SELECT group_id, commit_sha, rule_type, label, repo_url, branch_name, role, command, test_size, status, cached, cached_count, cached_locally_count, cached_remotely_count, total_run_count, target_type FROM "TestTargetStatuses"` err := te.GetOLAPDBHandle().NewQuery(context.Background(), "get_target_status").Raw(query).Take(&got) require.NoError(t, err) assert.ElementsMatch(t, got, expected) diff --git a/server/target/target.go b/server/target/target.go index c4cb9d7ab7f..36593b1568c 100644 --- a/server/target/target.go +++ b/server/target/target.go @@ -435,6 +435,11 @@ func fetchTargetsFromOLAPDB(ctx context.Context, env environment.Env, q *query_b TargetType int32 TestSize int32 Status int32 + Cached bool + CachedCount int32 + CachedLocallyCount int32 + CachedRemotelyCount int32 + TotalRunCount int32 StartTimeUsec int64 DurationUsec int64 InvocationUUID string @@ -462,9 +467,14 @@ func fetchTargetsFromOLAPDB(ctx context.Context, env environment.Env, q *query_b } statuses[row.Label] = append(statuses[row.Label], &trpb.TargetStatus{ - InvocationId: invocationID, - CommitSha: row.CommitSHA, - Status: convertToCommonStatus(build_event_stream.TestStatus(row.Status)), + InvocationId: invocationID, + CommitSha: row.CommitSHA, + Status: convertToCommonStatus(build_event_stream.TestStatus(row.Status)), + Cached: row.Cached, + CachedCount: row.CachedCount, + CachedLocallyCount: row.CachedLocallyCount, + CachedRemotelyCount: row.CachedRemotelyCount, + TotalRunCount: row.TotalRunCount, Timing: &cmpb.Timing{ StartTime: timestamppb.New(time.UnixMicro(row.StartTimeUsec)), Duration: durationpb.New(time.Microsecond * time.Duration(row.DurationUsec)), @@ -674,7 +684,8 @@ func readPaginatedTargetsFromOLAPDB(ctx context.Context, env environment.Env, re outerCommitQuery.SetLimit(targetHistoryPageSize) q := query_builder.NewQuery(` - SELECT label, rule_type, target_type, test_size, status, + SELECT label, rule_type, target_type, test_size, status, cached, + cached_count, cached_locally_count, cached_remotely_count, total_run_count, start_time_usec, duration_usec, invocation_uuid, commit_sha, branch_name, repo_url, invocation_start_time_usec FROM "TestTargetStatuses"`) diff --git a/server/target/target_test.go b/server/target/target_test.go index 93a1e433eb1..41d4a1213b8 100644 --- a/server/target/target_test.go +++ b/server/target/target_test.go @@ -59,6 +59,9 @@ func TestGetTargetHistory(t *testing.T) { Label: "//:flaky_test", Status: int32(bespb.TestStatus_FLAKY), InvocationUUID: iid1Hex, + CachedCount: 1, + CachedLocallyCount: 1, + TotalRunCount: 2, InvocationStartTimeUsec: 1e6, StartTimeUsec: 1e6 + 1, }, @@ -71,6 +74,10 @@ func TestGetTargetHistory(t *testing.T) { Label: "//:passing_test", Status: int32(bespb.TestStatus_PASSED), InvocationUUID: iid2Hex, + Cached: true, + CachedCount: 1, + CachedRemotelyCount: 1, + TotalRunCount: 1, InvocationStartTimeUsec: 2e6, StartTimeUsec: 2e6, }, @@ -129,6 +136,11 @@ func TestGetTargetHistory(t *testing.T) { { InvocationId: iid2, InvocationCreatedAtUsec: 2e6, + Cached: false, + CachedCount: 0, + CachedLocallyCount: 0, + CachedRemotelyCount: 0, + TotalRunCount: 0, Timing: timingUsec(2e6+1, 0), CommitSha: "commit2", Status: common.Status_PASSED, @@ -136,6 +148,11 @@ func TestGetTargetHistory(t *testing.T) { { InvocationId: iid1, InvocationCreatedAtUsec: 1e6, + Cached: false, + CachedCount: 1, + CachedLocallyCount: 1, + CachedRemotelyCount: 0, + TotalRunCount: 2, Timing: timingUsec(1e6+1, 0), CommitSha: "commit1", Status: common.Status_FLAKY, @@ -151,6 +168,11 @@ func TestGetTargetHistory(t *testing.T) { { InvocationId: iid2, InvocationCreatedAtUsec: 2e6, + Cached: true, + CachedCount: 1, + CachedLocallyCount: 0, + CachedRemotelyCount: 1, + TotalRunCount: 1, Timing: timingUsec(2e6, 0), CommitSha: "commit2", Status: common.Status_PASSED, @@ -158,6 +180,11 @@ func TestGetTargetHistory(t *testing.T) { { InvocationId: iid1, InvocationCreatedAtUsec: 1e6, + Cached: false, + CachedCount: 0, + CachedLocallyCount: 0, + CachedRemotelyCount: 0, + TotalRunCount: 0, Timing: timingUsec(1e6, 0), CommitSha: "commit1", Status: common.Status_PASSED, @@ -190,6 +217,11 @@ func TestGetTargetHistory(t *testing.T) { { InvocationId: iid2, InvocationCreatedAtUsec: 2e6, + Cached: false, + CachedCount: 0, + CachedLocallyCount: 0, + CachedRemotelyCount: 0, + TotalRunCount: 0, Timing: timingUsec(2e6+1, 0), CommitSha: "commit2", Status: common.Status_PASSED, @@ -205,6 +237,11 @@ func TestGetTargetHistory(t *testing.T) { { InvocationId: iid2, InvocationCreatedAtUsec: 2e6, + Cached: true, + CachedCount: 1, + CachedLocallyCount: 0, + CachedRemotelyCount: 1, + TotalRunCount: 1, Timing: timingUsec(2e6, 0), CommitSha: "commit2", Status: common.Status_PASSED, diff --git a/server/util/clickhouse/schema/schema.go b/server/util/clickhouse/schema/schema.go index 96e978e1244..eb5a909e495 100644 --- a/server/util/clickhouse/schema/schema.go +++ b/server/util/clickhouse/schema/schema.go @@ -354,14 +354,18 @@ type TestTargetStatus struct { Label string InvocationUUID string - RuleType string - UserID string - TargetType int32 - TestSize int32 - Status int32 - Cached bool - StartTimeUsec int64 - DurationUsec int64 + RuleType string + UserID string + TargetType int32 + TestSize int32 + Status int32 + Cached bool + CachedCount int32 + CachedLocallyCount int32 + CachedRemotelyCount int32 + TotalRunCount int32 + StartTimeUsec int64 + DurationUsec int64 // The following fields are from Invocation. BranchName string