github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/pkg/storage/storage_delete_test.go (about) 1 //go:build !windows 2 // +build !windows 3 4 package storage 5 6 import ( 7 "context" 8 "time" 9 10 . "github.com/onsi/ginkgo/v2" 11 . "github.com/onsi/gomega" 12 "github.com/prometheus/client_golang/prometheus" 13 "github.com/sirupsen/logrus" 14 15 "github.com/pyroscope-io/pyroscope/pkg/config" 16 "github.com/pyroscope-io/pyroscope/pkg/health" 17 "github.com/pyroscope-io/pyroscope/pkg/storage/dict" 18 "github.com/pyroscope-io/pyroscope/pkg/storage/dimension" 19 "github.com/pyroscope-io/pyroscope/pkg/storage/segment" 20 "github.com/pyroscope-io/pyroscope/pkg/storage/tree" 21 "github.com/pyroscope-io/pyroscope/pkg/testing" 22 ) 23 24 var _ = Describe("storage package", func() { 25 var s *Storage 26 27 testing.WithConfig(func(cfg **config.Config) { 28 JustBeforeEach(func() { 29 var err error 30 s, err = New(NewConfig(&(*cfg).Server), logrus.StandardLogger(), prometheus.NewRegistry(), new(health.Controller), NoopApplicationMetadataService{}) 31 Expect(err).ToNot(HaveOccurred()) 32 }) 33 }) 34 35 Context("delete app", func() { 36 /*************************************/ 37 /* h e l p e r f u n c t i o n s */ 38 /*************************************/ 39 checkSegmentsPresence := func(appname string, presence bool) { 40 segmentKey, err := segment.ParseKey(string(appname)) 41 Expect(err).ToNot(HaveOccurred()) 42 segmentKeyStr := segmentKey.SegmentKey() 43 Expect(segmentKeyStr).To(Equal(appname + "{}")) 44 _, ok := s.segments.Lookup(segmentKeyStr) 45 46 if presence { 47 Expect(ok).To(BeTrue()) 48 } else { 49 Expect(ok).To(BeFalse()) 50 } 51 } 52 53 checkDimensionsPresence := func(appname string, presence bool) interface{} { 54 d, ok := s.lookupAppDimension(appname) 55 if presence { 56 Expect(ok).To(BeTrue()) 57 } else { 58 Expect(ok).To(BeFalse()) 59 } 60 61 return d 62 } 63 64 checkTreesPresence := func(appname string, st time.Time, depth int, presence bool) interface{} { 65 key, err := segment.ParseKey(appname) 66 Expect(err).ToNot(HaveOccurred()) 67 treeKeyName := key.TreeKey(depth, st) 68 t, ok := s.trees.Lookup(treeKeyName) 69 if presence { 70 Expect(ok).To(BeTrue()) 71 } else { 72 Expect(ok).To(BeFalse()) 73 } 74 75 return t 76 } 77 78 checkDictsPresence := func(appname string, presence bool) interface{} { 79 d, ok := s.dicts.Lookup(appname) 80 if presence { 81 Expect(ok).To(BeTrue()) 82 } else { 83 Expect(ok).To(BeFalse()) 84 } 85 return d 86 } 87 88 checkLabelsPresence := func(appname string, presence bool) { 89 // this indirectly calls s.labels 90 appnames := s.GetAppNames(context.TODO()) 91 92 // linear scan should be fast enough here 93 found := false 94 for _, v := range appnames { 95 if v == appname { 96 found = true 97 } 98 } 99 100 if presence { 101 Expect(found).To(BeTrue()) 102 } else { 103 Expect(found).To(BeFalse()) 104 } 105 } 106 107 Context("simple app", func() { 108 It("works correctly", func() { 109 appname := "my.app.cpu" 110 111 // We insert info for an app 112 tree1 := tree.New() 113 tree1.Insert([]byte("a;b"), uint64(1)) 114 115 st := testing.SimpleTime(10) 116 et := testing.SimpleTime(19) 117 key, _ := segment.ParseKey(appname) 118 err := s.Put(context.TODO(), &PutInput{ 119 StartTime: st, 120 EndTime: et, 121 Key: key, 122 Val: tree1, 123 SpyName: "testspy", 124 SampleRate: 100, 125 }) 126 Expect(err).ToNot(HaveOccurred()) 127 128 // Since the DeleteApp also removes dictionaries 129 // therefore we need to create them manually here 130 // (they are normally created when TODO) 131 d := dict.New() 132 s.dicts.Put(appname, d) 133 134 /*******************************/ 135 /* S a n i t y C h e c k s */ 136 /*******************************/ 137 // Dimensions 138 Expect(s.dimensions.CacheSize()).To(Equal(uint64(1))) 139 checkDimensionsPresence(appname, true) 140 141 // Trees 142 Expect(s.trees.CacheSize()).To(Equal(uint64(1))) 143 checkTreesPresence(appname, st, 0, true) 144 145 // Segments 146 Expect(s.segments.CacheSize()).To(Equal(uint64(1))) 147 checkSegmentsPresence(appname, true) 148 149 // Dicts 150 // I manually inserted a dictionary so it should be fine? 151 Expect(s.dicts.CacheSize()).To(Equal(uint64(1))) 152 checkDictsPresence(appname, true) 153 154 // Labels 155 checkLabelsPresence(appname, true) 156 157 /*************************/ 158 /* D e l e t e a p p */ 159 /*************************/ 160 err = s.DeleteApp(context.TODO(), appname) 161 Expect(err).ToNot(HaveOccurred()) 162 163 // Trees 164 // should've been deleted from CACHE 165 Expect(s.trees.CacheSize()).To(Equal(uint64(0))) 166 checkTreesPresence(appname, st, 0, false) 167 168 // Dimensions 169 Expect(s.dimensions.CacheSize()).To(Equal(uint64(0))) 170 checkDimensionsPresence(appname, false) 171 172 // Dicts 173 Expect(s.dicts.CacheSize()).To(Equal(uint64(0))) 174 checkDictsPresence(appname, false) 175 176 // Segments 177 Expect(s.segments.CacheSize()).To(Equal(uint64(0))) 178 checkSegmentsPresence(appname, false) 179 180 // Labels 181 checkLabelsPresence(appname, false) 182 }) 183 }) 184 185 Context("app with labels", func() { 186 It("works correctly", func() { 187 appname := "my.app.cpu" 188 189 // We insert info for an app 190 tree1 := tree.New() 191 tree1.Insert([]byte("a;b"), uint64(1)) 192 193 // We are mirroring this on the simple.golang.cpu example 194 labels := []string{ 195 "", 196 "{foo=bar,function=fast}", 197 "{foo=bar,function=slow}", 198 } 199 200 st := testing.SimpleTime(10) 201 et := testing.SimpleTime(19) 202 for _, l := range labels { 203 key, _ := segment.ParseKey(appname + l) 204 err := s.Put(context.TODO(), &PutInput{ 205 StartTime: st, 206 EndTime: et, 207 Key: key, 208 Val: tree1, 209 SpyName: "testspy", 210 SampleRate: 100, 211 }) 212 Expect(err).ToNot(HaveOccurred()) 213 } 214 215 // Since the DeleteApp also removes dictionaries 216 // therefore we need to create them manually here 217 // (they are normally created when TODO) 218 d := dict.New() 219 s.dicts.Put(appname, d) 220 221 /*******************************/ 222 /* S a n i t y C h e c k s */ 223 /*******************************/ 224 225 By("checking dimensions were created") 226 Expect(s.dimensions.CacheSize()).To(Equal(uint64(4))) 227 checkDimensionsPresence(appname, true) 228 229 By("checking trees were created") 230 Expect(s.trees.CacheSize()).To(Equal(uint64(3))) 231 checkTreesPresence(appname, st, 0, true) 232 233 By("checking segments were created") 234 Expect(s.segments.CacheSize()).To(Equal(uint64(3))) 235 checkSegmentsPresence(appname, true) 236 237 By("checking dicts were created") 238 // Dicts 239 // I manually inserted a dictionary so it should be fine? 240 Expect(s.dicts.CacheSize()).To(Equal(uint64(1))) 241 checkDictsPresence(appname, true) 242 243 // Labels 244 checkLabelsPresence(appname, true) 245 246 /*************************/ 247 /* D e l e t e a p p */ 248 /*************************/ 249 By("deleting the app") 250 err := s.DeleteApp(context.TODO(), appname) 251 Expect(err).ToNot(HaveOccurred()) 252 253 By("checking trees were deleted") 254 // Trees 255 // should've been deleted from CACHE 256 Expect(s.trees.CacheSize()).To(Equal(uint64(0))) 257 checkTreesPresence(appname, st, 0, false) 258 259 // Dimensions 260 By("checking dimensions were deleted") 261 Expect(s.dimensions.CacheSize()).To(Equal(uint64(0))) 262 checkDimensionsPresence(appname, false) 263 264 // Dicts 265 By("checking dicts were deleted") 266 Expect(s.dicts.CacheSize()).To(Equal(uint64(0))) 267 checkDictsPresence(appname, false) 268 269 // Segments 270 By("checking segments were deleted") 271 Expect(s.segments.CacheSize()).To(Equal(uint64(0))) 272 checkSegmentsPresence(appname, false) 273 274 // Labels 275 By("checking labels were deleted") 276 checkLabelsPresence(appname, false) 277 }) 278 }) 279 280 // In this test we have 2 apps with the same label 281 // And deleting one app should not interfer with the labels of the other app 282 Context("multiple apps with labels", func() { 283 It("deletes the correct data", func() { 284 st := testing.SimpleTime(10) 285 insert := func(appname string) { 286 // We insert info for an app 287 tree1 := tree.New() 288 tree1.Insert([]byte("a;b"), uint64(1)) 289 290 // We are mirroring this on the simple.golang.cpu example 291 labels := []string{ 292 "", 293 "{foo=bar,function=fast}", 294 "{foo=bar,function=slow}", 295 } 296 297 et := testing.SimpleTime(19) 298 for _, l := range labels { 299 key, _ := segment.ParseKey(appname + l) 300 err := s.Put(context.TODO(), &PutInput{ 301 StartTime: st, 302 EndTime: et, 303 Key: key, 304 Val: tree1, 305 SpyName: "testspy", 306 SampleRate: 100, 307 }) 308 Expect(err).ToNot(HaveOccurred()) 309 } 310 311 // Since the DeleteApp also removes dictionaries 312 // therefore we need to create them manually here 313 // (they are normally created when TODO) 314 d := dict.New() 315 s.dicts.Put(appname, d) 316 } 317 318 app1name := "myapp1.cpu" 319 app2name := "myapp2.cpu" 320 321 insert(app1name) 322 insert(app2name) 323 324 /*******************************/ 325 /* S a n i t y C h e c k s */ 326 /*******************************/ 327 By("checking dimensions were created") 328 Expect(s.dimensions.CacheSize()).To(Equal(uint64(5))) 329 checkDimensionsPresence(app1name, true) 330 checkDimensionsPresence(app2name, true) 331 332 By("checking trees were created") 333 Expect(s.trees.CacheSize()).To(Equal(uint64(6))) 334 checkTreesPresence(app1name, st, 0, true) 335 checkTreesPresence(app2name, st, 0, true) 336 337 By("checking segments were created") 338 Expect(s.segments.CacheSize()).To(Equal(uint64(6))) 339 checkSegmentsPresence(app1name, true) 340 checkSegmentsPresence(app2name, true) 341 342 By("checking dicts were created") 343 Expect(s.dicts.CacheSize()).To(Equal(uint64(2))) 344 checkDictsPresence(app1name, true) 345 checkDictsPresence(app2name, true) 346 347 By("checking labels were created") 348 checkLabelsPresence(app1name, true) 349 checkLabelsPresence(app2name, true) 350 351 /*************************/ 352 /* D e l e t e a p p */ 353 /*************************/ 354 By("deleting the app") 355 err := s.DeleteApp(context.TODO(), app1name) 356 Expect(err).ToNot(HaveOccurred()) 357 358 By("checking trees were deleted") 359 Expect(s.trees.CacheSize()).To(Equal(uint64(3))) 360 checkTreesPresence(app1name, st, 0, false) 361 checkTreesPresence(app2name, st, 0, true) 362 363 // Dimensions 364 By("checking dimensions were deleted") 365 Expect(s.dimensions.CacheSize()).To(Equal(uint64(4))) 366 367 // Dimensions that refer to app2 are still intact 368 v, ok := s.dimensions.Lookup("__name__:myapp2.cpu") 369 Expect(ok).To(Equal(true)) 370 Expect(v.(*dimension.Dimension).Keys).To(Equal([]dimension.Key{ 371 dimension.Key("myapp2.cpu{foo=bar,function=fast}"), 372 dimension.Key("myapp2.cpu{foo=bar,function=slow}"), 373 dimension.Key("myapp2.cpu{}"), 374 })) 375 376 v, ok = s.dimensions.Lookup("foo:bar") 377 Expect(ok).To(Equal(true)) 378 Expect(v.(*dimension.Dimension).Keys).To(Equal([]dimension.Key{ 379 dimension.Key("myapp2.cpu{foo=bar,function=fast}"), 380 dimension.Key("myapp2.cpu{foo=bar,function=slow}"), 381 })) 382 383 v, ok = s.dimensions.Lookup("function:fast") 384 Expect(ok).To(Equal(true)) 385 Expect(v.(*dimension.Dimension).Keys).To(Equal([]dimension.Key{ 386 dimension.Key("myapp2.cpu{foo=bar,function=fast}"), 387 })) 388 389 v, ok = s.dimensions.Lookup("function:slow") 390 Expect(ok).To(Equal(true)) 391 Expect(v.(*dimension.Dimension).Keys).To(Equal([]dimension.Key{ 392 dimension.Key("myapp2.cpu{foo=bar,function=slow}"), 393 })) 394 395 By("checking dicts were deleted") 396 Expect(s.dicts.CacheSize()).To(Equal(uint64(1))) 397 checkDictsPresence(app1name, false) 398 checkDictsPresence(app2name, true) 399 400 By("checking segments were deleted") 401 Expect(s.segments.CacheSize()).To(Equal(uint64(3))) 402 checkSegmentsPresence(app1name, false) 403 checkSegmentsPresence(app2name, true) 404 405 By("checking labels were deleted") 406 checkLabelsPresence(app1name, false) 407 checkSegmentsPresence(app2name, true) 408 }) 409 }) 410 411 // Delete an unrelated app 412 // It should not fail 413 It("is idempotent", func() { 414 appname := "my.app.cpu" 415 416 // We insert info for an app 417 tree1 := tree.New() 418 tree1.Insert([]byte("a;b"), uint64(1)) 419 420 // We are mirroring this on the simple.golang.cpu example 421 labels := []string{ 422 "", 423 "{foo=bar,function=fast}", 424 "{foo=bar,function=slow}", 425 } 426 427 st := testing.SimpleTime(10) 428 et := testing.SimpleTime(19) 429 for _, l := range labels { 430 key, _ := segment.ParseKey(appname + l) 431 err := s.Put(context.TODO(), &PutInput{ 432 StartTime: st, 433 EndTime: et, 434 Key: key, 435 Val: tree1, 436 SpyName: "testspy", 437 SampleRate: 100, 438 }) 439 Expect(err).ToNot(HaveOccurred()) 440 } 441 442 // Since the DeleteApp also removes dictionaries 443 // therefore we need to create them manually here 444 // (they are normally created when TODO) 445 d := dict.New() 446 s.dicts.Put(appname, d) 447 448 /*******************************/ 449 /* S a n i t y C h e c k s */ 450 /*******************************/ 451 sanityChecks := func() { 452 By("checking dimensions were created") 453 Expect(s.dimensions.CacheSize()).To(Equal(uint64(4))) 454 checkDimensionsPresence(appname, true) 455 456 By("checking trees were created") 457 Expect(s.trees.CacheSize()).To(Equal(uint64(3))) 458 checkTreesPresence(appname, st, 0, true) 459 460 By("checking segments were created") 461 Expect(s.segments.CacheSize()).To(Equal(uint64(3))) 462 checkSegmentsPresence(appname, true) 463 464 By("checking dicts were created") 465 Expect(s.dicts.CacheSize()).To(Equal(uint64(1))) 466 checkDictsPresence(appname, true) 467 468 checkLabelsPresence(appname, true) 469 } 470 471 sanityChecks() 472 473 /*************************/ 474 /* D e l e t e a p p */ 475 /*************************/ 476 By("deleting the app") 477 err := s.DeleteApp(context.TODO(), "random.app") 478 Expect(err).ToNot(HaveOccurred()) 479 480 // nothing should have happened 481 // since the deleted app does not exist 482 sanityChecks() 483 }) 484 }) 485 })