Golang Performance Optimization Report

Go

Overview

This report summarizes three notable performance optimization pull requests merged in the past two weeks across popular open-source Golang projects. Each PR targets a specific bottleneck — ranging from algorithmic complexity reduction to in-memory caching — and delivers measurable, significant speedups.


PR #1 — prometheus/prometheus · Merged Feb 16, 2026

tsdb: Optimize LabelValues API performance

Repository: prometheus/prometheus (55,000+ stars) PR: #18069 Author: mishraa-G | Merged by: bboreham


Problem

The FindIntersectingPostings function suffered from severe O(N) performance degradation when processing matchers with sparse intersections. If a matcher’s posting ID was far ahead of the current candidate (e.g., matcher at 1,000,000 while candidate was at 0), the function would call Next() approximately 1,000,000 times — resulting in query timeouts for large datasets.

Root Cause

The function relied on sequential iteration via Next() for each candidate posting, with no ability to skip ahead. The LabelValues API path was therefore dramatically slower than the Series API for equivalent queries.

Solution

Replace Next() calls with Seek() operations on the candidate iterator. This enables logarithmic-time skipping through non-matching ranges, mirroring the approach already used in the faster Series API.

Key Code Changes

Before — sequential Next() iteration:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// postings_with_index_heap.go
func (h *postingsWithIndexHeap) next() error {
    pi := (*h)[0]
    next := pi.p.Next()
    // ...
}

// In FindIntersectingPostings:
} else if err := h.next(); err != nil {
    return nil, err
}

After — logarithmic Seek() jumping:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// Renamed to seekHead() to clarify it operates only on the heap's top element
func (h *postingsWithIndexHeap) seekHead(val storage.SeriesRef) error {
    pi := (*h)[0]
    next := pi.p.Seek(val)  // Jumps directly to target value
    // ...
}

// In FindIntersectingPostings:
} else if err := h.seekHead(p.At()); err != nil {
    return nil, err
}

New benchmark for the sparse intersection case:

1
2
3
4
5
6
7
8
9
func BenchmarkLabelValues_SlowPath(b *testing.B) {
    // Reproduces regression: dense candidates + matcher far ahead = O(N) iteration
    // 100k+ series in candidate posting list, matcher positioned far ahead
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        _, err := idx.LabelValues(ctx, 0, math.MaxInt64, "label", matchers...)
        require.NoError(b, err)
    }
}

Benchmark Results

ScenarioBeforeAfterImprovement
Sparse intersection (new benchmark)~919,901 ns/op~836 ns/op99.91%
High-overlap (existing benchmark)2.718 ms/op1.408 ms/op48.20%

Review Highlights

Reviewer bboreham called it “a great find” and requested method consolidation and naming clarity. The author implemented the suggestions by renaming seek() to seekHead() and removing unused code paths. Verified with go test ./tsdb/index/... showing no regressions.


PR #2 — grafana/grafana · Merged Feb 10, 2026

Library Panels: Add a Folder Tree Cache for getAllHandler

Repository: grafana/grafana (65,000+ stars) PR: #117475 Author: RafaelPaulovic | Reviewed by: JohnnyQQQQ, renatolabs


Problem

Grafana instances with large deployments (14,000+ folders and 1,800+ library panels) experienced unacceptable latency when retrieving all library panels via getAllHandler. Response times reached up to 52 seconds on unified storage and 16 seconds on legacy storage — caused by repeated folder service calls for every element.

Root Cause

The getAllHandler made a separate folder service call for each library panel to resolve folder metadata. This produced N+1 query behavior proportional to the number of library panels, with no caching between requests.

Solution

Introduce a FolderTree data structure providing O(1) lookups for folder hierarchy data, backed by a request-scoped in-memory cache with a 30-second TTL, shared per user/organization combination.

Key Code Changes

New FolderTree data structure (pkg/services/folder/tree.go):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
type FolderTree struct {
    Nodes   []FolderNode
    Index   map[string]int  // UID → position for O(1) lookup
    IDIndex map[int64]int   // ID  → position for O(1) lookup
}

type FolderNode struct {
    ID         int64
    UID        string
    Title      string
    Parent     int      // index into Nodes slice
    Children   []int    // indices into Nodes slice
    Accessible bool
}

Tree construction and key methods:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// NewFolderTree builds the tree from a flat folder slice,
// inserts "General" root at index 0, and wires parent-child relationships.
func NewFolderTree(folders []*folder.Folder) *FolderTree { ... }

// Ancestors returns an iterator yielding all accessible parent folders
// from immediate parent up to root — used for permission checks.
func (ft *FolderTree) Ancestors(uid string) iter.Seq[FolderNode] { ... }

// Children performs a breadth-first traversal yielding all descendants.
func (ft *FolderTree) Children(uid string) iter.Seq[FolderNode] { ... }

