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 }