diff --git a/pkg/server/telemetry/features.go b/pkg/server/telemetry/features.go index baead026eeea..4ba9c329545c 100644 --- a/pkg/server/telemetry/features.go +++ b/pkg/server/telemetry/features.go @@ -12,6 +12,7 @@ package telemetry import ( "fmt" + "math" "sync/atomic" "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode" @@ -27,16 +28,22 @@ import ( // raw numbers. // The numbers 0-10 are reported unchanged. func Bucket10(num int64) int64 { - if num <= 0 { - return 0 + if num == math.MinInt64 { + // This is needed to prevent overflow in the negation below. + return -1000000000000000000 + } + sign := int64(1) + if num < 0 { + sign = -1 + num = -num } if num < 10 { - return num + return num * sign } res := int64(10) - for ; res < 1000000000000000000 && res*10 < num; res *= 10 { + for ; res < 1000000000000000000 && res*10 <= num; res *= 10 { } - return res + return res * sign } // CountBucketed counts the feature identified by prefix and the value, using diff --git a/pkg/server/telemetry/features_test.go b/pkg/server/telemetry/features_test.go index 20beed346d9b..fe0f12787880 100644 --- a/pkg/server/telemetry/features_test.go +++ b/pkg/server/telemetry/features_test.go @@ -11,6 +11,7 @@ package telemetry_test import ( + "math" "sync" "testing" @@ -40,3 +41,53 @@ func TestGetCounterDoesNotRace(t *testing.T) { } require.Len(t, counterSet, 1) } + +// TestBucket checks integer quantization. +func TestBucket(t *testing.T) { + testData := []struct { + input int64 + expected int64 + }{ + {0, 0}, + {1, 1}, + {2, 2}, + {3, 3}, + {4, 4}, + {5, 5}, + {6, 6}, + {7, 7}, + {8, 8}, + {9, 9}, + {10, 10}, + {11, 10}, + {20, 10}, + {99, 10}, + {100, 100}, + {101, 100}, + {200, 100}, + {999, 100}, + {1000, 1000}, + {math.MaxInt64, 1000000000000000000}, + {-1, -1}, + {-2, -2}, + {-3, -3}, + {-4, -4}, + {-5, -5}, + {-6, -6}, + {-7, -7}, + {-8, -8}, + {-9, -9}, + {-10, -10}, + {-11, -10}, + {-20, -10}, + {-100, -100}, + {-200, -100}, + {math.MinInt64, -1000000000000000000}, + } + + for _, tc := range testData { + if actual, expected := telemetry.Bucket10(tc.input), tc.expected; actual != expected { + t.Errorf("%d: expected %d, got %d", tc.input, expected, actual) + } + } +}