Files
o11y/pkg/querier/bucket_cache_bench_test.go
zeekay ed4424bf84 merge: upstream/main (sync, brand preserved)
# Conflicts:
#	.github/workflows/build-community.yaml
#	.github/workflows/build-enterprise.yaml
#	.github/workflows/gor-signoz-community.yaml
#	cmd/community/Dockerfile
#	cmd/community/Dockerfile.multi-arch
#	cmd/community/main.go
#	cmd/community/server.go
#	cmd/enterprise/Dockerfile
#	cmd/enterprise/Dockerfile.integration
#	cmd/enterprise/Dockerfile.with-web.integration
#	cmd/server/metastore.go
#	cmd/zap.go
#	conf/prometheus.yml
#	deploy/docker-swarm/generator/hotrod/docker-compose.yaml
#	deploy/docker-swarm/generator/infra/docker-compose.yaml
#	deploy/docker-swarm/generator/infra/otel-agent-config.yaml
#	deploy/docker-swarm/generator/infra/otel-metrics-config.yaml
#	deploy/docker/generator/hotrod/docker-compose.yaml
#	deploy/docker/generator/infra/docker-compose.yaml
#	deploy/docker/generator/infra/otel-collector-config.yaml
#	docs/contributing/go/integration.md
#	ee/anomaly/hourly.go
#	ee/anomaly/params.go
#	ee/anomaly/seasonal.go
#	ee/authn/callbackauthn/oidccallbackauthn/authn.go
#	ee/authz/openfgaauthz/provider.go
#	ee/authz/openfgaschema/base.fga
#	ee/authz/openfgaserver/server.go
#	ee/licensing/config.go
#	ee/licensing/httplicensing/provider.go
#	ee/modules/dashboard/impldashboard/module.go
#	ee/querier/handler.go
#	ee/query-service/anomaly/seasonal.go
#	ee/query-service/app/api/cloudIntegrations.go
#	ee/query-service/app/api/featureFlags.go
#	ee/query-service/app/api/license.go
#	ee/query-service/app/api/queryrange.go
#	ee/query-service/rules/anomaly.go
#	ee/query-service/rules/anomaly_test.go
#	ee/query-service/rules/manager.go
#	ee/query-service/rules/manager_test.go
#	ee/query-service/usage/manager.go
#	ee/sqlschema/postgressqlschema/provider.go
#	ee/sqlstore/postgressqlstore/dialect.go
#	ee/sqlstore/postgressqlstore/provider.go
#	ee/zeus/config.go
#	ee/zeus/httpzeus/provider.go
#	frontend/.eslintrc.cjs
#	frontend/e2e/test-plan/README.md
#	frontend/e2e/test-plan/validation-report.md
#	frontend/e2e/tests/settings/account-settings/account-settings-settings.spec.ts
#	frontend/e2e/tests/settings/api-keys/api-keys-settings.spec.ts
#	frontend/playwright.config.ts
#	frontend/public/Images/signoz-hero-image.webp
#	frontend/public/Logos/signoz-brand-logo.svg
#	frontend/scripts/generate-permissions-type.cjs
#	frontend/src/assets/Images/o11y-hero-image.webp
#	frontend/src/assets/Images/signoz-hero-image.webp
#	frontend/src/assets/Logos/hanzo-brand-logo.svg
#	frontend/src/assets/Logos/hanzo-icon.svg
#	frontend/src/assets/Logos/o11y-brand-logo.svg
#	frontend/src/assets/Logos/signoz-brand-logo.svg
#	frontend/src/assets/SigNoz-dark.svg
#	frontend/src/assets/SigNoz-white.svg
#	frontend/src/assets/signoz.svg
#	frontend/src/components/DetailsDrawer/DetailsDrawer.styles.scss
#	frontend/src/components/HostMetricsDetail/HostMetricTraces/HostMetricTraces.styles.scss
#	frontend/src/components/HostMetricsDetail/HostMetricTraces/constants.ts
#	frontend/src/components/HostMetricsDetail/HostMetricsDetail.styles.scss
#	frontend/src/components/HostMetricsDetail/HostMetricsDetails.tsx
#	frontend/src/components/HostMetricsDetail/HostMetricsLogs/NoLogsContainer.tsx
#	frontend/src/components/HostMetricsDetail/HostMetricsLogs/constants.ts
#	frontend/src/components/LogsDownloadOptionsMenu/LogsDownloadOptionsMenu.styles.scss
#	frontend/src/components/WelcomeLeftContainer/index.tsx
#	frontend/src/container/APIKeys/APIKeys.styles.scss
#	frontend/src/container/APIKeys/APIKeys.test.tsx
#	frontend/src/container/APIKeys/APIKeys.tsx
#	frontend/src/container/CloudIntegrationPage/Header/Header.tsx
#	frontend/src/container/CloudIntegrationPage/HeroSection/HeroSection.tsx
#	frontend/src/container/CloudIntegrationPage/HeroSection/components/AccountActions.style.scss
#	frontend/src/container/CloudIntegrationPage/HeroSection/components/AccountActions.tsx
#	frontend/src/container/CloudIntegrationPage/HeroSection/components/AccountSettingsModal.style.scss
#	frontend/src/container/CloudIntegrationPage/HeroSection/components/AccountSettingsModal.tsx
#	frontend/src/container/CloudIntegrationPage/HeroSection/components/CloudAccountSetupModal.style.scss
#	frontend/src/container/CloudIntegrationPage/HeroSection/components/RemoveIntegrationAccount.tsx
#	frontend/src/container/CloudIntegrationPage/ServicesSection/ConfigureServiceModal.tsx
#	frontend/src/container/CloudIntegrationPage/ServicesSection/ServicesTabs.style.scss
#	frontend/src/container/CloudIntegrationPage/ServicesSection/ServicesTabs.tsx
#	frontend/src/container/DashboardContainer/DashboardSettings/General/AddTags/AddTags.styles.scss
#	frontend/src/container/DashboardContainer/DashboardSettings/General/GeneralSettings.styles.scss
#	frontend/src/container/FormAlertRules/ChQuerySection/ChQuerySection.tsx
#	frontend/src/container/FormAlertRules/UserGuide/index.tsx
#	frontend/src/container/InfraMonitoringHosts/HostsEmptyOrIncorrectMetrics.tsx
#	frontend/src/container/InfraMonitoringHosts/InfraMonitoring.styles.scss
#	frontend/src/container/InfraMonitoringHosts/__tests__/HostsEmptyOrIncorrectMetrics.test.tsx
#	frontend/src/container/InfraMonitoringK8s/Clusters/ClusterDetails/ClusterDetails.tsx
#	frontend/src/container/InfraMonitoringK8s/Clusters/utils.tsx
#	frontend/src/container/InfraMonitoringK8s/DaemonSets/DaemonSetDetails/DaemonSetDetails.tsx
#	frontend/src/container/InfraMonitoringK8s/DaemonSets/utils.tsx
#	frontend/src/container/InfraMonitoringK8s/Deployments/DeploymentDetails/DeploymentDetails.tsx
#	frontend/src/container/InfraMonitoringK8s/Deployments/utils.tsx
#	frontend/src/container/InfraMonitoringK8s/EntityDetailsUtils/EntityEvents/entityEvents.styles.scss
#	frontend/src/container/InfraMonitoringK8s/EntityDetailsUtils/EntityTraces/entityTraces.styles.scss
#	frontend/src/container/InfraMonitoringK8s/InfraMonitoringK8s.styles.scss
#	frontend/src/container/InfraMonitoringK8s/Jobs/JobDetails/JobDetails.tsx
#	frontend/src/container/InfraMonitoringK8s/Jobs/utils.tsx
#	frontend/src/container/InfraMonitoringK8s/K8sEmptyOrIncorrectMetrics.tsx
#	frontend/src/container/InfraMonitoringK8s/K8sFiltersSidePanel/K8sFiltersSidePanel.styles.scss
#	frontend/src/container/InfraMonitoringK8s/Namespaces/NamespaceDetails/NamespaceDetails.styles.scss
#	frontend/src/container/InfraMonitoringK8s/Namespaces/NamespaceDetails/NamespaceDetails.tsx
#	frontend/src/container/InfraMonitoringK8s/Namespaces/utils.tsx
#	frontend/src/container/InfraMonitoringK8s/Nodes/NodeDetails/NodeDetails.tsx
#	frontend/src/container/InfraMonitoringK8s/Nodes/utils.tsx
#	frontend/src/container/InfraMonitoringK8s/Pods/PodDetails/PodDetails.tsx
#	frontend/src/container/InfraMonitoringK8s/StatefulSets/StatefulSetDetails/StatefulSetDetails.tsx
#	frontend/src/container/InfraMonitoringK8s/StatefulSets/utils.tsx
#	frontend/src/container/InfraMonitoringK8s/Volumes/VolumeDetails/VolumeDetails.tsx
#	frontend/src/container/InfraMonitoringK8s/Volumes/utils.tsx
#	frontend/src/container/ListAlertRules/AlertsEmptyState/AlertsEmptyState.styles.scss
#	frontend/src/container/ListAlertRules/ListAlert.tsx
#	frontend/src/container/LogsContextList/styles.ts
#	frontend/src/container/LogsExplorerList/ColumnView/ColumnView.tsx
#	frontend/src/container/LogsExplorerViews/QueryStatus.tsx
#	frontend/src/container/MetricsApplication/Tabs/Overview/ApDex/ApDexTraces.tsx
#	frontend/src/container/MetricsExplorer/Explorer/RelatedMetrics.tsx
#	frontend/src/container/MetricsExplorer/Explorer/useGetRelatedMetricsGraphs.ts
#	frontend/src/container/MetricsExplorer/Summary/MetricTypeSearch.tsx
#	frontend/src/container/NewWidget/LeftContainer/QuerySection/QueryBuilder/ClickHouse/index.tsx
#	frontend/src/container/NewWidget/LeftContainer/QuerySection/QueryBuilder/ClickHouse/query.tsx
#	frontend/src/container/NewWidget/LeftContainer/QuerySection/QueryBuilder/clickHouse/index.tsx
#	frontend/src/container/NewWidget/LeftContainer/QuerySection/QueryBuilder/clickHouse/query.tsx
#	frontend/src/container/NewWidget/LeftContainer/QuerySection/QueryBuilder/datastore/index.tsx
#	frontend/src/container/NewWidget/LeftContainer/QuerySection/QueryBuilder/datastore/query.tsx
#	frontend/src/container/OnboardingQuestionaire/OnboardingFooter/OnboardingFooter.tsx
#	frontend/src/container/OnboardingQuestionaire/OnboardingHeader/OnboardingHeader.styles.scss
#	frontend/src/container/OnboardingQuestionaire/OnboardingHeader/OnboardingHeader.tsx
#	frontend/src/container/OnboardingV2Container/onboarding-configs/onboarding-config-with-links.json
#	frontend/src/container/QueryBuilder/QueryBuilder.tsx
#	frontend/src/container/QueryBuilder/components/Query/Query.tsx
#	frontend/src/container/RolesSettings/__tests__/utils.test.ts
#	frontend/src/container/ServiceTable/SkipOnBoardModal/index.tsx
#	frontend/src/container/TraceDetail/Missingtrace.tsx
#	frontend/src/container/TraceDetail/SelectedSpanDetails/config.ts
#	frontend/src/lib/uPlotV2/components/Tooltip/Tooltip.styles.scss
#	frontend/src/mocks-server/__mockdata__/apiKeys.ts
#	frontend/src/pages/AlertDetails/AlertHeader/AlertStatus/AlertStatus.tsx
#	frontend/src/pages/Integrations/IntegrationDetailPage/IntegrationDetailPage.styles.scss
#	frontend/src/pages/Integrations/IntegrationDetailPage/IntegrationDetailPage.tsx
#	frontend/src/pages/Integrations/IntegrationsList.tsx
#	frontend/src/pages/Integrations/utils.ts
#	frontend/src/types/api/integrations/aws.ts
#	pkg/alertmanager/o11yalertmanager/handler.go
#	pkg/apiserver/o11yapiserver/alertmanager.go
#	pkg/apiserver/o11yapiserver/cloudintegration.go
#	pkg/apiserver/o11yapiserver/inframonitoring.go
#	pkg/apiserver/o11yapiserver/llmpricingrule.go
#	pkg/apiserver/o11yapiserver/rawdataexport.go
#	pkg/apiserver/o11yapiserver/registry.go
#	pkg/apiserver/o11yapiserver/ruler.go
#	pkg/apiserver/o11yapiserver/ruler_examples.go
#	pkg/apiserver/o11yapiserver/ruler_examples_test.go
#	pkg/apiserver/o11yapiserver/rulestatehistory.go
#	pkg/apiserver/o11yapiserver/spanmapper.go
#	pkg/apiserver/o11yapiserver/tracedetail.go
#	pkg/authn/callbackauthn/googlecallbackauthn/authn.go
#	pkg/authz/openfgaauthz/provider.go
#	pkg/authz/openfgaschema/base.fga
#	pkg/authz/openfgaserver/server.go
#	pkg/authz/openfgaserver/server_test.go
#	pkg/authz/openfgaserver/sqlstore.go
#	pkg/http/middleware/api_key.go
#	pkg/http/middleware/authn.go
#	pkg/instrumentation/metric.go
#	pkg/modules/user/impluser/module.go
#	pkg/prometheus/clickhouseprometheus/client.go
#	pkg/prometheus/clickhouseprometheus/client_query_test.go
#	pkg/prometheus/clickhouseprometheus/provider.go
#	pkg/prometheus/config.go
#	pkg/prometheus/engine.go
#	pkg/prometheus/label.go
#	pkg/prometheus/prometheus.go
#	pkg/prometheus/prometheustest/provider.go
#	pkg/prometheus/prometheustest/utils_test.go
#	pkg/querier/promql_query.go
#	pkg/query-service/app/cloudintegrations/Readme.md
#	pkg/query-service/app/cloudintegrations/accountsRepo.go
#	pkg/query-service/app/cloudintegrations/constants.go
#	pkg/query-service/app/cloudintegrations/controller.go
#	pkg/query-service/app/cloudintegrations/models.go
#	pkg/query-service/app/cloudintegrations/serviceconfig_db.go
#	pkg/query-service/app/cloudintegrations/services/definitions/aws/eks/assets/dashboards/containerinsights.json
#	pkg/query-service/app/cloudintegrations/services/definitions/aws/eks/assets/dashboards/containerinsights_dot.json
#	pkg/query-service/app/cloudintegrations/services/definitions/aws/eks/assets/dashboards/overview.json
#	pkg/query-service/app/cloudintegrations/services/definitions/aws/eks/assets/dashboards/overview_dot.json
#	pkg/query-service/app/cloudintegrations/services/models.go
#	pkg/query-service/app/cloudintegrations/services/services.go
#	pkg/query-service/app/cloudintegrations/services/services_test.go
#	pkg/query-service/app/integrations/builtin_integrations/clickhouse/assets/dashboards/overview_dot.json
#	pkg/query-service/app/integrations/builtin_integrations/datastore/assets/dashboards/overview_dot.json
#	pkg/query-service/app/metricsexplorer/parser.go
#	pkg/query-service/app/metricsexplorer/summary.go
#	pkg/query-service/app/querier/querier_test.go
#	pkg/query-service/app/querier/v2/querier_test.go
#	pkg/query-service/app/summary.go
#	pkg/query-service/model/metrics_explorer/summary.go
#	pkg/query-service/rules/base_rule_test.go
#	pkg/query-service/rules/manager_test.go
#	pkg/query-service/rules/prom_rule.go
#	pkg/query-service/rules/prom_rule_task.go
#	pkg/query-service/rules/prom_rule_test.go
#	pkg/query-service/rules/threshold_rule_test.go
#	pkg/querybuilder/resourcefilter/tables.go
#	pkg/queryparser/queryfilterextractor/promql.go
#	pkg/ruler/o11yruler/handler.go
#	pkg/sqlmigration/088_migrate_ii_dashboards/clickhouse/overview.json
#	pkg/telemetrymetadata/metadata_test.go
#	pkg/telemetrystore/datastoretelemetrystore/telemetry.go
#	pkg/types/authtypes/name.go
#	pkg/types/authtypes/object.go
#	pkg/types/authtypes/selector.go
#	pkg/types/authtypes/typeable.go
#	pkg/types/authtypes/typeable_anonymous.go
#	pkg/types/authtypes/typeable_metaresource.go
#	pkg/types/authtypes/typeable_metaresources.go
#	pkg/types/authtypes/typeable_organization.go
#	pkg/types/authtypes/typeable_role.go
#	pkg/types/authtypes/typeable_user.go
#	pkg/types/authtypes/uuid.go
#	pkg/types/ctxtypes/auth.go
#	pkg/types/factor_api_key.go
#	pkg/types/integrationtypes/integration.go
#	pkg/types/roletypes/store.go
#	pkg/types/ruletypes/maintenance.go
#	pkg/types/serviceaccounttypes/service_account_role.go
#	pkg/types/serviceaccounttypes/store.go
2026-06-02 10:54:25 -07:00

