1- package block
1+ package block_test
22
33import (
44 "context"
55 "encoding/json"
6+ "fmt"
67 "os"
78 "path/filepath"
9+ "sort"
10+ "strings"
811 "testing"
912
13+ "github.com/prometheus/prometheus/model/labels"
14+ "github.com/prometheus/prometheus/prompb"
1015 "github.com/stretchr/testify/assert"
16+ "github.com/stretchr/testify/mock"
1117 "github.com/stretchr/testify/require"
1218 "google.golang.org/protobuf/encoding/protojson"
1319
1420 metastorev1 "github.com/grafana/pyroscope/api/gen/proto/go/metastore/v1"
21+ "github.com/grafana/pyroscope/pkg/block"
22+ "github.com/grafana/pyroscope/pkg/metrics"
23+ phlaremodel "github.com/grafana/pyroscope/pkg/model"
1524 "github.com/grafana/pyroscope/pkg/objstore/testutil"
25+ "github.com/grafana/pyroscope/pkg/test/mocks/mockmetrics"
1626)
1727
1828func Test_CompactBlocks (t * testing.T ) {
@@ -26,12 +36,12 @@ func Test_CompactBlocks(t *testing.T) {
2636 require .NoError (t , err )
2737
2838 dst , tempdir := testutil .NewFilesystemBucket (t , ctx , t .TempDir ())
29- compactedBlocks , err := Compact (ctx , resp .Blocks , bucket ,
30- WithCompactionDestination (dst ),
31- WithCompactionTempDir (tempdir ),
32- WithCompactionObjectOptions (
33- WithObjectDownload (filepath .Join (tempdir , "source" )),
34- WithObjectMaxSizeLoadInMemory (0 )), // Force download.
39+ compactedBlocks , err := block . Compact (ctx , resp .Blocks , bucket ,
40+ block . WithCompactionDestination (dst ),
41+ block . WithCompactionTempDir (tempdir ),
42+ block . WithCompactionObjectOptions (
43+ block . WithObjectDownload (filepath .Join (tempdir , "source" )),
44+ block . WithObjectMaxSizeLoadInMemory (0 )), // Force download.
3545 )
3646
3747 require .NoError (t , err )
@@ -46,12 +56,12 @@ func Test_CompactBlocks(t *testing.T) {
4656 assert .Equal (t , string (expectedJson ), string (compactedJson ))
4757
4858 t .Run ("Compact compacted blocks" , func (t * testing.T ) {
49- compactedBlocks , err = Compact (ctx , compactedBlocks , dst ,
50- WithCompactionDestination (dst ),
51- WithCompactionTempDir (tempdir ),
52- WithCompactionObjectOptions (
53- WithObjectDownload (filepath .Join (tempdir , "source" )),
54- WithObjectMaxSizeLoadInMemory (0 )), // Force download.
59+ compactedBlocks , err = block . Compact (ctx , compactedBlocks , dst ,
60+ block . WithCompactionDestination (dst ),
61+ block . WithCompactionTempDir (tempdir ),
62+ block . WithCompactionObjectOptions (
63+ block . WithObjectDownload (filepath .Join (tempdir , "source" )),
64+ block . WithObjectMaxSizeLoadInMemory (0 )), // Force download.
5565 )
5666
5767 require .NoError (t , err )
@@ -60,3 +70,274 @@ func Test_CompactBlocks(t *testing.T) {
6070 require .Len (t , compactedBlocks [0 ].Datasets , 4 )
6171 })
6272}
73+
74+ func Test_CompactBlocks_recordingRules (t * testing.T ) {
75+ ctx := context .Background ()
76+ bucket , _ := testutil .NewFilesystemBucket (t , ctx , "testdata" )
77+
78+ var resp metastorev1.GetBlockMetadataResponse
79+ raw , err := os .ReadFile ("testdata/block-metas.json" )
80+ require .NoError (t , err )
81+ err = protojson .Unmarshal (raw , & resp )
82+ require .NoError (t , err )
83+
84+ exporter := & stringExporter {}
85+ ruler := new (mockmetrics.MockRuler )
86+ ruler .On ("RecordingRules" , mock .Anything ).Return ([]* phlaremodel.RecordingRule {
87+ {
88+ Matchers : []* labels.Matcher {
89+ labels .MustNewMatcher (labels .MatchEqual , "__profile_type__" , "goroutine:goroutine:count:goroutine:count" ),
90+ },
91+ GroupBy : []string {"service_name" },
92+ ExternalLabels : labels .New (labels.Label {Name : "__name__" , Value : "profiles_recorded_goroutines_total_count" }),
93+ },
94+ {
95+ Matchers : []* labels.Matcher {
96+ labels .MustNewMatcher (labels .MatchEqual , "__profile_type__" , "memory:alloc_objects:count:space:bytes" ),
97+ },
98+ GroupBy : []string {"service_name" },
99+ ExternalLabels : labels .New (labels.Label {Name : "__name__" , Value : "profiles_recorded_mem_alloc_total_count" }),
100+ },
101+ {
102+ Matchers : []* labels.Matcher {
103+ labels .MustNewMatcher (labels .MatchEqual , "__profile_type__" , "memory:alloc_space:bytes:space:bytes" ),
104+ },
105+ GroupBy : []string {"service_name" },
106+ ExternalLabels : labels .New (labels.Label {Name : "__name__" , Value : "profiles_recorded_mem_alloc_total_bytes" }),
107+ },
108+ {
109+ Matchers : []* labels.Matcher {
110+ labels .MustNewMatcher (labels .MatchEqual , "__profile_type__" , "memory:inuse_objects:count:space:bytes" ),
111+ },
112+ GroupBy : []string {"service_name" },
113+ ExternalLabels : labels .New (labels.Label {Name : "__name__" , Value : "profiles_recorded_mem_inuse_total_count" }),
114+ },
115+ {
116+ Matchers : []* labels.Matcher {
117+ labels .MustNewMatcher (labels .MatchEqual , "__profile_type__" , "memory:inuse_space:bytes:space:bytes" ),
118+ },
119+ GroupBy : []string {"service_name" },
120+ ExternalLabels : labels .New (labels.Label {Name : "__name__" , Value : "profiles_recorded_mem_inuse_total_bytes" }),
121+ },
122+ {
123+ Matchers : []* labels.Matcher {
124+ labels .MustNewMatcher (labels .MatchEqual , "__profile_type__" , "process_cpu:cpu:nanoseconds:cpu:nanoseconds" ),
125+ },
126+ GroupBy : []string {"service_name" },
127+ ExternalLabels : labels .New (labels.Label {Name : "__name__" , Value : "profiles_recorded_cpu_usage_total_nanoseconds" }),
128+ },
129+ {
130+ Matchers : []* labels.Matcher {
131+ labels .MustNewMatcher (labels .MatchEqual , "__profile_type__" , "process_cpu:samples:count:cpu:nanoseconds" ),
132+ },
133+ GroupBy : []string {"service_name" },
134+ ExternalLabels : labels .New (labels.Label {Name : "__name__" , Value : "profiles_recorded_cpu_usage_total_samples" }),
135+ },
136+ // functions
137+ {
138+ Matchers : []* labels.Matcher {
139+ labels .MustNewMatcher (labels .MatchEqual , "__profile_type__" , "goroutine:goroutine:count:goroutine:count" ),
140+ },
141+ GroupBy : []string {"service_name" },
142+ ExternalLabels : labels .New (labels.Label {Name : "__name__" , Value : "profiles_recorded_goroutines_function_total_servehttp_count" }),
143+ FunctionName : "net/http.HandlerFunc.ServeHTTP" ,
144+ },
145+ {
146+ Matchers : []* labels.Matcher {
147+ labels .MustNewMatcher (labels .MatchEqual , "__profile_type__" , "memory:alloc_objects:count:space:bytes" ),
148+ },
149+ GroupBy : []string {"service_name" },
150+ ExternalLabels : labels .New (labels.Label {Name : "__name__" , Value : "profiles_recorded_mem_alloc_function_total_servehttp_count" }),
151+ FunctionName : "net/http.HandlerFunc.ServeHTTP" ,
152+ },
153+ {
154+ Matchers : []* labels.Matcher {
155+ labels .MustNewMatcher (labels .MatchEqual , "__profile_type__" , "memory:alloc_space:bytes:space:bytes" ),
156+ },
157+ GroupBy : []string {"service_name" },
158+ ExternalLabels : labels .New (labels.Label {Name : "__name__" , Value : "profiles_recorded_mem_alloc_function_total_servehttp_bytes" }),
159+ FunctionName : "net/http.HandlerFunc.ServeHTTP" ,
160+ },
161+ {
162+ Matchers : []* labels.Matcher {
163+ labels .MustNewMatcher (labels .MatchEqual , "__profile_type__" , "memory:inuse_objects:count:space:bytes" ),
164+ },
165+ GroupBy : []string {"service_name" },
166+ ExternalLabels : labels .New (labels.Label {Name : "__name__" , Value : "profiles_recorded_mem_inuse_function_total_servehttp_count" }),
167+ FunctionName : "net/http.HandlerFunc.ServeHTTP" ,
168+ },
169+ {
170+ Matchers : []* labels.Matcher {
171+ labels .MustNewMatcher (labels .MatchEqual , "__profile_type__" , "memory:inuse_space:bytes:space:bytes" ),
172+ },
173+ GroupBy : []string {"service_name" },
174+ ExternalLabels : labels .New (labels.Label {Name : "__name__" , Value : "profiles_recorded_mem_inuse_function_total_servehttp_bytes" }),
175+ FunctionName : "net/http.HandlerFunc.ServeHTTP" ,
176+ },
177+ {
178+ Matchers : []* labels.Matcher {
179+ labels .MustNewMatcher (labels .MatchEqual , "__profile_type__" , "process_cpu:cpu:nanoseconds:cpu:nanoseconds" ),
180+ },
181+ GroupBy : []string {"service_name" },
182+ ExternalLabels : labels .New (labels.Label {Name : "__name__" , Value : "profiles_recorded_cpu_usage_function_total_servehttp_nanoseconds" }),
183+ FunctionName : "net/http.HandlerFunc.ServeHTTP" ,
184+ },
185+ {
186+ Matchers : []* labels.Matcher {
187+ labels .MustNewMatcher (labels .MatchEqual , "__profile_type__" , "process_cpu:samples:count:cpu:nanoseconds" ),
188+ },
189+ GroupBy : []string {"service_name" },
190+ ExternalLabels : labels .New (labels.Label {Name : "__name__" , Value : "profiles_recorded_cpu_usage_function_total_servehttp_samples" }),
191+ FunctionName : "net/http.HandlerFunc.ServeHTTP" ,
192+ },
193+ })
194+ sampleObserver := metrics .NewSampleObserver (0 , exporter , ruler , labels .EmptyLabels ())
195+
196+ compactedBlocks , err := block .Compact (ctx , resp .Blocks , bucket ,
197+ block .WithSampleObserver (sampleObserver ),
198+ )
199+ // Close observer to flush export
200+ sampleObserver .Close ()
201+
202+ require .NoError (t , err )
203+ require .Len (t , compactedBlocks , 1 )
204+ require .NotZero (t , compactedBlocks [0 ].Size )
205+ require .Len (t , compactedBlocks [0 ].Datasets , 4 )
206+
207+ expectedMetrics , err := os .ReadFile ("testdata/profiles_recorded.txt" )
208+ require .NoError (t , err )
209+ expectedMetricsArray := strings .Split (string (expectedMetrics ), "\n " )
210+ sort .Strings (expectedMetricsArray )
211+ actualMetricsArray := strings .Split (exporter .String (), "\n " )
212+ sort .Strings (actualMetricsArray )
213+ assert .Equal (t , expectedMetricsArray , actualMetricsArray )
214+
215+ compactedJson , err := json .MarshalIndent (compactedBlocks , "" , " " )
216+ require .NoError (t , err )
217+ expectedJson , err := os .ReadFile ("testdata/compacted.golden" )
218+ require .NoError (t , err )
219+ assert .Equal (t , string (expectedJson ), string (compactedJson ))
220+ }
221+
222+ func Test_CompactBlocks_recordingRules_shadowedSymbols (t * testing.T ) {
223+ ctx := context .Background ()
224+ bucket , _ := testutil .NewFilesystemBucket (t , ctx , "testdata" )
225+
226+ var resp metastorev1.GetBlockMetadataResponse
227+ raw , err := os .ReadFile ("testdata/block-metas.json" )
228+ require .NoError (t , err )
229+ err = protojson .Unmarshal (raw , & resp )
230+ require .NoError (t , err )
231+
232+ exporter := & stringExporter {}
233+ ruler := new (mockmetrics.MockRuler )
234+ ruler .On ("RecordingRules" , mock .Anything ).Return ([]* phlaremodel.RecordingRule {
235+ {
236+ Matchers : []* labels.Matcher {
237+ labels .MustNewMatcher (labels .MatchEqual , "service_name" , "pyroscope-test/ingester" ),
238+ labels .MustNewMatcher (labels .MatchEqual , "__profile_type__" , "memory:alloc_space:bytes:space:bytes" ),
239+ },
240+ ExternalLabels : labels .New (labels.Label {Name : "__name__" , Value : "profiles_recorded_mem_alloc_total_pyroscope_ingester_Push_bytes" }),
241+ FunctionName : "github.com/grafana/pyroscope/pkg/ingester.(*Ingester).Push" ,
242+ },
243+ {
244+ Matchers : []* labels.Matcher {
245+ labels .MustNewMatcher (labels .MatchEqual , "service_name" , "pyroscope-test/ingester" ),
246+ labels .MustNewMatcher (labels .MatchEqual , "__profile_type__" , "memory:inuse_space:bytes:space:bytes" ),
247+ },
248+ ExternalLabels : labels .New (labels.Label {Name : "__name__" , Value : "profiles_recorded_mem_inuse_total_pyroscope_ingester_Push_bytes" }),
249+ FunctionName : "github.com/grafana/pyroscope/pkg/ingester.(*Ingester).Push" ,
250+ },
251+ {
252+ Matchers : []* labels.Matcher {
253+ labels .MustNewMatcher (labels .MatchEqual , "service_name" , "pyroscope-test/ingester" ),
254+ labels .MustNewMatcher (labels .MatchEqual , "__profile_type__" , "process_cpu:samples:count:cpu:nanoseconds" ),
255+ },
256+ ExternalLabels : labels .New (labels.Label {Name : "__name__" , Value : "profiles_recorded_cpu_usage_total_pyroscope_ingester_Push_samples" }),
257+ FunctionName : "github.com/grafana/pyroscope/pkg/ingester.(*Ingester).Push" ,
258+ },
259+ {
260+ Matchers : []* labels.Matcher {
261+ labels .MustNewMatcher (labels .MatchEqual , "service_name" , "pyroscope-test/ingester" ),
262+ labels .MustNewMatcher (labels .MatchEqual , "__profile_type__" , "memory:alloc_space:bytes:space:bytes" ),
263+ },
264+ ExternalLabels : labels .New (labels.Label {Name : "__name__" , Value : "profiles_recorded_mem_alloc_total_pyroscope_ingester_Serve_bytes" }),
265+ FunctionName : "net/http.HandlerFunc.ServeHTTP" ,
266+ },
267+ {
268+ Matchers : []* labels.Matcher {
269+ labels .MustNewMatcher (labels .MatchEqual , "service_name" , "pyroscope-test/ingester" ),
270+ labels .MustNewMatcher (labels .MatchEqual , "__profile_type__" , "memory:inuse_space:bytes:space:bytes" ),
271+ },
272+ ExternalLabels : labels .New (labels.Label {Name : "__name__" , Value : "profiles_recorded_mem_inuse_total_pyroscope_ingester_Serve_bytes" }),
273+ FunctionName : "net/http.HandlerFunc.ServeHTTP" ,
274+ },
275+ {
276+ Matchers : []* labels.Matcher {
277+ labels .MustNewMatcher (labels .MatchEqual , "service_name" , "pyroscope-test/ingester" ),
278+ labels .MustNewMatcher (labels .MatchEqual , "__profile_type__" , "process_cpu:samples:count:cpu:nanoseconds" ),
279+ },
280+ ExternalLabels : labels .New (labels.Label {Name : "__name__" , Value : "profiles_recorded_cpu_usage_total_pyroscope_ingester_Serve_samples" }),
281+ FunctionName : "net/http.HandlerFunc.ServeHTTP" ,
282+ },
283+ {
284+ Matchers : []* labels.Matcher {
285+ labels .MustNewMatcher (labels .MatchEqual , "service_name" , "pyroscope-test/query-frontend" ),
286+ labels .MustNewMatcher (labels .MatchEqual , "__profile_type__" , "memory:inuse_space:bytes:space:bytes" ),
287+ },
288+ ExternalLabels : labels .New (labels.Label {Name : "__name__" , Value : "profiles_recorded_mem_inuse_total_query_Serve_bytes" }),
289+ FunctionName : "net/http.HandlerFunc.ServeHTTP" ,
290+ },
291+ {
292+ Matchers : []* labels.Matcher {
293+ labels .MustNewMatcher (labels .MatchEqual , "service_name" , "pyroscope-test/query-frontend" ),
294+ labels .MustNewMatcher (labels .MatchEqual , "__profile_type__" , "memory:alloc_space:bytes:space:bytes" ),
295+ },
296+ ExternalLabels : labels .New (labels.Label {Name : "__name__" , Value : "profiles_recorded_mem_alloc_total_query_Serve_bytes" }),
297+ FunctionName : "net/http.HandlerFunc.ServeHTTP" ,
298+ },
299+ })
300+ sampleObserver := metrics .NewSampleObserver (0 , exporter , ruler , labels .EmptyLabels ())
301+
302+ compactedBlocks , err := block .Compact (ctx , resp .Blocks , bucket ,
303+ block .WithSampleObserver (sampleObserver ),
304+ )
305+ // Close observer to flush export
306+ sampleObserver .Close ()
307+
308+ require .NoError (t , err )
309+ require .Len (t , compactedBlocks , 1 )
310+ require .NotZero (t , compactedBlocks [0 ].Size )
311+ require .Len (t , compactedBlocks [0 ].Datasets , 4 )
312+
313+ expectedMetrics , err := os .ReadFile ("testdata/profiles_recorded_shadowed.txt" )
314+ require .NoError (t , err )
315+ expectedMetricsArray := strings .Split (string (expectedMetrics ), "\n " )
316+ sort .Strings (expectedMetricsArray )
317+ actualMetricsArray := strings .Split (exporter .String (), "\n " )
318+ sort .Strings (actualMetricsArray )
319+ assert .Equal (t , expectedMetricsArray , actualMetricsArray )
320+
321+ compactedJson , err := json .MarshalIndent (compactedBlocks , "" , " " )
322+ require .NoError (t , err )
323+ expectedJson , err := os .ReadFile ("testdata/compacted.golden" )
324+ require .NoError (t , err )
325+ assert .Equal (t , string (expectedJson ), string (compactedJson ))
326+ }
327+
328+ type stringExporter struct {
329+ output string
330+ }
331+
332+ func (e * stringExporter ) Send (tenant string , series []prompb.TimeSeries ) error {
333+ for _ , s := range series {
334+ e .output += fmt .Sprintf ("%s: %s\n " , tenant , s .String ())
335+ }
336+ return nil
337+ }
338+
339+ func (* stringExporter ) Flush () {}
340+
341+ func (e * stringExporter ) String () string {
342+ return e .output
343+ }
0 commit comments