bosun.org@v0.0.0-20210513094433-e25bc3e69a1f/cmd/bosun/database/tag_metadata.go (about)

     1  package database
     2  
     3  import (
     4  	"fmt"
     5  	"strconv"
     6  	"strings"
     7  	"time"
     8  
     9  	"bosun.org/opentsdb"
    10  	"github.com/garyburd/redigo/redis"
    11  )
    12  
    13  /*
    14  	Tag metadata gets stored in various ways:
    15  
    16  	Metadata itself gets stored as a simple key (tmeta:tags:name) -> "timestamp:value".
    17  
    18  	To facilitate subset lookups, there will be index sets for each possible subset of inserted tags.
    19  	tmeta:idx:{subset} -> set of tmeta keys
    20  */
    21  
    22  func tagMetaKey(tags opentsdb.TagSet, name string) string {
    23  	return fmt.Sprintf("tmeta:%s:%s", tags.Tags(), name)
    24  }
    25  
    26  func tagMetaIdxKey(tagK, tagV string) string {
    27  	return fmt.Sprintf("tmeta:idx:%s=%s", tagK, tagV)
    28  }
    29  
    30  func (d *dataAccess) PutTagMetadata(tags opentsdb.TagSet, name string, value string, updated time.Time) error {
    31  	conn := d.Get()
    32  	defer conn.Close()
    33  	key := tagMetaKey(tags, name)
    34  	keyValue := fmt.Sprintf("%d:%s", updated.UTC().Unix(), value)
    35  	_, err := conn.Do("SET", key, keyValue)
    36  	if err != nil {
    37  		return err
    38  	}
    39  	for tagK, tagV := range tags {
    40  		_, err := conn.Do("SADD", tagMetaIdxKey(tagK, tagV), key)
    41  		if err != nil {
    42  			return err
    43  		}
    44  	}
    45  	return nil
    46  }
    47  
    48  func (d *dataAccess) DeleteTagMetadata(tags opentsdb.TagSet, name string) error {
    49  	conn := d.Get()
    50  	defer conn.Close()
    51  	key := tagMetaKey(tags, name)
    52  	_, err := conn.Do("DEL", key)
    53  	if err != nil {
    54  		return err
    55  	}
    56  	for tagK, tagV := range tags {
    57  		_, err := conn.Do("SREM", tagMetaIdxKey(tagK, tagV), key)
    58  		if err != nil {
    59  			return err
    60  		}
    61  	}
    62  	return nil
    63  }
    64  
    65  func (d *dataAccess) GetTagMetadata(tags opentsdb.TagSet, name string) ([]*TagMetadata, error) {
    66  	conn := d.Get()
    67  	defer conn.Close()
    68  	args := []interface{}{}
    69  	for tagK, tagV := range tags {
    70  		args = append(args, tagMetaIdxKey(tagK, tagV))
    71  	}
    72  	keys, err := redis.Strings(conn.Do("SINTER", args...))
    73  	if err != nil {
    74  		return nil, err
    75  	}
    76  	args = []interface{}{}
    77  	for _, key := range keys {
    78  		if name == "" || strings.HasSuffix(key, ":"+name) {
    79  			args = append(args, key)
    80  		}
    81  	}
    82  	results, err := redis.Strings(conn.Do("MGET", args...))
    83  	data := []*TagMetadata{}
    84  	for i := range args {
    85  		// break up key to get tags and name
    86  		key := args[i].(string)[len("tmeta:"):]
    87  		sepIdx := strings.LastIndex(key, ":")
    88  		tags := key[:sepIdx]
    89  		name := key[sepIdx+1:]
    90  		tagSet, err := opentsdb.ParseTags(tags)
    91  		if err != nil {
    92  			return nil, err
    93  		}
    94  		// break up response to get time and value
    95  		parts := strings.SplitN(results[i], ":", 2)
    96  		if len(parts) != 2 {
    97  			return nil, fmt.Errorf("Expect metadata value to be `time:value`")
    98  		}
    99  		val := parts[1]
   100  		time, err := strconv.ParseInt(parts[0], 10, 64)
   101  		if err != nil {
   102  			return nil, err
   103  		}
   104  		obj := &TagMetadata{
   105  			Tags:        tagSet,
   106  			Name:        name,
   107  			Value:       val,
   108  			LastTouched: time,
   109  		}
   110  		data = append(data, obj)
   111  	}
   112  	return data, nil
   113  }