446 lines
12 KiB
Go

package querier
import (
"context"
"encoding/json"
"fmt"
"testing"
"time"
"github.com/hanzoai/o11y/pkg/cache"
"github.com/hanzoai/o11y/pkg/cache/cachetest"
"github.com/hanzoai/o11y/pkg/instrumentation/instrumentationtest"
qbtypes "github.com/hanzoai/o11y/pkg/types/querybuildertypes/querybuildertypesv5"
"github.com/hanzoai/o11y/pkg/types/telemetrytypes"
"github.com/hanzoai/o11y/pkg/valuer"
"github.com/stretchr/testify/require"
)
// BenchmarkBucketCache_GetMissRanges benchmarks the GetMissRanges operation.
func BenchmarkBucketCache_GetMissRanges(b *testing.B) {
bc := createBenchmarkBucketCache(b)
ctx := context.Background()
orgID := valuer.UUID{}
// Pre-populate cache with some data
for i := 0; i < 10; i++ {
query := &mockQuery{
fingerprint: fmt.Sprintf("bench-query-%d", i),
startMs: uint64(i * 10000),
endMs: uint64((i + 1) * 10000),
}
result := createBenchmarkResult(query.startMs, query.endMs, 1000)
bc.Put(ctx, orgID, query, qbtypes.Step{Duration: 1000 * time.Millisecond}, result)
}
// Create test queries with varying cache hit patterns
queries := []struct {
name string
query *mockQuery
}{
{
name: "full_cache_hit",
query: &mockQuery{
fingerprint: "bench-query-5",
startMs: 50000,
endMs: 60000,
},
},
{
name: "full_cache_miss",
query: &mockQuery{
fingerprint: "bench-query-new",
startMs: 100000,
endMs: 110000,
},
},
{
name: "partial_cache_hit",
query: &mockQuery{
fingerprint: "bench-query-5",
startMs: 45000,
endMs: 65000,
},
},
}
for _, tc := range queries {
b.Run(tc.name, func(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
cached, missing := bc.GetMissRanges(ctx, orgID, tc.query, qbtypes.Step{Duration: 1000 * time.Millisecond})
_ = cached
_ = missing
}
})
}
}
// BenchmarkBucketCache_Put benchmarks the Put operation.
func BenchmarkBucketCache_Put(b *testing.B) {
bc := createBenchmarkBucketCache(b)
ctx := context.Background()
orgID := valuer.UUID{}
testCases := []struct {
name string
numSeries int
numValues int
numQueries int
}{
{"small_result_1_series_100_values", 1, 100, 1},
{"medium_result_10_series_100_values", 10, 100, 1},
{"large_result_100_series_100_values", 100, 100, 1},
{"huge_result_1000_series_100_values", 1000, 100, 1},
{"many_values_10_series_1000_values", 10, 1000, 1},
}
for _, tc := range testCases {
b.Run(tc.name, func(b *testing.B) {
// Create test data
queries := make([]*mockQuery, tc.numQueries)
results := make([]*qbtypes.Result, tc.numQueries)
for i := 0; i < tc.numQueries; i++ {
queries[i] = &mockQuery{
fingerprint: fmt.Sprintf("bench-put-query-%d", i),
startMs: uint64(i * 100000),
endMs: uint64((i + 1) * 100000),
}
results[i] = createBenchmarkResultWithSeries(
queries[i].startMs,
queries[i].endMs,
1000,
tc.numSeries,
tc.numValues,
)
}
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for j := 0; j < tc.numQueries; j++ {
bc.Put(ctx, orgID, queries[j], qbtypes.Step{Duration: 1000 * time.Millisecond}, results[j])
}
}
})
}
}
// BenchmarkBucketCache_MergeTimeSeriesValues benchmarks merging of time series data.
func BenchmarkBucketCache_MergeTimeSeriesValues(b *testing.B) {
bc := createBenchmarkBucketCache(b).(*bucketCache)
testCases := []struct {
name string
numBuckets int
numSeries int
numValues int
}{
{"small_2_buckets_10_series", 2, 10, 100},
{"medium_5_buckets_50_series", 5, 50, 100},
{"large_10_buckets_100_series", 10, 100, 100},
{"many_buckets_20_buckets_50_series", 20, 50, 100},
}
for _, tc := range testCases {
b.Run(tc.name, func(b *testing.B) {
// Create test buckets
buckets := make([]*qbtypes.CachedBucket, tc.numBuckets)
for i := 0; i < tc.numBuckets; i++ {
startMs := uint64(i * 10000)
endMs := uint64((i + 1) * 10000)
result := createBenchmarkResultWithSeries(startMs, endMs, 1000, tc.numSeries, tc.numValues)
valueBytes, _ := json.Marshal(result.Value)
buckets[i] = &qbtypes.CachedBucket{
StartMs: startMs,
EndMs: endMs,
Type: qbtypes.RequestTypeTimeSeries,
Value: valueBytes,
Stats: result.Stats,
}
}
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
result := bc.mergeTimeSeriesValues(context.Background(), buckets)
_ = result
}
})
}
}
// BenchmarkBucketCache_FindMissingRangesWithStep benchmarks finding missing ranges.
func BenchmarkBucketCache_FindMissingRangesWithStep(b *testing.B) {
bc := createBenchmarkBucketCache(b).(*bucketCache)
testCases := []struct {
name string
numBuckets int
gapPattern string // "none", "uniform", "random"
}{
{"no_gaps_10_buckets", 10, "none"},
{"uniform_gaps_10_buckets", 10, "uniform"},
{"random_gaps_20_buckets", 20, "random"},
{"many_buckets_100", 100, "uniform"},
}
for _, tc := range testCases {
b.Run(tc.name, func(b *testing.B) {
// Create test buckets based on pattern
buckets := createBucketsWithPattern(tc.numBuckets, tc.gapPattern)
startMs := uint64(0)
endMs := uint64(tc.numBuckets * 20000)
stepMs := uint64(1000)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
missing := bc.findMissingRangesWithStep(buckets, startMs, endMs, stepMs)
_ = missing
}
})
}
}
// BenchmarkGetUniqueSeriesKey benchmarks the series key generation.
func BenchmarkGetUniqueSeriesKey(b *testing.B) {
testCases := []struct {
name string
numLabels int
}{
{"1_label", 1},
{"5_labels", 5},
{"10_labels", 10},
{"20_labels", 20},
{"50_labels", 50},
}
for _, tc := range testCases {
b.Run(tc.name, func(b *testing.B) {
labels := make([]*qbtypes.Label, tc.numLabels)
for i := 0; i < tc.numLabels; i++ {
labels[i] = &qbtypes.Label{
Key: telemetrytypes.TelemetryFieldKey{
Name: fmt.Sprintf("label_%d", i),
FieldDataType: telemetrytypes.FieldDataTypeString,
},
Value: fmt.Sprintf("value_%d", i),
}
}
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
key := qbtypes.GetUniqueSeriesKey(labels)
_ = key
}
})
}
}
// BenchmarkBucketCache_ConcurrentOperations benchmarks concurrent cache operations.
func BenchmarkBucketCache_ConcurrentOperations(b *testing.B) {
bc := createBenchmarkBucketCache(b)
ctx := context.Background()
orgID := valuer.UUID{}
// Pre-populate cache
for i := 0; i < 100; i++ {
query := &mockQuery{
fingerprint: fmt.Sprintf("concurrent-query-%d", i),
startMs: uint64(i * 10000),
endMs: uint64((i + 1) * 10000),
}
result := createBenchmarkResult(query.startMs, query.endMs, 1000)
bc.Put(ctx, orgID, query, qbtypes.Step{Duration: 1000 * time.Millisecond}, result)
}
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
i := 0
for pb.Next() {
// Mix of operations
switch i % 3 {
case 0: // Read
query := &mockQuery{
fingerprint: fmt.Sprintf("concurrent-query-%d", i%100),
startMs: uint64((i % 100) * 10000),
endMs: uint64(((i % 100) + 1) * 10000),
}
cached, missing := bc.GetMissRanges(ctx, orgID, query, qbtypes.Step{Duration: 1000 * time.Millisecond})
_ = cached
_ = missing
case 1: // Write
query := &mockQuery{
fingerprint: fmt.Sprintf("concurrent-query-new-%d", i),
startMs: uint64(i * 10000),
endMs: uint64((i + 1) * 10000),
}
result := createBenchmarkResult(query.startMs, query.endMs, 1000)
bc.Put(ctx, orgID, query, qbtypes.Step{Duration: 1000 * time.Millisecond}, result)
case 2: // Partial read
query := &mockQuery{
fingerprint: fmt.Sprintf("concurrent-query-%d", i%100),
startMs: uint64((i%100)*10000 - 5000),
endMs: uint64(((i%100)+1)*10000 + 5000),
}
cached, missing := bc.GetMissRanges(ctx, orgID, query, qbtypes.Step{Duration: 1000 * time.Millisecond})
_ = cached
_ = missing
}
i++
}
})
}
// BenchmarkBucketCache_FilterResultToTimeRange benchmarks filtering results to time range.
func BenchmarkBucketCache_FilterResultToTimeRange(b *testing.B) {
bc := createBenchmarkBucketCache(b).(*bucketCache)
testCases := []struct {
name string
numSeries int
numValues int
}{
{"small_10_series_100_values", 10, 100},
{"medium_50_series_500_values", 50, 500},
{"large_100_series_1000_values", 100, 1000},
}
for _, tc := range testCases {
b.Run(tc.name, func(b *testing.B) {
// Create a large result
result := createBenchmarkResultWithSeries(0, 100000, 1000, tc.numSeries, tc.numValues)
// Filter to middle 50%
startMs := uint64(25000)
endMs := uint64(75000)
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
filtered := bc.filterResultToTimeRange(result, startMs, endMs)
_ = filtered
}
})
}
}
// Helper function to create benchmark bucket cache.
func createBenchmarkBucketCache(tb testing.TB) BucketCache {
config := cache.Config{
Provider: "memory",
Memory: cache.Memory{
NumCounters: 10 * 1000,
MaxCost: 1 << 26,
},
}
memCache, err := cachetest.New(config)
require.NoError(tb, err)
return NewBucketCache(instrumentationtest.New().ToProviderSettings(), memCache, time.Hour, 5*time.Minute)
}
// Helper function to create benchmark result.
func createBenchmarkResult(startMs, endMs uint64, step uint64) *qbtypes.Result {
return createBenchmarkResultWithSeries(startMs, endMs, step, 10, 100)
}
// Helper function to create benchmark result with specific series and values.
func createBenchmarkResultWithSeries(startMs, endMs uint64, _ uint64, numSeries, numValuesPerSeries int) *qbtypes.Result {
series := make([]*qbtypes.TimeSeries, numSeries)
for i := 0; i < numSeries; i++ {
ts := &qbtypes.TimeSeries{
Labels: []*qbtypes.Label{
{
Key: telemetrytypes.TelemetryFieldKey{
Name: "host",
FieldDataType: telemetrytypes.FieldDataTypeString,
},
Value: fmt.Sprintf("server-%d", i),
},
{
Key: telemetrytypes.TelemetryFieldKey{
Name: "service",
FieldDataType: telemetrytypes.FieldDataTypeString,
},
Value: fmt.Sprintf("service-%d", i%5),
},
},
Values: make([]*qbtypes.TimeSeriesValue, 0, numValuesPerSeries),
}
// Generate values
valueStep := (endMs - startMs) / uint64(numValuesPerSeries)
if valueStep == 0 {
valueStep = 1
}
for j := 0; j < numValuesPerSeries; j++ {
timestamp := int64(startMs + uint64(j)*valueStep)
if timestamp < int64(endMs) {
ts.Values = append(ts.Values, &qbtypes.TimeSeriesValue{
Timestamp: timestamp,
Value: float64(i*100 + j),
})
}
}
series[i] = ts
}
return &qbtypes.Result{
Type: qbtypes.RequestTypeTimeSeries,
Value: &qbtypes.TimeSeriesData{
QueryName: "benchmark_query",
Aggregations: []*qbtypes.AggregationBucket{
{
Index: 0,
Series: series,
},
},
},
Stats: qbtypes.ExecStats{
RowsScanned: uint64(numSeries * numValuesPerSeries),
BytesScanned: uint64(numSeries * numValuesPerSeries * 100),
DurationMS: 10,
},
}
}
// Helper function to create buckets with specific gap patterns.
func createBucketsWithPattern(numBuckets int, pattern string) []*qbtypes.CachedBucket {
buckets := make([]*qbtypes.CachedBucket, 0, numBuckets)
for i := 0; i < numBuckets; i++ {
// Skip some buckets based on pattern
if pattern == "uniform" && i%3 == 0 {
continue // Create gaps every 3rd bucket
}
if pattern == "random" && i%7 < 2 {
continue // Create random gaps
}
startMs := uint64(i * 10000)
endMs := uint64((i + 1) * 10000)
buckets = append(buckets, &qbtypes.CachedBucket{
StartMs: startMs,
EndMs: endMs,
Type: qbtypes.RequestTypeTimeSeries,
Value: json.RawMessage(`{}`),
Stats: qbtypes.ExecStats{},
})
}
return buckets
}