github.com/grafana/pyroscope@v1.18.0/pkg/operations/v2/model.go (about) 1 package v2 2 3 import ( 4 "context" 5 "fmt" 6 "net/http" 7 "slices" 8 "strings" 9 "time" 10 11 "github.com/gorilla/mux" 12 "github.com/pkg/errors" 13 14 metastorev1 "github.com/grafana/pyroscope/api/gen/proto/go/metastore/v1" 15 "github.com/grafana/pyroscope/pkg/operations" 16 ) 17 18 type blockQuery struct { 19 From string 20 To string 21 View string 22 23 parsedFrom time.Time 24 parsedTo time.Time 25 } 26 27 func readQuery(r *http.Request) *blockQuery { 28 queryFrom := r.URL.Query().Get("queryFrom") 29 if queryFrom == "" { 30 queryFrom = "now-24h" 31 } 32 parsedFrom, _ := operations.ParseTime(queryFrom) 33 queryTo := r.URL.Query().Get("queryTo") 34 if queryTo == "" { 35 queryTo = "now" 36 } 37 parsedTo, _ := operations.ParseTime(queryTo) 38 view := r.URL.Query().Get("view") 39 if view == "" { 40 view = "table" 41 } 42 return &blockQuery{ 43 From: queryFrom, 44 To: queryTo, 45 View: view, 46 parsedFrom: parsedFrom, 47 parsedTo: parsedTo, 48 } 49 } 50 51 type blockDetails struct { 52 ID string 53 MinTime string 54 MaxTime string 55 Duration int 56 FormattedDuration string 57 Shard uint32 58 CompactionLevel uint32 59 Size string 60 Datasets []datasetDetails 61 BlockTenant string // Empty for multi-tenant blocks (compaction level 0) 62 } 63 64 type labelPair struct { 65 Key string 66 Value string 67 } 68 69 type labelSet struct { 70 Pairs []labelPair 71 } 72 73 type datasetDetails struct { 74 Tenant string 75 Name string 76 MinTime string 77 MaxTime string 78 Size string 79 ProfilesSize string 80 IndexSize string 81 SymbolsSize string 82 ProfilesPercentage float64 83 IndexPercentage float64 84 SymbolsPercentage float64 85 LabelSets []labelSet 86 } 87 88 type blockGroup struct { 89 MinTime time.Time 90 FormattedMinTime string 91 Blocks []*blockDetails 92 MinTimeAge string 93 MaxBlockDurationMinutes int 94 } 95 96 type blockListResult struct { 97 BlockGroups []*blockGroup 98 MaxBlocksPerGroup int 99 GroupDurationMinutes int 100 } 101 102 // Sorts a slice of block groups by MinTime in descending order. 103 func sortBlockGroupsByMinTimeDec(bg []*blockGroup) { 104 slices.SortFunc(bg, func(a, b *blockGroup) int { 105 return b.MinTime.Compare(a.MinTime) 106 }) 107 } 108 109 // Sorts a slice of block details by MinTime in descending order. 110 func sortBlockDetailsByMinTimeDec(bd []*blockDetails) { 111 slices.SortFunc(bd, func(a, b *blockDetails) int { 112 return strings.Compare(b.MinTime, a.MinTime) 113 }) 114 } 115 116 type profileInfo struct { 117 RowNumber int 118 Timestamp string 119 SeriesIndex uint32 120 ProfileType string 121 SampleCount int 122 } 123 124 type treeNode struct { 125 Name string 126 Value uint64 127 Percent float64 128 Location string // File path and line number (e.g., "pkg/util/logger.go:L91") 129 FormattedValue string // Formatted value with unit (e.g., "1.5 MB", "250 ms") 130 Children []*treeNode 131 } 132 133 type profileCallTreePageContent struct { 134 User string 135 BlockID string 136 Shard uint32 137 BlockTenant string 138 Dataset *datasetDetails 139 Timestamp string 140 ProfileInfo *profileMetadata 141 Tree *treeNode 142 Now string 143 } 144 145 type profileMetadata struct { 146 Labels []labelPair 147 SampleCount int 148 Unit string 149 ProfileType string 150 } 151 152 type tsdbIndexInfo struct { 153 From string 154 Through string 155 Checksum uint32 156 Series []seriesInfo 157 Symbols []string 158 LabelValueSets []labelValueSet 159 } 160 161 type labelValueSet struct { 162 LabelName string 163 LabelValues []string 164 } 165 166 type seriesInfo struct { 167 SeriesIndex uint32 168 SeriesRef uint64 169 Labels []labelPair 170 } 171 172 type datasetIndexPageContent struct { 173 User string 174 BlockID string 175 Shard uint32 176 BlockTenant string 177 Dataset *datasetDetails 178 TSDBIndex *tsdbIndexInfo 179 Now string 180 } 181 182 type symbolsInfo struct { 183 Strings []symbolEntry 184 TotalStrings int 185 Functions []functionEntry 186 TotalFunctions int 187 Locations []locationEntry 188 TotalLocations int 189 Mappings []mappingEntry 190 TotalMappings int 191 } 192 193 type symbolEntry struct { 194 Index int 195 Symbol string 196 } 197 198 type functionEntry struct { 199 Index int 200 ID uint64 201 Name string 202 SystemName string 203 Filename string 204 StartLine uint32 205 } 206 207 type locationLine struct { 208 FunctionName string 209 Line int64 210 } 211 212 type locationEntry struct { 213 Index int 214 ID uint64 215 Address uint64 216 MappingID uint32 217 Lines []locationLine 218 } 219 220 type mappingEntry struct { 221 Index int 222 ID uint64 223 MemoryStart uint64 224 MemoryLimit uint64 225 FileOffset uint64 226 Filename string 227 BuildID string 228 } 229 230 type datasetSymbolsPageContent struct { 231 User string 232 BlockID string 233 Shard uint32 234 BlockTenant string 235 Dataset *datasetDetails 236 Symbols *symbolsInfo 237 Page int 238 PageSize int 239 TotalPages int 240 HasPrevPage bool 241 HasNextPage bool 242 Tab string 243 Now string 244 } 245 246 const emptyDatasetPlaceholder = "_empty" 247 248 type datasetRequest struct { 249 TenantID string 250 BlockID string 251 BlockTenant string 252 DatasetName string 253 Shard uint32 254 } 255 256 func parseDatasetRequest(r *http.Request) (*datasetRequest, error) { 257 vars := mux.Vars(r) 258 259 tenantID := vars["tenant"] 260 if tenantID == "" { 261 return nil, errors.New("No tenant id provided") 262 } 263 264 blockID := vars["block"] 265 if blockID == "" { 266 return nil, errors.New("No block id provided") 267 } 268 269 datasetName := r.URL.Query().Get("dataset") 270 if datasetName == "" { 271 return nil, errors.New("No dataset name provided") 272 } 273 if datasetName == emptyDatasetPlaceholder { 274 datasetName = "" 275 } 276 277 shardStr := r.URL.Query().Get("shard") 278 if shardStr == "" { 279 return nil, errors.New("No shard provided") 280 } 281 var shard uint32 282 if _, err := fmt.Sscanf(shardStr, "%d", &shard); err != nil { 283 return nil, errors.Wrap(err, "invalid shard parameter") 284 } 285 286 blockTenant := r.URL.Query().Get("block_tenant") 287 288 return &datasetRequest{ 289 TenantID: tenantID, 290 BlockID: blockID, 291 BlockTenant: blockTenant, 292 DatasetName: datasetName, 293 Shard: shard, 294 }, nil 295 } 296 297 func (h *Handlers) getDatasetMetadata(ctx context.Context, req *datasetRequest) (*metastorev1.BlockMeta, *metastorev1.Dataset, error) { 298 metadataResp, err := h.MetastoreClient.GetBlockMetadata(ctx, &metastorev1.GetBlockMetadataRequest{ 299 Blocks: &metastorev1.BlockList{ 300 Tenant: req.BlockTenant, 301 Shard: req.Shard, 302 Blocks: []string{req.BlockID}, 303 }, 304 }) 305 if err != nil { 306 return nil, nil, errors.Wrap(err, "failed to get block metadata") 307 } 308 309 if len(metadataResp.Blocks) == 0 { 310 return nil, nil, errors.New("Block not found") 311 } 312 313 blockMeta := metadataResp.Blocks[0] 314 315 var foundDataset *metastorev1.Dataset 316 for _, ds := range blockMeta.Datasets { 317 dsName := blockMeta.StringTable[ds.Name] 318 if dsName == req.DatasetName { 319 foundDataset = ds 320 break 321 } 322 } 323 324 if foundDataset == nil { 325 return nil, nil, errors.New("Dataset not found") 326 } 327 328 return blockMeta, foundDataset, nil 329 }