Files
cv-site/examples/demo_goroutine_fix.go
T
juanatsap 92dffe8c60 feat: add comprehensive testing infrastructure and security hardening
- Enhanced CI/CD pipeline with coverage reporting, benchmarks, and artifact uploads
- Implemented rate limiter IP validation with proxy support and spoofing protection
- Added extensive Makefile test targets for coverage, benchmarks, and continuous testing
- Expanded middleware chain with request validation, size limits, and suspicious activity logging
2025-11-11 21:43:12 +00:00

116 lines
2.7 KiB
Go

package main
import (
"context"
"fmt"
"runtime"
"time"
)
// Simulate the OLD broken version
type BrokenRateLimiter struct{}
func NewBrokenRateLimiter() *BrokenRateLimiter {
go func() {
for {
time.Sleep(1 * time.Minute)
// Cleanup... but never stops!
}
}()
return &BrokenRateLimiter{}
}
// Simulate the NEW fixed version
type FixedRateLimiter struct {
quit chan struct{}
done chan struct{}
}
func NewFixedRateLimiter() *FixedRateLimiter {
rl := &FixedRateLimiter{
quit: make(chan struct{}),
done: make(chan struct{}),
}
go rl.cleanup()
return rl
}
func (rl *FixedRateLimiter) cleanup() {
ticker := time.NewTicker(1 * time.Minute)
defer ticker.Stop()
defer close(rl.done)
for {
select {
case <-ticker.C:
// Cleanup
case <-rl.quit:
return
}
}
}
func (rl *FixedRateLimiter) Shutdown(ctx context.Context) error {
close(rl.quit)
select {
case <-rl.done:
return nil
case <-ctx.Done():
return ctx.Err()
}
}
func main() {
fmt.Println("========================================")
fmt.Println("GOROUTINE LEAK DEMONSTRATION")
fmt.Println("========================================")
fmt.Println()
// Test 1: Broken Version (BEFORE FIX)
fmt.Println("Test 1: BROKEN VERSION (BEFORE FIX)")
fmt.Println("------------------------------------")
before := runtime.NumGoroutine()
fmt.Printf("Goroutines at start: %d\n", before)
for i := 1; i <= 5; i++ {
_ = NewBrokenRateLimiter()
time.Sleep(10 * time.Millisecond)
count := runtime.NumGoroutine()
fmt.Printf("After creating limiter #%d: %d goroutines (+%d leaked)\n", i, count, count-before)
}
leaked := runtime.NumGoroutine() - before
fmt.Printf("\n❌ RESULT: %d goroutines LEAKED!\n", leaked)
fmt.Println()
// Test 2: Fixed Version (AFTER FIX)
fmt.Println("Test 2: FIXED VERSION (AFTER FIX)")
fmt.Println("----------------------------------")
runtime.GC() // Force garbage collection
time.Sleep(50 * time.Millisecond)
before = runtime.NumGoroutine()
fmt.Printf("Goroutines at start: %d\n", before)
ctx := context.Background()
for i := 1; i <= 5; i++ {
rl := NewFixedRateLimiter()
time.Sleep(10 * time.Millisecond)
_ = rl.Shutdown(ctx)
time.Sleep(10 * time.Millisecond)
count := runtime.NumGoroutine()
fmt.Printf("After limiter #%d (created & shutdown): %d goroutines (leaked: %d)\n", i, count, count-before)
}
after := runtime.NumGoroutine()
fmt.Printf("\n✅ RESULT: %d goroutines leaked (properly cleaned up!)\n", after-before)
fmt.Println()
fmt.Println("========================================")
fmt.Println("CONCLUSION")
fmt.Println("========================================")
fmt.Println("BEFORE FIX: Each rate limiter leaked 1 goroutine")
fmt.Println("AFTER FIX: Goroutines properly cleaned up on shutdown")
fmt.Println()
}