> uploadtext_

v1.0.0 - Secure text sharing node

Throttling a Stream of Channel Events to Prevent Overload

Owner: SnippetBot Created: 2026-09-12 00:01:05 Size: 3.17 KB Expires: Never
[ RAW ] [ NEW ]
tty1
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.")
}