package main import ( "fmt" "time" ) // Event represents some data received, e.g., a webhook payload type Event struct { ID int Timestamp time.Time Payload string } // eventProcessor processes events at a throttled rate // It reads from `in`, processes, and sends to `out`. // `throttleInterval` defines the minimum time between sending events to `out`. func eventProcessor(in <-chan Event, out chan<- Event, throttleInterval time.Duration, shutdown <-chan struct{}) { ticker := time.NewTicker(throttleInterval) defer ticker.Stop() defer close(out) // Ensure output channel is closed on exit fmt.Println("[EventProcessor] Started.") for { select { case <-shutdown: fmt.Println("[EventProcessor] Shutdown signal received. Exiting.") return case <-ticker.C: // A "slot" is available to process and send an event. select { case event, ok := <-in: if !ok { fmt.Println("[EventProcessor] Input channel closed. No more new events to throttle.") return // Input channel closed, so nothing more to process. } fmt.Printf("[EventProcessor] Processing event ID %d at %s (throttled) ", event.ID, time.Now().Format("15:04:05.000")) // Simulate some work time.Sleep(30 * time.Millisecond) out <- event // Send the event to the output channel case <-shutdown: // Check shutdown again while waiting for event if `in` is empty fmt.Println("[EventProcessor] Shutdown signal received while waiting for an event. Exiting.") return default: // No event currently available in 'in' channel, and ticker fired. // This means we are not currently backlogged on 'in'. // Continue waiting for either new event or next ticker. } } } } func main() { inputEvents := make(chan Event, 20) // Buffered input channel outputEvents := make(chan Event, 20) // Buffered output channel shutdownCh := make(chan struct{}) // Process at most 1 event per 200 milliseconds go eventProcessor(inputEvents, outputEvents, 200*time.Millisecond, shutdownCh) // Simulate a burst of incoming events fmt.Println("[Main] Sending a burst of 10 events...") for i := 1; i <= 10; i++ { inputEvents <- Event{ID: i, Timestamp: time.Now(), Payload: fmt.Sprintf("Data-%d", i)} time.Sleep(50 * time.Millisecond) // Events come in faster than throttle rate (50ms vs 200ms) } fmt.Println("[Main] Sending a slow event after a delay...") time.Sleep(1 * time.Second) inputEvents <- Event{ID: 11, Timestamp: time.Now(), Payload: "Data-11"} // Goroutine to consume processed events go func() { for processedEvent := range outputEvents { fmt.Printf("[Main] Received processed event ID %d from output channel ", processedEvent.ID) } fmt.Println("[Main] Output channel closed by processor.") }() // Allow processor to work and drain events time.Sleep(5 * time.Second) fmt.Println("[Main] Closing input events channel.") close(inputEvents) // Signal that no more input events will come time.Sleep(1 * time.Second) // Give time for `eventProcessor` to detect `in` closure and exit fmt.Println("[Main] Signaling shutdown.") close(shutdownCh) // Wait a bit for everything to settle time.Sleep(500 * time.Millisecond) fmt.Println("[Main] Application finished.") }