github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/pkg/storage/storage_delete.go (about) 1 package storage 2 3 import ( 4 "context" 5 "fmt" 6 7 "github.com/pyroscope-io/pyroscope/pkg/storage/dimension" 8 "github.com/pyroscope-io/pyroscope/pkg/storage/segment" 9 ) 10 11 type DeleteInput struct { 12 // Key must match exactly one segment. 13 Key *segment.Key 14 } 15 16 func (s *Storage) Delete(_ context.Context, di *DeleteInput) error { 17 return s.deleteSegmentAndRelatedData(di.Key) 18 } 19 20 func (s *Storage) deleteSegmentAndRelatedData(k *segment.Key) error { 21 sk := k.SegmentKey() 22 if err := s.trees.DiscardPrefix(sk); err != nil { 23 return err 24 } 25 for key, value := range k.Labels() { 26 d, ok := s.lookupDimensionKV(key, value) 27 if !ok { 28 continue 29 } 30 d.Delete(dimension.Key(sk)) 31 32 if len(d.Keys) > 0 { 33 continue 34 } 35 // There are no more references. 36 if err := s.labels.Delete(key, value); err != nil { 37 return err 38 } 39 if key == "__name__" { 40 if err := s.dicts.Delete(k.DictKey()); err != nil { 41 return err 42 } 43 } 44 } 45 return s.segments.Delete(sk) 46 } 47 48 // DeleteApp fully deletes an app 49 // It does so by deleting Segments, Dictionaries, Trees, Dimensions and Labels 50 // It's an idempotent call, ie. if the app already does not exist, no error is triggered. 51 // TODO cancelation? 52 func (s *Storage) DeleteApp(_ context.Context, appname string) error { 53 /***********************************/ 54 /* V a l i d a t i o n s */ 55 /***********************************/ 56 s.logger.Debugf("deleting app '%s' \n", appname) 57 key, err := segment.ParseKey(appname) 58 if err != nil { 59 return err 60 } 61 62 // the only label expected is __name__ 63 s.logger.Debugf("found %d labels\n", len(key.Labels())) 64 if len(key.Labels()) != 1 { 65 return fmt.Errorf("only app name is supported") 66 } 67 68 s.logger.Debugf("looking for __name__ key\n") 69 nameKey := "__name__" 70 _, ok := key.Labels()[nameKey] 71 if !ok { 72 return fmt.Errorf("could not __name__ key") 73 } 74 75 /*****************************/ 76 /* D e l e t i o n */ 77 /*****************************/ 78 79 // d is the dimension of the application. Its 'Keys' member lists all 80 // the segments of the application. Every segments describes a series – 81 // a unique combination of the label key-value pairs. These segments have 82 // to be removed. 83 // 84 // For example, given the app name "my_application". It's dimension could 85 // be represented as follows (we assume that there were two app instances 86 // with distinct tag sets): 87 // __name__=my_application 88 // my_application{foo=bar,baz=qux} 89 // my_application{foo=bar,baz=wadlo} 90 // 91 // Although as far as we drop the whole application, we could delete this 92 // dimension, we also have to take care of the dimensions affected by the app 93 // segments: 94 // my_application{foo=bar,baz=qux} 95 // my_application{foo=bar,baz=wadlo} 96 // 97 // In the example these dimensions are 'foo:bar', 'baz:qux', 'baz:wadlo'. 98 // We have to remove app segment keys from all the associated dimensions 99 // and, if the dimension is not referenced anymore, remove it alongside 100 // which the label KV pair itself: 101 // foo=bar 102 // my_application{foo=bar,baz=qux} 103 // my_application{foo=bar,baz=wadlo} 104 // baz=qux 105 // my_application{foo=bar,baz=qux} 106 // baz=wadlo 107 // my_application{foo=bar,baz=wadlo} 108 // 109 // As you can see, these dimensions (and labels) are to be removed, simply 110 // because there are no more segments/apps referencing the dimension. 111 // But let's say we have one more app: 112 // __name__=my_another_application 113 // my_another_application{foo=bar} 114 // 115 // Thus, our 'foo=bar' dimension would look differently: 116 // foo=bar 117 // my_application{foo=bar,baz=qux} 118 // my_application{foo=bar,baz=wadlo} 119 // my_another_application{foo=bar} 120 // 121 // In this case, 'foo=bar' dimension (and the corresponding label KV pair) 122 // must not be removed when we delete 'my_application' but should only 123 // include 'my_another_application': 124 // foo=bar 125 // my_another_application{foo=bar} 126 s.logger.Debugf("looking for app dimension '%s'\n", appname) 127 d, ok := s.lookupAppDimension(appname) 128 if !ok { 129 // Technically this does not necessarily mean the dimension does not exist 130 // Since this could be triggered by an error 131 s.logger.Debugf("dimensions could not be found, exiting early") 132 return nil 133 } 134 135 s.logger.Debugf("iterating over dimension keys (aka segments keys)\n") 136 for _, segmentKey := range d.Keys { 137 sk2, err := segment.ParseKey(string(segmentKey)) 138 if err != nil { 139 return err 140 } 141 142 s.logger.Debugf("iterating over segment %s labels\n", segmentKey) 143 for labelKey, labelValue := range sk2.Labels() { 144 // skip __name__, since this is the dimension we are already iterating 145 if labelKey == "__name__" { 146 continue 147 } 148 149 s.logger.Debugf("looking up dimension with key='%s' and value='%s'\n", labelKey, labelValue) 150 d2, ok := s.lookupDimensionKV(labelKey, labelValue) 151 if !ok { 152 s.logger.Debugf("skipping since dimension could not be found\n") 153 continue 154 } 155 156 s.logger.Debugf("deleting dimension with key %s\n", segmentKey) 157 d2.Delete(dimension.Key(segmentKey)) 158 159 // We can only delete the dimension once it's not pointing to any segments 160 if len(d2.Keys) > 0 { 161 s.logger.Debugf("dimension is still pointing to valid segments. not deleting it. \n") 162 continue 163 } 164 165 s.logger.Debugf("deleting labels %s=%s \n", labelKey, labelValue) 166 if err := s.labels.Delete(labelKey, labelValue); err != nil { 167 return err 168 } 169 170 s.logger.Debugf("deleting dimension %s=%s \n", labelKey, labelValue) 171 if err := s.dimensions.Delete(labelKey + ":" + labelValue); err != nil { 172 return err 173 } 174 } 175 } 176 177 appWithCurlyBrackets := appname + "{" 178 179 s.logger.Debugf("deleting trees with prefix %s\n", appWithCurlyBrackets) 180 if err = s.trees.DiscardPrefix(appWithCurlyBrackets); err != nil { 181 return err 182 } 183 184 s.logger.Debugf("deleting segments with prefix %s\n", appWithCurlyBrackets) 185 if err = s.segments.DiscardPrefix(appWithCurlyBrackets); err != nil { 186 return err 187 } 188 189 s.logger.Debugf("deleting dicts %s\n", key.DictKey()) 190 if err := s.dicts.Delete(key.DictKey()); err != nil { 191 return err 192 } 193 194 s.logger.Debugf("deleting labels\n") 195 if err := s.labels.Delete("__name__", appname); err != nil { 196 return err 197 } 198 199 s.logger.Debugf("deleting dimensions for __name__=%s\n", appname) 200 return s.dimensions.Delete("__name__:" + appname) 201 }