// Contains and GetTitle provide O(1) accessibility and title lookups.
func (ft *FolderTree) Contains(uid string) bool { ... }
func (ft *FolderTree) GetTitle(uid string) string { ... }

Cache layer (pkg/services/libraryelements/cache.go):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// 30-second TTL cache keyed by orgID+userUID
type folderTreeCache struct {
    mu    sync.RWMutex
    store map[cacheKey]*cachedTree
}

type cachedTree struct {
    tree      *folder.FolderTree
    expiresAt time.Time
}

Performance Results

Request TypeBeforeAfter (1st request)After (2nd request)
Unified Storage52.3s8.4s0.4s
Legacy Storage16.4s6.9s0.4s

Review Highlights

The PR was approved by JohnnyQQQQ and renatolabs after discussion on cache scope — ultimately made global with fallback support for cache misses. Successfully backported to release-12.3.3.


PR #3 — grafana/grafana · Merged Feb 9, 2026

Alerting: Improve Performance of Folder Access Checks

Repository: grafana/grafana (65,000+ stars) PR: #117080 Author: alexander-akhmetov | Milestone: 12.4.x


Problem

In Grafana’s alerting system, every folder access permission check triggered a call to the database-backed scope resolver. For high-frequency alerting rule evaluations, this produced unnecessary database round-trips even when the required folder hierarchy information was already available in memory.

Root Cause

The HasAccessInFolder() and AuthorizeAccessInFolder() functions always delegated to the database-backed access control evaluators with no fast path for cases where folder fullpath UIDs were pre-computed and accessible locally.

Solution

Convert Namespace from a type alias to a struct carrying a FullpathUIDs field. Introduce checkFolderAccessByFullpath() as an in-memory fast path — falling back to the database only when fullpath data is unavailable.

Key Code Changes

Updated Namespace struct (adding fullpath data):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// Before: Namespace was a simple type alias
type Namespace = models.AlertRuleGroup

// After: Namespace is a struct with in-memory hierarchy data
type Namespace struct {
    models.AlertRuleGroup
    FullpathUIDs string  // pre-computed folder hierarchy, e.g. "uid1/uid2/uid3"
}

// New constructor helpers
func NewNamespace(group models.AlertRuleGroup) Namespace { ... }
func NewNamespaceUID(uid string) Namespace { ... }

New interface for in-memory permission checks:

1
2
3
4
5
// NamespacedWithFullpath allows callers to access folder hierarchy data
type NamespacedWithFullpath interface {
    models.Namespaced
    GetFullpathUIDs() string
}

New fast-path function checkFolderAccessByFullpath:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
func checkFolderAccessByFullpath(user identity.Requester, rule models.Namespaced) bool {
    nf, ok := rule.(models.NamespacedWithFullpath)
    if !ok {
        return false  // no fullpath data, must fall back to DB
    }
    fullpath := nf.GetFullpathUIDs()
    if fullpath == "" {
        return false
    }
    // Validate permissions against target folder and all parent folders
    // using pre-computed UIDs — no database call needed
    // Returns true only if user has required permissions for all ancestors
    return evaluateLocalPermissions(user, fullpath)
}

Updated permission check with fast path:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
func (s *RuleService) HasAccessInFolder(ctx context.Context,
    user identity.Requester, rule models.Namespaced) (bool, error) {

    // Fast path: check in-memory using fullpath UIDs
    if checkFolderAccessByFullpath(user, rule) {
        return true, nil  // skip database entirely
    }

    // Slow path: fall back to database-backed scope resolver
    return s.accessControl.Evaluate(ctx, user, accesscontrol.EvalAll(
        accesscontrol.EvalPermission(ruleRead, folderScope),
    ))
}

Database query updated to include fullpath UIDs:

1
2
3
4
5
// Queries now request fullpath data to enable the fast path
folders, err := s.folderService.GetFolders(ctx, folder.GetFoldersQuery{
    WithFullpathUIDs: true,  // new field
    OrgID:            orgID,
})

Impact

Eliminates database calls for the common case where folder hierarchy data is pre-computed, significantly reducing latency in alerting rule evaluation loops. Reviewer Yuri Tseretian commented: “This is a great improvement.”

Comprehensive test coverage was added for: permissions on target/parent folders, deeply nested hierarchies, mixed permission scenarios, and fallback behavior when fullpath UIDs are unavailable.


Summary Table

#RepositoryPRTitleStarsMergedKey Improvement
1prometheus/prometheus#18069tsdb: Optimize LabelValues API performance55k+Feb 16, 202699.91% faster (O(N)→O(log N)) via Seek()
2grafana/grafana#117475Library Panels: folder tree cache for getAllHandler65k+Feb 10, 202652s → 0.4s via O(1) FolderTree cache
3grafana/grafana#117080Alerting: Improve performance of folder access checks65k+Feb 9, 2026Eliminates DB calls via in-memory fullpath evaluation

Report generated: 2026-02-18