Repository: prometheus/alertmanager
File: dispatch/dispatch_bench_test.go (Go)

Code before:

	// Wait for dispatcher to create all expected groups
	require.Eventually(b, func() bool {
		groups, _, _ := d.Groups(ctx,
			func(*Route) bool { return true },
			func(*types.Alert, time.Time) bool { return true },
		)
		return len(groups) >= expectedMinGroups
	}, 30*time.Second, 10*time.Millisecond, "expected %d groups to be created", expectedMinGroups)
}

// BenchmarkGroups simulates a realistic production scenario:
// - 500 leaf routes in a deep hierarchy (25 teams × 4 clusters × 5 priorities)
// - 5000 stable aggregation groups (average ~10 per leaf route)
// - Measures Groups() API latency (simulates GET /api/v2/alerts/groups)
//
// This benchmark demonstrates the concurrent dispatcher's benefit:
// - main branch: Global lock blocks all Groups() calls during any alert processing
// - concurrent dispatcher: Per-route locks allow Groups() to run mostly lock-free.
func BenchmarkGroups(b *testing.B) {
	b.Run("500 routes, 5000 groups", func(b *testing.B) {
		benchmarkGroups(b, 5000, 3, 25, 4, 5)
	})
	b.Run("120 routes, 10000 groups", func(b *testing.B) {
		benchmarkGroups(b, 10000, 3, 20, 4, 5)
	})
}

func benchmarkGroups(b *testing.B, numGroups, alertsPerGroup, numTeams, numClusters, numPriorities int) {
	route := buildDeepRouteTree(numTeams, numClusters, numPriorities)

	b.ReportAllocs()

	dispatcher, alerts, _ := setupDispatcher(b, route)
	go dispatcher.Run(time.Now())
	defer dispatcher.Stop()

	// Pre-populate with stable groups (uses existing helper)
	populateGroups(b, dispatcher, alerts, numGroups, alertsPerGroup, numTeams, numClusters, numPriorities, numGroups)

	ctx := context.Background()

	routeFilter := func(*Route) bool { return true }
	alertFilter := func(*types.Alert, time.Time) bool { return true }

	b.ResetTimer()

	// Measure Groups() API latency
	for b.Loop() {
		groups, _, _ := dispatcher.Groups(ctx, routeFilter, alertFilter)
		if len(groups) != numGroups {

Review comment (suggestion):
I think this produces 400 leaf routes:
```suggestion
	b.Run("400 routes, 10000 groups", func(b *testing.B) {
		benchmarkGroups(b, 10000, 3, 20, 4, 5)
	})
```

Code after:

	// Wait for dispatcher to create all expected groups
	require.Eventually(b, func() bool {
		groups, _, _ := d.Groups(ctx,
			func(*Route) bool { return true },
			func(*types.Alert, time.Time) bool { return true },
		)
		return len(groups) >= expectedMinGroups
	}, 30*time.Second, 10*time.Millisecond, "expected %d groups to be created", expectedMinGroups)
}

// BenchmarkGroups simulates a realistic production scenario:
// - 500 leaf routes in a deep hierarchy (25 teams × 4 clusters × 5 priorities)
// - 5000 stable aggregation groups (average ~10 per leaf route)
// - Measures Groups() API latency (simulates GET /api/v2/alerts/groups)
//
// This benchmark demonstrates the concurrent dispatcher's benefit:
// - main branch: Global lock blocks all Groups() calls during any alert processing
// - concurrent dispatcher: Per-route locks allow Groups() to run mostly lock-free.
func BenchmarkGroups(b *testing.B) {
	b.Run("500 routes, 5000 groups", func(b *testing.B) {
		benchmarkGroups(b, 5000, 3, 25, 4, 5)
	})
	b.Run("400 routes, 10000 groups", func(b *testing.B) {
		benchmarkGroups(b, 10000, 3, 20, 4, 5)
	})
}

func benchmarkGroups(b *testing.B, numGroups, alertsPerGroup, numTeams, numClusters, numPriorities int) {
	route := buildDeepRouteTree(numTeams, numClusters, numPriorities)

	b.ReportAllocs()

	dispatcher, alerts, _ := setupDispatcher(b, route)
	go dispatcher.Run(time.Now())
	defer dispatcher.Stop()

	// Pre-populate with stable groups (uses existing helper)
	populateGroups(b, dispatcher, alerts, numGroups, alertsPerGroup, numTeams, numClusters, numPriorities, numGroups)

	ctx := context.Background()

	routeFilter := func(*Route) bool { return true }
	alertFilter := func(*types.Alert, time.Time) bool { return true }

	b.ResetTimer()

	// Measure Groups() API latency
	for b.Loop() {
		groups, _, _ := dispatcher.Groups(ctx, routeFilter, alertFilter)
		if len(groups) != numGroups {