bosun.org@v0.0.0-20210513094433-e25bc3e69a1f/cmd/bosun/database/silence_data.go (about) 1 package database 2 3 import ( 4 "encoding/json" 5 "time" 6 7 "bosun.org/models" 8 "bosun.org/slog" 9 "github.com/garyburd/redigo/redis" 10 ) 11 12 /* 13 14 Silences : hash of Id - json of silence. Id is sha of fields 15 16 SilencesByEnd : zlist of end-time to id. 17 18 Easy to find active. Find all with end time in future, and filter to those with start time in the past. 19 20 */ 21 22 const ( 23 silenceHash = "Silences" 24 silenceIdx = "SilencesByEnd" 25 ) 26 27 type SilenceDataAccess interface { 28 GetActiveSilences() ([]*models.Silence, error) 29 AddSilence(*models.Silence) error 30 DeleteSilence(id string) error 31 32 ListSilences(endingAfter int64) (map[string]*models.Silence, error) 33 } 34 35 func (d *dataAccess) Silence() SilenceDataAccess { 36 return d 37 } 38 39 func (d *dataAccess) GetActiveSilences() ([]*models.Silence, error) { 40 conn := d.Get() 41 defer conn.Close() 42 43 now := time.Now().UTC() 44 vals, err := redis.Strings(conn.Do("ZRANGEBYSCORE", silenceIdx, now.Unix(), "+inf")) 45 if err != nil { 46 return nil, err 47 } 48 if len(vals) == 0 { 49 return nil, nil 50 } 51 silences, err := d.getSilences(vals, conn) 52 if err != nil { 53 return nil, err 54 } 55 filtered := make([]*models.Silence, 0, len(silences)) 56 for _, s := range silences { 57 if s.Start.After(now) { 58 continue 59 } 60 filtered = append(filtered, s) 61 } 62 return filtered, nil 63 } 64 65 func (d *dataAccess) getSilences(ids []string, conn redis.Conn) ([]*models.Silence, error) { 66 args := make([]interface{}, len(ids)+1) 67 args[0] = silenceHash 68 for i := range ids { 69 args[i+1] = ids[i] 70 } 71 jsons, err := redis.Strings(conn.Do("HMGET", args...)) 72 if err != nil { 73 slog.Error(err, args) 74 return nil, err 75 } 76 silences := make([]*models.Silence, 0, len(jsons)) 77 for idx, j := range jsons { 78 s := &models.Silence{} 79 if err := json.Unmarshal([]byte(j), s); err != nil { 80 slog.Errorf("Incorrect silence data for %s. We are going to delete this silence rule", ids[idx]) 81 deleteErr := d.DeleteSilence(ids[idx]) 82 if deleteErr != nil { 83 slog.Errorf("Error while delete silence %s: %s", ids[idx], deleteErr.Error()) 84 return nil, err 85 } 86 } 87 silences = append(silences, s) 88 } 89 return silences, nil 90 } 91 92 func (d *dataAccess) AddSilence(s *models.Silence) error { 93 conn := d.Get() 94 defer conn.Close() 95 96 if _, err := conn.Do("ZADD", silenceIdx, s.End.UTC().Unix(), s.ID()); err != nil { 97 return err 98 } 99 dat, err := json.Marshal(s) 100 if err != nil { 101 return err 102 } 103 _, err = conn.Do("HSET", silenceHash, s.ID(), dat) 104 return err 105 } 106 107 func (d *dataAccess) DeleteSilence(id string) error { 108 conn := d.Get() 109 defer conn.Close() 110 111 if _, err := conn.Do("ZREM", silenceIdx, id); err != nil { 112 return err 113 } 114 if _, err := conn.Do("HDEL", silenceHash, id); err != nil { 115 return err 116 } 117 return nil 118 } 119 120 func (d *dataAccess) ListSilences(endingAfter int64) (map[string]*models.Silence, error) { 121 conn := d.Get() 122 defer conn.Close() 123 124 ids, err := redis.Strings(conn.Do("ZRANGEBYSCORE", silenceIdx, endingAfter, "+inf")) 125 if err != nil { 126 return nil, err 127 } 128 if len(ids) == 0 { 129 return map[string]*models.Silence{}, nil 130 } 131 silences, err := d.getSilences(ids, conn) 132 if err != nil { 133 return nil, err 134 } 135 m := make(map[string]*models.Silence, len(silences)) 136 for _, s := range silences { 137 m[s.ID()] = s 138 } 139 return m, nil 140 }