Documentation ¶
Overview ¶
The Tideland Go Library loop package is intended to support the developer implementing the typical Go idiom for concurrent applications running in a loop in the background and doing a select on one or more channels. Stopping those loops or getting aware of internal errors requires extra efforts. The loop package helps to control this kind of goroutines.
Beside the simple controlled loops the also can be made recoverable. In this case a user defined recovery function gets notified if a loop ends with an error or panics. The paseed passed list of recovering information helps to check the reason and frequency.
A third way are sentinels. Those can monitor multiple loops and other sentinels. So hierarchies can be defined. In case of no handler function an error of one monitored instance will lead to a stop of all monitored instances. Otherwise the user can check the error reason inside the handler function and optionally restart the loop or sentinel.
See the example functions for more information.
Index ¶
Examples ¶
Constants ¶
const ( ErrLoopPanicked = iota + 1 ErrHandlingFailed ErrRestartNonStopped ErrKilledBySentinel )
const ( Running = iota Stopping Stopped )
Status of the loop.
Variables ¶
This section is empty.
Functions ¶
func IsKilledBySentinelError ¶
IsKilledBySentinelError allows to check, if a loop or sentinel has been stopped due to internal reasons or after the error of another loop or sentinel.
Types ¶
type Loop ¶
type Loop interface { Observable // ShallStop returns a channel signalling the loop to // stop working. ShallStop() <-chan struct{} // IsStopping returns a channel that can be used to wait until // the loop is stopping or to avoid deadlocks when communicating // with the loop. IsStopping() <-chan struct{} }
Loop manages running loops in the background as goroutines.
func Go ¶
Go starts the loop function in the background. The loop can be stopped or killed. This leads to a signal out of the channel Loop.ShallStop. The loop then has to end working returning a possible error. Wait then waits until the loop ended and returns the error.
func GoRecoverable ¶
func GoRecoverable(lf LoopFunc, rf RecoverFunc, dps ...interface{}) Loop
GoRecoverable starts the loop function in the background. The loop can be stopped or killed. This leads to a signal out of the channel Loop.ShallStop. The loop then has to end working returning a possible error. Wait then waits until the loop ended and returns the error.
If the loop panics a Recovering is created and passed with all Recoverings before to the RecoverFunc. If it returns nil the loop will be started again. Otherwise the loop will be killed with that error.
type LoopFunc ¶
LoopFunc is managed loop function.
Example ¶
ExampleLoopFunc shows the usage of loop.Go with one loop function and no recovery. The inner loop contains a select listening to the channel returned by ShallStop. Other channels are for the standard communication with the loop.
package main import ( "errors" "github.com/tideland/golib/loop" ) func main() { printC := make(chan string) // Sample loop function. loopF := func(l loop.Loop) error { for { select { case <-l.ShallStop(): // We shall stop. return nil case str := <-printC: if str == "panic" { return errors.New("panic") } println(str) } } } l := loop.Go(loopF, "simple loop demo") printC <- "Hello" printC <- "World" if err := l.Stop(); err != nil { panic(err) } }
Output:
type NotificationHandlerFunc ¶
type NotificationHandlerFunc func(s Sentinel, o Observable, rs Recoverings) (Recoverings, error)
NotificationHandlerFunc allows a sentinel to react on an observers error notification.
type Observable ¶
type Observable interface { fmt.Stringer // Stop tells the observable to stop working and waits until it is done. Stop() error // Kill kills the observable with the passed error. Kill(err error) // Wait blocks the caller until the observable ended and returns // a possible error. Wait() error // Restart stops the observable and restarts it afterwards. Restart() error // Error returns information about the current status and error. Error() (status int, err error) // contains filtered or unexported methods }
Observable is a common base interface for those objects that a sentinel can monitor.
type RecoverFunc ¶
type RecoverFunc func(rs Recoverings) (Recoverings, error)
RecoverFunc decides if a loop shall be started again or end with an error. It is also responsible to trim the list of revocerings if needed.
Example ¶
ExampleRecoverFunc demonstrates the usage of a recovery function when using loop.GoRecoverable. Here the frequency of the recoverings (more than five in 10 milliseconds) or the total number is checked. If the total number is not interesting the recoverings could be trimmed by e.g. rs.Trim(5). The fields Time and Reason per recovering allow even more diagnosis.
package main import ( "errors" "time" "github.com/tideland/golib/loop" ) func main() { printC := make(chan string) loopF := func(l loop.Loop) error { for { select { case <-l.ShallStop(): return nil case str := <-printC: println(str) } } } // Recovery function checking frequency and total number. recoverF := func(rs loop.Recoverings) (loop.Recoverings, error) { if rs.Frequency(5, 10*time.Millisecond) { return nil, errors.New("too high error frequency") } if rs.Len() >= 10 { return nil, errors.New("too many errors") } return rs, nil } loop.GoRecoverable(loopF, recoverF, "recoverable loop demo") }
Output:
type Recovering ¶
Recovering stores time and reason of one of the recoverings.
type Recoverings ¶
type Recoverings []*Recovering
Recoverings is a list of recoverings a loop already had.
func (Recoverings) First ¶
func (rs Recoverings) First() *Recovering
Last returns the last recovering.
func (Recoverings) Frequency ¶
func (rs Recoverings) Frequency(num int, dur time.Duration) bool
Frequency checks if a given number of restarts happened during a given duration.
func (Recoverings) Last ¶
func (rs Recoverings) Last() *Recovering
Last returns the last recovering.
func (Recoverings) Trim ¶
func (rs Recoverings) Trim(l int) Recoverings
Trim returns the last recoverings defined by l. This way the recover func can con control the length and take care that the list not grows too much.
type Sentinel ¶
type Sentinel interface { Observable // Observe tells the sentinel to monitor the passed observables. Observe(o ...Observable) // Forget tells the sentinel to forget the passed observables. Forget(o ...Observable) // ObservablesDo executes the passed function for each observable, // e.g. to react after an error. ObservablesDo(f func(o Observable) error) error }
Sentinel manages a number of loops or other sentinels.
Example ¶
ExampleSentinel demonstrates the monitoring of loops and sentinel with a handler function trying to restart the faulty observable. The nested sentinel has no handler function. An error of a monitored observable would lead to the stop of all observables.
package main import ( "errors" "time" "github.com/tideland/golib/loop" ) func main() { loopF := func(l loop.Loop) error { for { select { case <-l.ShallStop(): return nil } } } handleF := func(s loop.Sentinel, o loop.Observable, rs loop.Recoverings) (loop.Recoverings, error) { if rs.Frequency(5, 10*time.Millisecond) { return nil, errors.New("too high error frequency") } return nil, o.Restart() } loopA := loop.Go(loopF, "loop", "a") loopB := loop.Go(loopF, "loop", "b") loopC := loop.Go(loopF, "loop", "c") loopD := loop.Go(loopF, "loop", "d") sentinel := loop.GoNotifiedSentinel(handleF, "sentinel demo") sentinel.Observe(loopA, loopB) // Hierarchies are possible. observedSentinel := loop.GoSentinel("nested sentinel w/o handler") sentinel.Observe(observedSentinel) observedSentinel.Observe(loopC) observedSentinel.Observe(loopD) }
Output:
func GoNotifiedSentinel ¶
func GoNotifiedSentinel(nhf NotificationHandlerFunc, dps ...interface{}) Sentinel
GoNotifiedSentinel starts a new sentinel with a notification handler function. It can manage loops and other sentinels and restart them in case of errors, based on the notification handler function.
func GoSentinel ¶
func GoSentinel(dps ...interface{}) Sentinel
GoSentinel starts a new sentinel. It can manage loops and other sentinels and will stop them in case of errors.