diff --git a/go/vt/vtgate/executor_scatter_stats.go b/go/vt/vtgate/executor_scatter_stats.go index 24858564a54..86a74d1d1b8 100644 --- a/go/vt/vtgate/executor_scatter_stats.go +++ b/go/vt/vtgate/executor_scatter_stats.go @@ -48,6 +48,25 @@ type statsResultItem struct { Count uint64 } +func castToFloat64(input interface{}) float64 { + switch input.(type) { + case int64: + return float64(input.(int64)) + case uint64: + return float64(input.(uint64)) + case time.Duration: + return float64(input.(time.Duration)) + } + return 0 +} + +func divideOrReturnZero(dividend, divisor interface{}) float64 { + if castToFloat64(divisor) == 0 { + return 0 + } + return castToFloat64(dividend) / castToFloat64(divisor) +} + func (e *Executor) gatherScatterStats() (statsResults, error) { scatterExecTime := time.Duration(0) readOnlyTime := time.Duration(0) @@ -91,11 +110,11 @@ func (e *Executor) gatherScatterStats() (statsResults, error) { route := routes[i] resultItems[i] = &statsResultItem{ Query: plan.Original, - AvgTimePerQuery: time.Duration(plan.ExecTime.Nanoseconds() / int64(plan.ExecCount)), - PercentTimeOfReads: float64(100 * plan.ExecTime / readOnlyTime), - PercentTimeOfScatters: float64(100 * plan.ExecTime / scatterExecTime), - PercentCountOfReads: float64(100 * plan.ExecCount / readOnlyCount), - PercentCountOfScatters: float64(100 * plan.ExecCount / scatterCount), + AvgTimePerQuery: time.Duration(divideOrReturnZero(plan.ExecTime.Nanoseconds(), plan.ExecCount)), + PercentTimeOfReads: 100 * divideOrReturnZero(plan.ExecTime, readOnlyTime), + PercentTimeOfScatters: 100 * divideOrReturnZero(plan.ExecTime, scatterExecTime), + PercentCountOfReads: 100 * divideOrReturnZero(plan.ExecCount, readOnlyCount), + PercentCountOfScatters: 100 * divideOrReturnZero(plan.ExecCount, scatterCount), From: route.Keyspace.Name + "." + route.TableName, Count: plan.ExecCount, } diff --git a/go/vt/vtgate/executor_scatter_stats_test.go b/go/vt/vtgate/executor_scatter_stats_test.go index 9183df9a130..0ed192f7986 100644 --- a/go/vt/vtgate/executor_scatter_stats_test.go +++ b/go/vt/vtgate/executor_scatter_stats_test.go @@ -19,6 +19,9 @@ package vtgate import ( "context" "testing" + "time" + + "github.com/stretchr/testify/assert" "net/http/httptest" @@ -76,3 +79,37 @@ func TestScatterStatsHttpWriting(t *testing.T) { require.Contains(t, recorder.Body.String(), query4) require.NoError(t, err) } + +func TestDivideOrReturnZero(t *testing.T) { + // Expected types + timeDurationZero := time.Duration(0) + unitZero := uint64(0) + intZero := int64(0) + timeDurationOne := time.Duration(1) + uintOne := uint64(1) + intOne := int64(1) + + resultZero := float64(0) + resultOne := float64(1) + + assert.Equal(t, divideOrReturnZero(timeDurationOne, timeDurationZero), resultZero) + assert.Equal(t, divideOrReturnZero(timeDurationOne, unitZero), resultZero) + assert.Equal(t, divideOrReturnZero(timeDurationOne, intZero), resultZero) + assert.Equal(t, divideOrReturnZero(timeDurationOne, timeDurationOne), resultOne) + assert.Equal(t, divideOrReturnZero(timeDurationOne, uintOne), resultOne) + assert.Equal(t, divideOrReturnZero(timeDurationOne, intOne), resultOne) + assert.Equal(t, divideOrReturnZero(uintOne, timeDurationOne), resultOne) + assert.Equal(t, divideOrReturnZero(uintOne, uintOne), resultOne) + assert.Equal(t, divideOrReturnZero(uintOne, intOne), resultOne) + assert.Equal(t, divideOrReturnZero(intOne, timeDurationOne), resultOne) + assert.Equal(t, divideOrReturnZero(intOne, uintOne), resultOne) + assert.Equal(t, divideOrReturnZero(intOne, intOne), resultOne) + + // Unexpected type + int16 := int16(1) + intArray := [2]int64{1, 2} + + assert.Equal(t, divideOrReturnZero(uintOne, int16), resultZero) + assert.Equal(t, divideOrReturnZero(uintOne, intArray), resultZero) + +}