Last active
June 23, 2022 16:20
-
-
Save soyart/a703f8236c7cae7fc2fcc8ba391c399a to your computer and use it in GitHub Desktop.
Code for practicing alerting
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package main | |
| import ( | |
| "crypto/rand" | |
| "fmt" | |
| "math/big" | |
| "strings" | |
| "time" | |
| ) | |
| type alert struct { | |
| Level int `json:"level"` // Severity level | |
| SameLevel bool `json:"SameLevel"` // If the alert severity level is unchanged from the previous alert | |
| Text string `json:"text"` // Severity text | |
| Interval int `json:"interval"` // Alert interval | |
| Timer <-chan time.Time `json:"timer"` // Timer to trigger actual alerts | |
| Balance *big.Int `json:"balance"` | |
| } | |
| func main() { | |
| // loop and loopMock creates alerts and decides their severity | |
| // loop() | |
| loopMock() | |
| } | |
| // handleAlert decides when and how to alert | |
| func handleAlert( | |
| i int, prevTimer <-chan time.Time, prevQuit chan struct{}, prevAlert alert, mostRecent alert, | |
| ) ( | |
| retTimer <-chan time.Time, retQuit chan struct{}, retAlert alert, | |
| ) { | |
| alertFunc, thisTimer, thisQuit := newAlertFunc(i, mostRecent.Interval) | |
| retAlert = mostRecent | |
| retQuit = thisQuit | |
| var shouldAlertNow bool | |
| if i == 0 { | |
| // [nil a'] ... (1st alert must always alert now | |
| shouldAlertNow = true | |
| // No one needs 1st run's timer and quit channels | |
| mostRecent.Timer = nil | |
| retTimer = thisTimer | |
| retQuit = nil | |
| } else if mostRecent.SameLevel && prevAlert.SameLevel { | |
| // a a a a [a a'] ... -> wait to alert later on prevTimer | |
| mostRecent.Timer = prevTimer | |
| retTimer = prevTimer | |
| } else if mostRecent.SameLevel && !prevAlert.SameLevel { | |
| // a a a a [b b'] ... -> wait to alert later on thisTimer | |
| mostRecent.Timer = prevTimer | |
| retTimer = prevTimer | |
| } else if !mostRecent.SameLevel && !prevAlert.SameLevel { | |
| // a a a a [b a'] -> alert now! | |
| shouldAlertNow = true | |
| mostRecent.Timer = nil | |
| retTimer = thisTimer | |
| } else if !mostRecent.SameLevel { | |
| // a a a a [b a'] ... -> alert now! | |
| shouldAlertNow = true | |
| mostRecent.Timer = nil | |
| retTimer = thisTimer | |
| } | |
| go alertFunc(mostRecent, shouldAlertNow) | |
| if prevQuit != nil { | |
| close(prevQuit) | |
| } | |
| return retTimer, retQuit, retAlert | |
| } | |
| // newAlertFunc returns a function closed with quit channel. | |
| // This returned function is used to actually alert (print) | |
| func newAlertFunc(funcId int, interval int) (func(alert, bool), <-chan time.Time, chan struct{}) { | |
| // quit will be captured into the returned closure | |
| quit := make(chan struct{}) | |
| return func(mostRecent alert, alertNow bool) { | |
| printAlert := func(s string) { | |
| fmt.Printf( | |
| "\n[LoopSendAlerts %d]\tALERT (%s) %+v,\t%v\n\n", | |
| funcId, s, mostRecent, strings.Split(time.Now().String(), " ")[1], | |
| ) | |
| } | |
| if alertNow { | |
| printAlert("alertNow") | |
| return | |
| } | |
| // Sends an alert and waits | |
| // select clause blocks here until it breaks | |
| select { | |
| case <-quit: | |
| fmt.Printf("[LoopSendAlerts %d] baling out (%s again): recevied QUIT signal,\n", funcId, mostRecent.Text) | |
| break | |
| // Newer alerts has come | |
| case <-mostRecent.Timer: | |
| printAlert("onTimer") | |
| break | |
| } | |
| }, time.After(time.Second * time.Duration(interval)), quit | |
| } | |
| func loop() { | |
| var c int // counts instance | |
| var prevTimer <-chan time.Time | |
| var prevQuit chan struct{} // nil now or if closed | |
| var prevAlert alert | |
| for { | |
| c++ | |
| if c >= 50 { | |
| break | |
| } | |
| balance, err := rand.Int(rand.Reader, big.NewInt(150)) | |
| if err != nil { | |
| panic(err.Error()) | |
| } | |
| var mostRecent alert | |
| if cmp := balance.Cmp(tresholdx2); cmp > 0 { | |
| mostRecent = l1 | |
| } else if cmp := balance.Cmp(treshold); cmp > 0 { | |
| mostRecent = l2 | |
| } else if cmp <= 0 { | |
| if balance.Cmp(big.NewInt(0)) == 0 { | |
| // catastrophe | |
| mostRecent = l4 | |
| } else { | |
| // critical | |
| mostRecent = l3 | |
| } | |
| } | |
| mostRecent.Balance = balance | |
| if mostRecent.Level == prevAlert.Level { | |
| mostRecent.SameLevel = true | |
| } | |
| fmt.Printf( | |
| "[LoopGetBalances %d]\tseverity: %s level: %d balance: %v interval: %d\n", | |
| c, mostRecent.Text, mostRecent.Level, mostRecent.Balance, mostRecent.Interval, | |
| ) | |
| prevTimer, prevQuit, prevAlert = handleAlert(c, prevTimer, prevQuit, prevAlert, mostRecent) | |
| time.Sleep(time.Millisecond * time.Duration(fetchInterval)) | |
| } | |
| } | |
| func loopMock() { | |
| var prevTimer <-chan time.Time | |
| var prevQuit chan struct{} // nil now or if closed | |
| var prevAlert alert | |
| for i, mostRecent := range mockAlerts { | |
| if mostRecent.Level == prevAlert.Level { | |
| mostRecent.SameLevel = true | |
| } | |
| fmt.Printf( | |
| "[LoopGetBalances %d]\tseverity: %s level: %d balance: %v interval: %d\n", | |
| i, mostRecent.Text, mostRecent.Level, mostRecent.Balance, mostRecent.Interval, | |
| ) | |
| prevTimer, prevQuit, prevAlert = handleAlert(i, prevTimer, prevQuit, prevAlert, mostRecent) | |
| time.Sleep(time.Millisecond * time.Duration(fetchInterval)) | |
| } | |
| } | |
| var ( | |
| fetchInterval = 1250 | |
| l1 = alert{Level: 1, Interval: 20, Text: "SAFE", Balance: big.NewInt(10)} | |
| l2 = alert{Level: 2, Interval: 15, Text: "WARN", Balance: big.NewInt(20)} | |
| l3 = alert{Level: 3, Interval: 10, Text: "CRITICAL", Balance: big.NewInt(30)} | |
| l4 = alert{Level: 4, Interval: 5, Text: "CATASTROPHE", Balance: big.NewInt(40)} | |
| mockAlerts = []alert{l1, l1, l1, l1, l1, l1, l1, l1, l1, l1, l1, l1, l1, l1, l1, l1, l1, l1, l2, l2, l2, l3, l3, l3, l2, l2, l3, l3, l3, l4, l4, l4, l4} | |
| // mockAlerts = []alert{l4, l4, l4, l4, l4, l4} | |
| treshold = big.NewInt(25) | |
| tresholdx2 = new(big.Int).Mul(treshold, big.NewInt(2)) | |
| ) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment