Repository: prometheus/prometheus
File: web/api/v1/api.go (Go)

Code before:
		result = append(result, item)
	}
	return result
}

func (api *API) serveTSDBBlock(_ *http.Request) apiFuncResult {
	dirs, err := os.ReadDir(api.dbDir)
	if err != nil {
		return apiFuncResult{nil, &apiError{errorInternal, fmt.Errorf("read TSDB dir: %w", err)}, nil, nil}
	}

	var blocks []tsdb.BlockMeta

	for _, entry := range dirs {
		if !entry.IsDir() {
			continue
		}
		_, err := ulid.Parse(entry.Name())
		if err != nil {
			continue
		}

		blockPath := filepath.Join(api.dbDir, entry.Name())
		block, err := tsdb.OpenBlock(api.logger, blockPath, nil, nil)
		if err != nil {
			continue
		}
		meta := block.Meta()
		blocks = append(blocks, meta)
		defer block.Close()
	}

	return apiFuncResult{
		data: map[string][]tsdb.BlockMeta{
			"blocks": blocks,
		},
	}
}

func (api *API) serveTSDBStatus(r *http.Request) apiFuncResult {
	limit := 10
	if s := r.FormValue("limit"); s != "" {
		var err error
		if limit, err = strconv.Atoi(s); err != nil || limit < 1 {
			return apiFuncResult{nil, &apiError{errorBadData, errors.New("limit must be a positive number")}, nil, nil}
		}
	}
	s, err := api.db.Stats(labels.MetricName, limit)
	if err != nil {
		return apiFuncResult{nil, &apiError{errorInternal, err}, nil, nil}
	}

Review comment (bug):
Is this `continue` here to handle deletions of underlying block directories while iterating over them? I think we should error out instead of just continuing unless it's an error that indicates that the directory doesn't exist anymore, right?

But this race condition, along with my other comment above about re-fetching the metadata for each block although we already have that information in the TSDB instance itself somewhere, make me wonder whether it would be better to add a function to the TSDB itself that can return this existing info. Then the TSDB instance could handle both the race condition / concurrency aspects (which I guess it already has to do anyway for other purposes), as well as handing out the existing info it has. I don't know too much about the current TSDB code though, does that make sense?

Code after:
		result = append(result, item)
	}
	return result
}

func (api *API) serveTSDBBlocks(_ *http.Request) apiFuncResult {
	blockMetas, err := api.db.BlockMetas()
	if err != nil {
		return apiFuncResult{nil, &apiError{errorInternal, fmt.Errorf("error getting block metadata: %w", err)}, nil, nil}
	}

	return apiFuncResult{
		data: map[string][]tsdb.BlockMeta{
			"blocks": blockMetas,
		},
	}
}

func (api *API) serveTSDBStatus(r *http.Request) apiFuncResult {
	limit := 10
	if s := r.FormValue("limit"); s != "" {
		var err error
		if limit, err = strconv.Atoi(s); err != nil || limit < 1 {
			return apiFuncResult{nil, &apiError{errorBadData, errors.New("limit must be a positive number")}, nil, nil}
		}
	}
	s, err := api.db.Stats(labels.MetricName, limit)
	if err != nil {
		return apiFuncResult{nil, &apiError{errorInternal, err}, nil, nil}
	}
	metrics, err := api.gatherer.Gather()
	if err != nil {
		return apiFuncResult{nil, &apiError{errorInternal, fmt.Errorf("error gathering runtime status: %w", err)}, nil, nil}
	}
	chunkCount := int64(math.NaN())
	for _, mF := range metrics {
		if *mF.Name == "prometheus_tsdb_head_chunks" {
			m := mF.Metric[0]
			if m.Gauge != nil {
				chunkCount = int64(m.Gauge.GetValue())
				break
			}
		}
	}
	return apiFuncResult{TSDBStatus{
		HeadStats: HeadStats{
			NumSeries:     s.NumSeries,
			ChunkCount:    chunkCount,
			MinTime:       s.MinTime,
			MaxTime:       s.MaxTime,
			NumLabelPairs: s.IndexPostingStats.NumLabelPairs,