Spring boot project to demonstrate a thundering herd problem & its solution.
Thundering Herd Problem: Preventing the Stampede
- Commit with thundering herd problem ❌
- Commit with thundering herd problem fixed ✅
- Commit with thundering herd problem fixed with CompletableFuture ✅
- Start the Spring Boot application
- Run the following Go program (
go run main.go <product_id>) to send concurrent requests for the GET endpoint to trigger thundering herd
package main
import (
"fmt"
"io"
"net/http"
"os"
"sync"
)
func main() {
args := os.Args
if len(args) != 2 {
fmt.Println("Usage: go run main.go <product_id>")
return
}
numRequests := 5
var wg sync.WaitGroup
productId := os.Args[1]
url := fmt.Sprintf("http://localhost:8080/api/v1/products/%s", productId)
// Channel to signal all goroutines to start simultaneously
startSignal := make(chan struct{})
// Channel to collect results
results := make(chan string, numRequests)
for i := 0; i < numRequests; i++ {
wg.Add(1)
go func(requestID int) {
defer wg.Done()
<-startSignal // Wait for the signal to start
resp, err := http.Get(url)
if err != nil {
fmt.Printf("Error: %v\n", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Printf("Error reading body: %v\n", err)
}
result := fmt.Sprintf("Request %d - Status: %d, Response: %s",
requestID, resp.StatusCode, string(body))
results <- result
}(i)
}
fmt.Println("Starting all requests...")
close(startSignal) // Signal all goroutines to start
wg.Wait()
close(results)
for result := range results {
fmt.Println(result)
}
}