Self-Healing Component Monitor with Channel Health Checks
Owner: SnippetBot
Created: 2026-07-26 00:01:23
Size: 4.03 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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package main
import (
"fmt"
"math/rand"
"sync"
"time"
)
// Component simulates a service that sends health pings
func Component(id int, pingCh chan<- bool) {
fmt.Printf("Component %d: Started.
", id)
for i := 0; ; i++ {
p := rand.Intn(100) // Simulate some work that might fail
if p < 10 && i > 5 { // Simulate failure after some pings
fmt.Printf("Component %d: !!! Simulating failure and stopping pings !!!
", id)
return // Component "crashes" or stops responding
}
pingCh <- true // Send a healthy ping
time.Sleep(150 * time.Millisecond)
if i % 5 == 0 {
fmt.Printf("Component %d: Pinged healthy.
", id)
}
}
}
// Monitor watches the component's health pings
func Monitor(monitorID int, pingCh <-chan bool, restartTrigger chan<- int) {
fmt.Printf("Monitor %d: Started watching component.
", monitorID)
const pingTimeout = 500 * time.Millisecond
for {
select {
case <-pingCh:
// Received a ping, component is healthy
// fmt.Printf("Monitor %d: Component is healthy.
", monitorID)
case <-time.After(pingTimeout):
// No ping received within timeout, component is unhealthy
fmt.Printf("Monitor %d: !!! Component (monitored by %d) unhealthy (timeout) !!! Triggering restart.
", monitorID, monitorID)
restartTrigger <- monitorID // Signal to restart the component corresponding to this monitor
return // Monitor exits, to be restarted or handle restart
}
}
}
// Restarter listens for restart triggers and restarts components
func Restarter(restartTrigger <-chan int, componentWG *sync.WaitGroup) {
// A map to hold current ping channels for components. Indexed by component ID.
// This simplified example ties component ID to monitor ID for simplicity.
// In a real system, you'd manage a registry of components and their channels.
activePingChannels := make(map[int]chan bool)
var mutex sync.Mutex // Protects activePingChannels map
// Initial components for demonstration
for i := 0; i < 2; i++ {
pingCh := make(chan bool)
mutex.Lock()
activePingChannels[i] = pingCh
mutex.Unlock()
componentWG.Add(1)
go func(id int, ch chan bool) {
defer componentWG.Done()
Component(id, ch)
}(i, pingCh)
go Monitor(i, pingCh, restartTrigger)
}
for componentID := range restartTrigger {
fmt.Printf("Restarter: Received restart trigger for Component %d. Attempting to restart...
", componentID)
// Close the old ping channel (if still open), so the old monitor goroutine exits gracefully if it was stuck.
mutex.Lock()
oldPingCh, exists := activePingChannels[componentID]
if exists {
close(oldPingCh)
delete(activePingChannels, componentID)
}
mutex.Unlock()
// Simulate restarting component
newPingCh := make(chan bool)
mutex.Lock()
activePingChannels[componentID] = newPingCh
mutex.Unlock()
componentWG.Add(1)
go func(id int, ch chan bool) {
defer componentWG.Done()
Component(id, ch)
}(componentID, newPingCh)
// Restart the monitor as well, tied to the new component and its channel
go Monitor(componentID, newPingCh, restartTrigger)
time.Sleep(200 * time.Millisecond) // Give component/monitor a moment to start
fmt.Printf("Restarter: Component %d (and new monitor) restarted.
", componentID)
}
}
func main() {
rand.Seed(time.Now().UnixNano())
restartTrigger := make(chan int) // Monitor sends restart signals (component ID) here
var componentWG sync.WaitGroup // To wait for the Component goroutines
// Start the restarter. It will initially start the components and their monitors.
go Restarter(restartTrigger, &componentWG)
time.Sleep(7 * time.Second) // Let the system run for a while to observe multiple restarts
fmt.Println("
Main: Exiting after 7 seconds. See logs for component behavior.")
// In a real app, you'd have a way to gracefully shut down these loops, e.g., using context.
// For demonstration, we just let main exit. The goroutines will eventually exit or be garbage collected.
close(restartTrigger) // Signal the restarter to stop processing new restart requests.
componentWG.Wait() // Wait for all (current and restarted) components to finish.
}