Repository: prometheus/prometheus
File: tsdb/head.go (Go)

Code before:
}

// compactable returns whether the head has a compactable range.
// The head has a compactable range when the head time range is 1.5 times the chunk range.
// The 0.5 acts as a buffer of the appendable window.
func (h *Head) compactable() bool {
	if !h.initialized() {
		return false
	}

	return h.MaxTime()-h.MinTime() > h.chunkRange.Load()/2*3
}

// Close flushes the WAL and closes the head.
// It also takes a snapshot of in-memory chunks if enabled.
func (h *Head) Close() error {
	h.closedMtx.Lock()
	defer h.closedMtx.Unlock()
	h.closed = true

	// mmap all but last chunk in case we're performing snapshot since that only
	// takes samples from most recent head chunk.
	h.mmapHeadChunks()

	var errs []error
	if err := h.chunkDiskMapper.Close(); err != nil {
		errs = append(errs, err)
	}
	if h.wal != nil {
		if err := h.wal.Close(); err != nil {
			errs = append(errs, err)
		}
	}
	if h.wbl != nil {
		if err := h.wbl.Close(); err != nil {
			errs = append(errs, err)
		}
	}
	if len(errs) == 0 && h.opts.EnableMemorySnapshotOnShutdown {
		if err := h.performChunkSnapshot(); err != nil {
			errs = append(errs, err)
		}
	}
	return errors.Join(errs...)
}

// String returns an human readable representation of the TSDB head. It's important to
// keep this function in order to avoid the struct dump when the head is stringified in
// errors or logs.
func (*Head) String() string {
	return "head"

Review comment (refactor):
Here you can omit the test to simplify the code - `errors.Join` removes nils from the list.

Code after:

	// Remove deleted series IDs from the postings lists.
	h.postings.Delete(deleted, affected)

	// Remove tombstones referring to the deleted series.
	h.tombstones.DeleteTombstones(deleted)
	h.tombstones.TruncateBefore(mint)

	if h.wal != nil {
		h.walExpiriesMtx.Lock()
		// Samples for deleted series are likely still in the WAL, so flag that the deleted series records should be kept during
		// WAL checkpointing while the WAL contains data through actualInOrderMint.
		// If we didn't keep these series records then on start up when we replay the WAL, or any other code that reads the WAL,
		// wouldn't be able to use those samples since we would have no labels for that ref ID.
		for ref := range deleted {
			h.walExpiries[chunks.HeadSeriesRef(ref)] = actualInOrderMint
		}
		h.walExpiriesMtx.Unlock()
	}

	return actualInOrderMint, minOOOTime, minMmapFile
}

// Tombstones returns a new reader over the head's tombstones.
func (h *Head) Tombstones() (tombstones.Reader, error) {
	return h.tombstones, nil
}

// NumSeries returns the number of series tracked in the head.
func (h *Head) NumSeries() uint64 {
	return h.numSeries.Load()
}

// NumStaleSeries returns the number of stale series in the head.
func (h *Head) NumStaleSeries() uint64 {
	return h.numStaleSeries.Load()
}

var headULID = ulid.MustParse("0000000000XXXXXXXXXXXXHEAD")

// Meta returns meta information about the head.
// The head is dynamic so will return dynamic results.
func (h *Head) Meta() BlockMeta {
	return BlockMeta{
		MinTime: h.MinTime(),
		MaxTime: h.MaxTime(),
		ULID:    headULID,
		Stats: BlockStats{
			NumSeries: h.NumSeries(),
		},
	}