github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/pkg/storage/storage_test.go (about)

     1  //go:build !windows
     2  // +build !windows
     3  
     4  package storage
     5  
     6  import (
     7  	"context"
     8  	"runtime"
     9  	"strconv"
    10  	"time"
    11  
    12  	. "github.com/onsi/ginkgo/v2"
    13  	. "github.com/onsi/gomega"
    14  	"github.com/prometheus/client_golang/prometheus"
    15  	"github.com/shirou/gopsutil/mem"
    16  	"github.com/sirupsen/logrus"
    17  
    18  	"github.com/pyroscope-io/pyroscope/pkg/config"
    19  	"github.com/pyroscope-io/pyroscope/pkg/flameql"
    20  	"github.com/pyroscope-io/pyroscope/pkg/health"
    21  	"github.com/pyroscope-io/pyroscope/pkg/storage/dimension"
    22  	"github.com/pyroscope-io/pyroscope/pkg/storage/segment"
    23  	"github.com/pyroscope-io/pyroscope/pkg/storage/tree"
    24  	"github.com/pyroscope-io/pyroscope/pkg/testing"
    25  )
    26  
    27  // 21:22:08      air |  (time.Duration) 16m40s,
    28  // 21:22:08      air |  (time.Duration) 2h46m40s,
    29  // 21:22:08      air |  (time.Duration) 27h46m40s,
    30  // 21:22:08      air |  (time.Duration) 277h46m40s,
    31  // 21:22:08      air |  (time.Duration) 2777h46m40s,
    32  // 21:22:08      air |  (time.Duration) 27777h46m40s
    33  
    34  var s *Storage
    35  
    36  var maxTime = time.Unix(1<<62, 999999999)
    37  
    38  var _ = Describe("storage package", func() {
    39  	suite := func() {
    40  		Context("delete tests", func() {
    41  			Context("simple delete", func() {
    42  				It("works correctly", func() {
    43  					tree := tree.New()
    44  					tree.Insert([]byte("a;b"), uint64(1))
    45  					tree.Insert([]byte("a;c"), uint64(2))
    46  					st := testing.SimpleTime(10)
    47  					et := testing.SimpleTime(19)
    48  					st2 := testing.SimpleTime(0)
    49  					et2 := testing.SimpleTime(30)
    50  					key, _ := segment.ParseKey("foo")
    51  
    52  					s.Put(context.TODO(), &PutInput{
    53  						StartTime:  st,
    54  						EndTime:    et,
    55  						Key:        key,
    56  						Val:        tree,
    57  						SpyName:    "testspy",
    58  						SampleRate: 100,
    59  					})
    60  
    61  					Expect(s.Delete(context.TODO(), &DeleteInput{key})).ToNot(HaveOccurred())
    62  					gOut, err := s.Get(context.TODO(), &GetInput{
    63  						StartTime: st2,
    64  						EndTime:   et2,
    65  						Key:       key,
    66  					})
    67  
    68  					Expect(err).ToNot(HaveOccurred())
    69  					Expect(gOut).To(BeNil())
    70  					Expect(s.Close()).ToNot(HaveOccurred())
    71  				})
    72  			})
    73  			Context("delete all trees", func() {
    74  				It("works correctly", func() {
    75  					tree1 := tree.New()
    76  					tree1.Insert([]byte("a;b"), uint64(1))
    77  					tree1.Insert([]byte("a;c"), uint64(2))
    78  					tree2 := tree.New()
    79  					tree2.Insert([]byte("c;d"), uint64(1))
    80  					tree2.Insert([]byte("e;f"), uint64(2))
    81  					st := testing.SimpleTime(10)
    82  					et := testing.SimpleTime(19)
    83  					st2 := testing.SimpleTime(0)
    84  					et2 := testing.SimpleTime(30)
    85  					key, _ := segment.ParseKey("foo")
    86  
    87  					s.Put(context.TODO(), &PutInput{
    88  						StartTime:  st,
    89  						EndTime:    et,
    90  						Key:        key,
    91  						Val:        tree1,
    92  						SpyName:    "testspy",
    93  						SampleRate: 100,
    94  					})
    95  
    96  					s.Put(context.TODO(), &PutInput{
    97  						StartTime:  st,
    98  						EndTime:    et,
    99  						Key:        key,
   100  						Val:        tree2,
   101  						SpyName:    "testspy",
   102  						SampleRate: 100,
   103  					})
   104  
   105  					Expect(s.Delete(context.TODO(), &DeleteInput{key})).ToNot(HaveOccurred())
   106  					s.GetValues(context.TODO(), "__name__", func(v string) bool {
   107  						Fail("app name label was not removed")
   108  						return false
   109  					})
   110  
   111  					gOut, err := s.Get(context.TODO(), &GetInput{
   112  						StartTime: st2,
   113  						EndTime:   et2,
   114  						Key:       key,
   115  					})
   116  
   117  					Expect(err).ToNot(HaveOccurred())
   118  					Expect(gOut).To(BeNil())
   119  					Expect(s.Close()).ToNot(HaveOccurred())
   120  				})
   121  			})
   122  			Context("put after delete", func() {
   123  				It("works correctly", func() {
   124  					tree1 := tree.New()
   125  					tree1.Insert([]byte("a;b"), uint64(1))
   126  					tree1.Insert([]byte("a;c"), uint64(2))
   127  					tree2 := tree.New()
   128  					tree2.Insert([]byte("c;d"), uint64(1))
   129  					tree2.Insert([]byte("e;f"), uint64(2))
   130  					st := testing.SimpleTime(10)
   131  					et := testing.SimpleTime(19)
   132  					st2 := testing.SimpleTime(0)
   133  					et2 := testing.SimpleTime(30)
   134  					key, _ := segment.ParseKey("foo")
   135  
   136  					err := s.Put(context.TODO(), &PutInput{
   137  						StartTime:  st,
   138  						EndTime:    et,
   139  						Key:        key,
   140  						Val:        tree1,
   141  						SpyName:    "testspy",
   142  						SampleRate: 100,
   143  					})
   144  					Expect(err).ToNot(HaveOccurred())
   145  
   146  					Expect(s.Delete(context.TODO(), &DeleteInput{key})).ToNot(HaveOccurred())
   147  					s.Put(context.TODO(), &PutInput{
   148  						StartTime:  st,
   149  						EndTime:    et,
   150  						Key:        key,
   151  						Val:        tree2,
   152  						SpyName:    "testspy",
   153  						SampleRate: 100,
   154  					})
   155  
   156  					gOut, err := s.Get(context.TODO(), &GetInput{
   157  						StartTime: st2,
   158  						EndTime:   et2,
   159  						Key:       key,
   160  					})
   161  
   162  					Expect(err).ToNot(HaveOccurred())
   163  					Expect(gOut.Tree).ToNot(BeNil())
   164  					Expect(gOut.Tree.String()).To(Equal(tree2.String()))
   165  					Expect(s.Close()).ToNot(HaveOccurred())
   166  				})
   167  			})
   168  		})
   169  
   170  		Context("smoke tests", func() {
   171  			Context("check segment cache", func() {
   172  				It("works correctly", func() {
   173  					tree := tree.New()
   174  
   175  					size := 32
   176  					treeKey := make([]byte, size)
   177  					for i := 0; i < size; i++ {
   178  						treeKey[i] = 'a'
   179  					}
   180  					for i := 0; i < 60; i++ {
   181  						k := string(treeKey) + strconv.Itoa(i+1)
   182  						tree.Insert([]byte(k), uint64(i+1))
   183  
   184  						key, _ := segment.ParseKey("tree_key" + strconv.Itoa(i+1))
   185  						err := s.Put(context.TODO(), &PutInput{
   186  							Key:        key,
   187  							Val:        tree,
   188  							SpyName:    "testspy",
   189  							SampleRate: 100,
   190  						})
   191  						Expect(err).ToNot(HaveOccurred())
   192  					}
   193  					Expect(s.Close()).ToNot(HaveOccurred())
   194  				})
   195  			})
   196  			Context("simple 10 second write", func() {
   197  				It("works correctly", func() {
   198  					tree := tree.New()
   199  					tree.Insert([]byte("a;b"), uint64(1))
   200  					tree.Insert([]byte("a;c"), uint64(2))
   201  					st := testing.SimpleTime(10)
   202  					et := testing.SimpleTime(19)
   203  					st2 := testing.SimpleTime(0)
   204  					et2 := testing.SimpleTime(30)
   205  					key, _ := segment.ParseKey("foo")
   206  
   207  					err := s.Put(context.TODO(), &PutInput{
   208  						StartTime:  st,
   209  						EndTime:    et,
   210  						Key:        key,
   211  						Val:        tree,
   212  						SpyName:    "testspy",
   213  						SampleRate: 100,
   214  					})
   215  					Expect(err).ToNot(HaveOccurred())
   216  
   217  					o, err := s.Get(context.TODO(), &GetInput{
   218  						StartTime: st2,
   219  						EndTime:   et2,
   220  						Key:       key,
   221  					})
   222  
   223  					Expect(err).ToNot(HaveOccurred())
   224  					Expect(o.Tree).ToNot(BeNil())
   225  					Expect(o.Tree.String()).To(Equal(tree.String()))
   226  					Expect(s.Close()).ToNot(HaveOccurred())
   227  				})
   228  			})
   229  			Context("simple 20 second write", func() {
   230  				It("works correctly", func() {
   231  					tree := tree.New()
   232  					tree.Insert([]byte("a;b"), uint64(2))
   233  					tree.Insert([]byte("a;c"), uint64(4))
   234  					st := testing.SimpleTime(10)
   235  					et := testing.SimpleTime(29)
   236  					st2 := testing.SimpleTime(0)
   237  					et2 := testing.SimpleTime(30)
   238  					key, _ := segment.ParseKey("foo")
   239  
   240  					err := s.Put(context.TODO(), &PutInput{
   241  						StartTime:  st,
   242  						EndTime:    et,
   243  						Key:        key,
   244  						Val:        tree,
   245  						SpyName:    "testspy",
   246  						SampleRate: 100,
   247  					})
   248  					Expect(err).ToNot(HaveOccurred())
   249  
   250  					o, err := s.Get(context.TODO(), &GetInput{
   251  						StartTime: st2,
   252  						EndTime:   et2,
   253  						Key:       key,
   254  					})
   255  
   256  					Expect(err).ToNot(HaveOccurred())
   257  					Expect(o.Tree).ToNot(BeNil())
   258  					Expect(o.Tree.String()).To(Equal(tree.String()))
   259  					Expect(s.Close()).ToNot(HaveOccurred())
   260  				})
   261  			})
   262  			Context("evict cache items periodically", func() {
   263  				It("works correctly", func() {
   264  					tree := tree.New()
   265  
   266  					size := 16
   267  					treeKey := make([]byte, size)
   268  					for i := 0; i < size; i++ {
   269  						treeKey[i] = 'a'
   270  					}
   271  					for i := 0; i < 200; i++ {
   272  						k := string(treeKey) + strconv.Itoa(i+1)
   273  						tree.Insert([]byte(k), uint64(i+1))
   274  
   275  						key, _ := segment.ParseKey("tree_key" + strconv.Itoa(i+1))
   276  						err := s.Put(context.TODO(), &PutInput{
   277  							Key:        key,
   278  							Val:        tree,
   279  							SpyName:    "testspy",
   280  							SampleRate: 100,
   281  						})
   282  						Expect(err).ToNot(HaveOccurred())
   283  					}
   284  
   285  					for i := 0; i < 5; i++ {
   286  						_, err := mem.VirtualMemory()
   287  						Expect(err).ToNot(HaveOccurred())
   288  
   289  						var m runtime.MemStats
   290  						runtime.ReadMemStats(&m)
   291  						time.Sleep(time.Second)
   292  					}
   293  					Expect(s.Close()).ToNot(HaveOccurred())
   294  				})
   295  			})
   296  		})
   297  	}
   298  
   299  	logrus.SetLevel(logrus.InfoLevel)
   300  
   301  	// Disk-based storage
   302  	testing.WithConfig(func(cfg **config.Config) {
   303  		JustBeforeEach(func() {
   304  			var err error
   305  			s, err = New(NewConfig(&(*cfg).Server), logrus.StandardLogger(), prometheus.NewRegistry(), new(health.Controller), NoopApplicationMetadataService{})
   306  			Expect(err).ToNot(HaveOccurred())
   307  		})
   308  		suite()
   309  	})
   310  
   311  	// In-memory storage
   312  	testing.WithConfig(func(cfg **config.Config) {
   313  		JustBeforeEach(func() {
   314  			var err error
   315  			s, err = New(NewConfig(&(*cfg).Server).WithInMemory(), logrus.StandardLogger(), prometheus.NewRegistry(), new(health.Controller), NoopApplicationMetadataService{})
   316  			Expect(err).ToNot(HaveOccurred())
   317  		})
   318  		suite()
   319  	})
   320  })
   321  
   322  var _ = Describe("persistence", func() {
   323  	// Disk-based storage
   324  	testing.WithConfig(func(cfg **config.Config) {
   325  		JustBeforeEach(func() {
   326  			var err error
   327  			s, err = New(NewConfig(&(*cfg).Server), logrus.StandardLogger(), prometheus.NewRegistry(), new(health.Controller), NoopApplicationMetadataService{})
   328  			Expect(err).ToNot(HaveOccurred())
   329  		})
   330  		Context("persist data between restarts", func() {
   331  			It("works correctly", func() {
   332  				tree := tree.New()
   333  				tree.Insert([]byte("a;b"), uint64(1))
   334  				tree.Insert([]byte("a;c"), uint64(2))
   335  				st := testing.SimpleTime(10)
   336  				et := testing.SimpleTime(19)
   337  				st2 := testing.SimpleTime(0)
   338  				et2 := testing.SimpleTime(30)
   339  
   340  				appKey, _ := segment.ParseKey("foo")
   341  				key, _ := segment.ParseKey("foo{tag=value}")
   342  
   343  				err := s.Put(context.TODO(), &PutInput{
   344  					StartTime:  st,
   345  					EndTime:    et,
   346  					Key:        key,
   347  					Val:        tree,
   348  					SpyName:    "testspy",
   349  					SampleRate: 100,
   350  				})
   351  				Expect(err).ToNot(HaveOccurred())
   352  
   353  				o, err := s.Get(context.TODO(), &GetInput{
   354  					StartTime: st2,
   355  					EndTime:   et2,
   356  					Key:       appKey,
   357  				})
   358  
   359  				Expect(err).ToNot(HaveOccurred())
   360  				Expect(o.Tree).ToNot(BeNil())
   361  				Expect(o.Tree.String()).To(Equal(tree.String()))
   362  				Expect(s.Close()).ToNot(HaveOccurred())
   363  
   364  				s2, err := New(NewConfig(&(*cfg).Server), logrus.StandardLogger(), prometheus.NewRegistry(), new(health.Controller), NoopApplicationMetadataService{})
   365  				Expect(err).ToNot(HaveOccurred())
   366  
   367  				o2, err := s2.Get(context.TODO(), &GetInput{
   368  					StartTime: st2,
   369  					EndTime:   et2,
   370  					Key:       appKey,
   371  				})
   372  				Expect(err).ToNot(HaveOccurred())
   373  				Expect(o2.Tree).ToNot(BeNil())
   374  				Expect(o2.Tree.String()).To(Equal(tree.String()))
   375  				Expect(s2.Close()).ToNot(HaveOccurred())
   376  			})
   377  		})
   378  	})
   379  })
   380  
   381  var _ = Describe("querying", func() {
   382  	setup := func() {
   383  		keys := []string{
   384  			"app.name{foo=bar,baz=qux}",
   385  			"app.name{foo=bar,baz=xxx}",
   386  			"app.name{waldo=fred,baz=xxx}",
   387  		}
   388  		for _, k := range keys {
   389  			t := tree.New()
   390  			t.Insert([]byte("a;b"), uint64(1))
   391  			t.Insert([]byte("a;c"), uint64(2))
   392  			st := testing.SimpleTime(10)
   393  			et := testing.SimpleTime(19)
   394  			key, err := segment.ParseKey(k)
   395  			Expect(err).ToNot(HaveOccurred())
   396  			err = s.Put(context.TODO(), &PutInput{
   397  				StartTime:  st,
   398  				EndTime:    et,
   399  				Key:        key,
   400  				Val:        t,
   401  				SpyName:    "testspy",
   402  				SampleRate: 100,
   403  			})
   404  			Expect(err).ToNot(HaveOccurred())
   405  		}
   406  	}
   407  
   408  	suite := func() {
   409  		Context("basic queries", func() {
   410  			It("get returns result with query", func() {
   411  				qry, err := flameql.ParseQuery(`app.name{foo="bar"}`)
   412  				Expect(err).ToNot(HaveOccurred())
   413  				output, err := s.Get(context.TODO(), &GetInput{
   414  					StartTime: time.Time{},
   415  					EndTime:   maxTime,
   416  					Query:     qry,
   417  				})
   418  				Expect(err).ToNot(HaveOccurred())
   419  				Expect(output).ToNot(BeNil())
   420  				Expect(output.Tree).ToNot(BeNil())
   421  				Expect(output.Tree.Samples()).To(Equal(uint64(6)))
   422  				Expect(s.Close()).ToNot(HaveOccurred())
   423  			})
   424  
   425  			It("get returns a particular tree for a fully qualified key", func() {
   426  				k, err := segment.ParseKey(`app.name{foo=bar,baz=qux}`)
   427  				Expect(err).ToNot(HaveOccurred())
   428  				output, err := s.Get(context.TODO(), &GetInput{
   429  					StartTime: time.Time{},
   430  					EndTime:   maxTime,
   431  					Key:       k,
   432  				})
   433  				Expect(err).ToNot(HaveOccurred())
   434  				Expect(output).ToNot(BeNil())
   435  				Expect(output.Tree).ToNot(BeNil())
   436  				Expect(output.Tree.Samples()).To(Equal(uint64(3)))
   437  				Expect(s.Close()).ToNot(HaveOccurred())
   438  			})
   439  
   440  			It("get returns all results for a key containing only app name", func() {
   441  				k, err := segment.ParseKey(`app.name`)
   442  				Expect(err).ToNot(HaveOccurred())
   443  				output, err := s.Get(context.TODO(), &GetInput{
   444  					StartTime: time.Time{},
   445  					EndTime:   maxTime,
   446  					Key:       k,
   447  				})
   448  				Expect(err).ToNot(HaveOccurred())
   449  				Expect(output).ToNot(BeNil())
   450  				Expect(output.Tree).ToNot(BeNil())
   451  				Expect(output.Tree.Samples()).To(Equal(uint64(9)))
   452  				Expect(s.Close()).ToNot(HaveOccurred())
   453  			})
   454  
   455  			It("query returns expected results", func() {
   456  				type testCase struct {
   457  					query       string
   458  					segmentKeys []dimension.Key
   459  				}
   460  
   461  				testCases := []testCase{
   462  					{`app.name`, []dimension.Key{
   463  						dimension.Key("app.name{baz=qux,foo=bar}"),
   464  						dimension.Key("app.name{baz=xxx,foo=bar}"),
   465  						dimension.Key("app.name{baz=xxx,waldo=fred}"),
   466  					}},
   467  					{`app.name{foo="bar"}`, []dimension.Key{
   468  						dimension.Key("app.name{baz=qux,foo=bar}"),
   469  						dimension.Key("app.name{baz=xxx,foo=bar}"),
   470  					}},
   471  					{`app.name{foo=~"^b.*"}`, []dimension.Key{
   472  						dimension.Key("app.name{baz=qux,foo=bar}"),
   473  						dimension.Key("app.name{baz=xxx,foo=bar}"),
   474  					}},
   475  					{`app.name{baz=~"xxx|qux"}`, []dimension.Key{
   476  						dimension.Key("app.name{baz=qux,foo=bar}"),
   477  						dimension.Key("app.name{baz=xxx,foo=bar}"),
   478  						dimension.Key("app.name{baz=xxx,waldo=fred}"),
   479  					}},
   480  					{`app.name{baz!="xxx"}`, []dimension.Key{
   481  						dimension.Key("app.name{baz=qux,foo=bar}"),
   482  					}},
   483  					{`app.name{foo!="bar"}`, []dimension.Key{
   484  						dimension.Key("app.name{baz=xxx,waldo=fred}"),
   485  					}},
   486  					{`app.name{foo!~".*"}`, []dimension.Key{
   487  						dimension.Key("app.name{baz=xxx,waldo=fred}"),
   488  					}},
   489  					{`app.name{baz!~"^x.*"}`, []dimension.Key{
   490  						dimension.Key("app.name{baz=qux,foo=bar}"),
   491  					}},
   492  					{`app.name{foo="bar",baz!~"^x.*"}`, []dimension.Key{
   493  						dimension.Key("app.name{baz=qux,foo=bar}"),
   494  					}},
   495  
   496  					{`app.name{foo=~"b.*",foo!~".*r"}`, nil},
   497  
   498  					{`app.name{foo!="non-existing-value"}`, []dimension.Key{
   499  						dimension.Key("app.name{baz=qux,foo=bar}"),
   500  						dimension.Key("app.name{baz=xxx,foo=bar}"),
   501  						dimension.Key("app.name{baz=xxx,waldo=fred}"),
   502  					}},
   503  					{`app.name{foo!~"non-existing-.*"}`, []dimension.Key{
   504  						dimension.Key("app.name{baz=qux,foo=bar}"),
   505  						dimension.Key("app.name{baz=xxx,foo=bar}"),
   506  						dimension.Key("app.name{baz=xxx,waldo=fred}"),
   507  					}},
   508  					{`app.name{non_existing_key!="bar"}`, []dimension.Key{
   509  						dimension.Key("app.name{baz=qux,foo=bar}"),
   510  						dimension.Key("app.name{baz=xxx,foo=bar}"),
   511  						dimension.Key("app.name{baz=xxx,waldo=fred}"),
   512  					}},
   513  					{`app.name{non_existing_key!~"bar"}`, []dimension.Key{
   514  						dimension.Key("app.name{baz=qux,foo=bar}"),
   515  						dimension.Key("app.name{baz=xxx,foo=bar}"),
   516  						dimension.Key("app.name{baz=xxx,waldo=fred}"),
   517  					}},
   518  
   519  					{`app.name{foo="non-existing-value"}`, nil},
   520  					{`app.name{foo=~"non-existing-.*"}`, nil},
   521  					{`app.name{non_existing_key="bar"}`, nil},
   522  					{`app.name{non_existing_key=~"bar"}`, nil},
   523  
   524  					{`non-existing-app{}`, nil},
   525  				}
   526  
   527  				for _, tc := range testCases {
   528  					qry, err := flameql.ParseQuery(tc.query)
   529  					Expect(err).ToNot(HaveOccurred())
   530  					r := s.execQuery(context.TODO(), qry)
   531  					if tc.segmentKeys == nil {
   532  						Expect(r).To(BeEmpty())
   533  						continue
   534  					}
   535  					Expect(r).To(ConsistOf(tc.segmentKeys))
   536  				}
   537  				Expect(s.Close()).ToNot(HaveOccurred())
   538  			})
   539  		})
   540  	}
   541  
   542  	// Disk-based storage
   543  	testing.WithConfig(func(cfg **config.Config) {
   544  		JustBeforeEach(func() {
   545  			var err error
   546  			s, err = New(NewConfig(&(*cfg).Server), logrus.StandardLogger(), prometheus.NewRegistry(), new(health.Controller), NoopApplicationMetadataService{})
   547  			Expect(err).ToNot(HaveOccurred())
   548  			setup()
   549  		})
   550  		suite()
   551  	})
   552  
   553  	// In-memory storage
   554  	testing.WithConfig(func(cfg **config.Config) {
   555  		JustBeforeEach(func() {
   556  			var err error
   557  			s, err = New(NewConfig(&(*cfg).Server).WithInMemory(), logrus.StandardLogger(), prometheus.NewRegistry(), new(health.Controller), NoopApplicationMetadataService{})
   558  			Expect(err).ToNot(HaveOccurred())
   559  			setup()
   560  		})
   561  		suite()
   562  	})
   563  })
   564  
   565  var _ = Describe("CollectGarbage", func() {
   566  	suite := func() {
   567  		Context("RetentionPolicy", func() {
   568  			It("removes data outside retention period", func() {
   569  				key, _ := segment.ParseKey("foo")
   570  				tree := tree.New()
   571  				tree.Insert([]byte("a;b"), uint64(1))
   572  				tree.Insert([]byte("a;c"), uint64(2))
   573  				now := time.Now()
   574  
   575  				err := s.Put(context.TODO(), &PutInput{
   576  					StartTime:  now.Add(-3 * time.Hour),
   577  					EndTime:    now.Add(-3 * time.Hour).Add(time.Second * 10),
   578  					Key:        key,
   579  					Val:        tree,
   580  					SpyName:    "testspy",
   581  					SampleRate: 100,
   582  				})
   583  				Expect(err).ToNot(HaveOccurred())
   584  
   585  				err = s.Put(context.TODO(), &PutInput{
   586  					StartTime:  now.Add(-time.Minute),
   587  					EndTime:    now.Add(-time.Minute).Add(time.Second * 10),
   588  					Key:        key,
   589  					Val:        tree,
   590  					SpyName:    "testspy",
   591  					SampleRate: 100,
   592  				})
   593  				Expect(err).ToNot(HaveOccurred())
   594  
   595  				rp := segment.NewRetentionPolicy().SetAbsolutePeriod(time.Hour)
   596  				s.enforceRetentionPolicy(context.Background(), rp)
   597  				Expect(err).ToNot(HaveOccurred())
   598  
   599  				o, err := s.Get(context.TODO(), &GetInput{
   600  					StartTime: now.Add(-3 * time.Hour),
   601  					EndTime:   now.Add(-3 * time.Hour).Add(time.Second * 10),
   602  					Key:       key,
   603  				})
   604  
   605  				Expect(err).ToNot(HaveOccurred())
   606  				Expect(o).To(BeNil())
   607  
   608  				o, err = s.Get(context.TODO(), &GetInput{
   609  					StartTime: now.Add(-time.Minute),
   610  					EndTime:   now.Add(-time.Minute).Add(time.Second * 10),
   611  					Key:       key,
   612  				})
   613  
   614  				Expect(err).ToNot(HaveOccurred())
   615  				Expect(o).ToNot(BeNil())
   616  
   617  				Expect(s.Close()).ToNot(HaveOccurred())
   618  			})
   619  		})
   620  	}
   621  
   622  	// Disk-based storage
   623  	testing.WithConfig(func(cfg **config.Config) {
   624  		JustBeforeEach(func() {
   625  			var err error
   626  			s, err = New(NewConfig(&(*cfg).Server), logrus.StandardLogger(), prometheus.NewRegistry(), new(health.Controller), NoopApplicationMetadataService{})
   627  			Expect(err).ToNot(HaveOccurred())
   628  		})
   629  		suite()
   630  	})
   631  
   632  	// In-memory storage
   633  	testing.WithConfig(func(cfg **config.Config) {
   634  		JustBeforeEach(func() {
   635  			var err error
   636  			s, err = New(NewConfig(&(*cfg).Server).WithInMemory(), logrus.StandardLogger(), prometheus.NewRegistry(), new(health.Controller), NoopApplicationMetadataService{})
   637  			Expect(err).ToNot(HaveOccurred())
   638  		})
   639  		suite()
   640  	})
   641  })
   642  
   643  var _ = Describe("Getters", func() {
   644  	testing.WithConfig(func(cfg **config.Config) {
   645  		JustBeforeEach(func() {
   646  			var err error
   647  			s, err = New(NewConfig(&(*cfg).Server), logrus.StandardLogger(), prometheus.NewRegistry(), new(health.Controller), NoopApplicationMetadataService{})
   648  			Expect(err).ToNot(HaveOccurred())
   649  		})
   650  
   651  		It("gets app names correctly", func() {
   652  			tree := tree.New()
   653  			tree.Insert([]byte("a;b"), uint64(1))
   654  			tree.Insert([]byte("a;c"), uint64(2))
   655  			st := testing.SimpleTime(10)
   656  			et := testing.SimpleTime(19)
   657  			key, _ := segment.ParseKey("foo")
   658  
   659  			s.Put(context.TODO(), &PutInput{
   660  				StartTime:  st,
   661  				EndTime:    et,
   662  				Key:        key,
   663  				Val:        tree,
   664  				SpyName:    "testspy",
   665  				SampleRate: 100,
   666  			})
   667  
   668  			want := []string{"foo"}
   669  			Expect(s.GetAppNames(context.TODO())).To(Equal(
   670  				want,
   671  			))
   672  			Expect(s.Close()).ToNot(HaveOccurred())
   673  		})
   674  	})
   675  })