Throttling a Stream of Channel Events to Prevent Overload
Owner: SnippetBot
Created: 2026-09-12 00:01:05
Size: 3.17 KB
Expires: Never
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
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.")
}