-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.go
More file actions
133 lines (117 loc) · 4.22 KB
/
main.go
File metadata and controls
133 lines (117 loc) · 4.22 KB
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
128
129
130
131
132
133
package main
import (
"context"
"errors"
"fmt"
"log/slog"
"time"
"github.com/aretw0/lifecycle"
)
func main() {
// 1. Setup Metrics (Log provider for easy viewing)
lifecycle.SetMetricsProvider(lifecycle.NewLogMetricsProvider())
// 3. Define the "Showcase" Supervisor
// This supervisor will manage three types of workers to demonstrate reliability.
// A. The Stable Worker: Never fails.
stableFactory := func() (lifecycle.Worker, error) {
return lifecycle.NewWorkerFromFunc("stable-worker", func(ctx context.Context) error {
fmt.Println(" [✓] Stable worker started.")
<-ctx.Done()
fmt.Println(" [✓] Stable worker stopped.")
return nil
}), nil
}
// B. The Flaky Worker: Retries periodically but will eventually trigger the circuit breaker.
flakyFactory := func() (lifecycle.Worker, error) {
return lifecycle.NewWorkerFromFunc("flaky-worker", func(ctx context.Context) error {
fmt.Println(" [!] Flaky worker starting...")
select {
case <-ctx.Done():
return nil
case <-time.After(3 * time.Second):
fmt.Println(" [!] Flaky worker CRASHING!")
return errors.New("temporary failure")
}
}), nil
}
// C. The Critical Worker: If this fails, the app has issues.
criticalFactory := func() (lifecycle.Worker, error) {
return lifecycle.NewWorkerFromFunc("critical-worker", func(ctx context.Context) error {
fmt.Println(" [★] Critical worker active.")
<-ctx.Done()
return nil
}), nil
}
sup := lifecycle.NewSupervisor("Cluster-A", lifecycle.SupervisorStrategyOneForOne,
lifecycle.SupervisorSpec{
Name: "stable-1",
Type: "process",
Factory: stableFactory,
},
lifecycle.SupervisorSpec{
Name: "flaky-api",
Type: "container",
Factory: flakyFactory,
Backoff: lifecycle.SupervisorBackoff{
InitialInterval: 500 * time.Millisecond,
MaxInterval: 2 * time.Second,
Multiplier: 2.0,
MaxRestarts: 3, // Trigger circuit breaker after 3 restarts
MaxDuration: 30 * time.Second,
},
},
lifecycle.SupervisorSpec{
Name: "core-logic",
Type: "func",
Factory: criticalFactory,
},
)
// 6. Run Everything
fmt.Println("================================================================")
fmt.Println(" LIFECYCLE V1.5+ - RELIABILITY SHOWCASE")
fmt.Println("================================================================")
fmt.Println(" This demo shows:")
fmt.Println(" 1. Auto-healing (Supervisor)")
fmt.Println(" 2. Protection (Circuit Breaker)")
fmt.Println(" 3. Introspection (Mermaid via 'status' command)")
fmt.Println(" 4. Control (interactive 's' to suspend, 'r' to resume, 'q' to quit)")
fmt.Println("================================================================")
fmt.Println(" Type 'status' to see the initial tree.")
err := lifecycle.Run(lifecycle.Job(func(ctx context.Context) error {
// Setup Interactive Router
suspendHandler := lifecycle.NewSuspendHandler()
// Define a panic handler for demonstration purposes
panicHandler := lifecycle.HandlerFunc(func(ctx context.Context, e lifecycle.Event) error {
fmt.Println("\n [!!!] PANIC command received! Crashing application...")
panic("simulated panic from interactive command")
})
router := lifecycle.NewInteractiveRouter(
lifecycle.WithSuspendOnInterrupt(suspendHandler),
lifecycle.WithDefaultMappings(),
lifecycle.WithCommand("panic", panicHandler),
lifecycle.WithShutdown(func() {
fmt.Println("\n [!] Shutdown requested via command.")
lifecycle.Shutdown(ctx)
}),
lifecycle.WithCommand("status", lifecycle.HandlerFunc(func(ctx context.Context, e lifecycle.Event) error {
fmt.Println("--- LIVE TOPOLOGY DIAGRAM ---")
fmt.Println(lifecycle.WorkerTreeDiagram(sup.State()))
fmt.Println("----------------------------")
return nil
})),
)
// Wire up the Supervisor to the Suspend/Resume system
suspendHandler.Manage(sup)
// Start supervisor in background via lifecycle.Go
lifecycle.Go(ctx, func(ctx context.Context) error {
return sup.Start(ctx)
})
// Start the interactive router (blocks until 'q' or signals)
return router.Start(ctx)
}),
lifecycle.WithShutdownTimeout(3*time.Second), // Parameterized shutdown diagnostics
)
if err != nil {
slog.Error("Showcase exited with error", "error", err)
}
}