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  }