github.com/koko1123/flow-go-1@v0.29.6/module/irrecoverable/irrecoverable_example_test.go (about) 1 package irrecoverable_test 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "sync" 8 "time" 9 10 "github.com/koko1123/flow-go-1/module/component" 11 "github.com/koko1123/flow-go-1/module/irrecoverable" 12 ) 13 14 var ErrTriggerRestart = errors.New("restart me") 15 var ErrNoRestart = errors.New("fatal, no restarts") 16 17 func Example() { 18 // a context is mandatory in order to call RunComponent 19 ctx, cancel := context.WithCancel(context.Background()) 20 defer cancel() 21 22 // component.ComponentFactory encapsulates all of the component building logic 23 // required before running Start() 24 starts := 0 25 componentFactory := func() (component.Component, error) { 26 starts++ 27 return NewExampleComponent(starts), nil 28 } 29 30 // this is the place to inspect the encountered error and implement the appropriate error 31 // handling behaviors, e.g. restarting the component, firing an alert to pagerduty, etc ... 32 // the shutdown of the component is handled for you by RunComponent, but you may consider 33 // performing additional cleanup here 34 onError := func(err error) component.ErrorHandlingResult { 35 // check the error type to decide whether to restart or shutdown 36 if errors.Is(err, ErrTriggerRestart) { 37 fmt.Printf("Restarting component after fatal error: %v\n", err) 38 return component.ErrorHandlingRestart 39 } else { 40 fmt.Printf("An irrecoverable error occurred: %v\n", err) 41 // shutdown other components. it might also make sense to just panic here 42 // depending on the circumstances 43 return component.ErrorHandlingStop 44 } 45 } 46 47 // run the component. this is a blocking call, and will return with an error if the 48 // first startup or any subsequent restart attempts fails or the context is canceled 49 err := component.RunComponent(ctx, componentFactory, onError) 50 if err != nil { 51 fmt.Printf("Error returned from RunComponent: %v\n", err) 52 } 53 54 // Output: 55 // [Component 1] Starting up 56 // [Component 1] Shutting down 57 // Restarting component after fatal error: restart me 58 // [Component 2] Starting up 59 // [Component 2] Shutting down 60 // An irrecoverable error occurred: fatal, no restarts 61 // Error returned from RunComponent: fatal, no restarts 62 } 63 64 // ExampleComponent is an example of a typical component 65 type ExampleComponent struct { 66 id int 67 started chan struct{} 68 ready sync.WaitGroup 69 done sync.WaitGroup 70 } 71 72 func NewExampleComponent(id int) *ExampleComponent { 73 return &ExampleComponent{ 74 id: id, 75 started: make(chan struct{}), 76 } 77 } 78 79 // start the component and register its shutdown handler 80 // this component will throw an error after 20ms to demonstrate the error handling 81 func (c *ExampleComponent) Start(ctx irrecoverable.SignalerContext) { 82 c.printMsg("Starting up") 83 84 // do some setup... 85 86 c.ready.Add(2) 87 c.done.Add(2) 88 89 go func() { 90 c.ready.Done() 91 defer c.done.Done() 92 93 <-ctx.Done() 94 95 c.printMsg("Shutting down") 96 // do some cleanup... 97 }() 98 99 go func() { 100 c.ready.Done() 101 defer c.done.Done() 102 103 select { 104 case <-time.After(20 * time.Millisecond): 105 // encounter irrecoverable error 106 if c.id > 1 { 107 ctx.Throw(ErrNoRestart) 108 } else { 109 ctx.Throw(ErrTriggerRestart) 110 } 111 case <-ctx.Done(): 112 c.printMsg("Cancelled by parent") 113 } 114 }() 115 116 close(c.started) 117 } 118 119 // simply return the Started channel 120 // all startup processing is done in Start() 121 func (c *ExampleComponent) Ready() <-chan struct{} { 122 ready := make(chan struct{}) 123 go func() { 124 <-c.started 125 c.ready.Wait() 126 close(ready) 127 }() 128 return ready 129 } 130 131 // simply return the Stopped channel 132 // all shutdown processing is done in shutdownOnCancel() 133 func (c *ExampleComponent) Done() <-chan struct{} { 134 done := make(chan struct{}) 135 go func() { 136 <-c.started 137 c.done.Wait() 138 close(done) 139 }() 140 return done 141 } 142 143 func (c *ExampleComponent) printMsg(msg string) { 144 fmt.Printf("[Component %d] %s\n", c.id, msg) 145 }