Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 15 additions & 20 deletions internal/stacktrace/trie.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,9 +145,14 @@ func (t *prefixTrie) Clear() {
// segmentPrefixTrie is a path segment-based trie optimized for "/" delimited paths.
// It stores path segments (e.g., "github.com", "DataDog") as nodes instead of individual characters,
// providing better memory efficiency and potentially faster lookups for module paths.
//
// Concurrency: This trie follows a write-once-read-many (WORM) pattern where all writes
// occur during package initialization (init function) before any concurrent access begins.
// After initialization, the trie is effectively immutable and can be safely read by multiple
// goroutines without synchronization.
type segmentPrefixTrie struct {
root *segmentTrieNode
mu sync.RWMutex
// No mutex needed - structure is immutable after init()
}

// segmentTrieNode represents a single path segment node in the trie
Expand All @@ -165,15 +170,13 @@ func newSegmentPrefixTrie() *segmentPrefixTrie {
}
}

// Insert adds a prefix to the segment trie
// Insert adds a prefix to the segment trie.
// This method should only be called during initialization before any concurrent access.
func (t *segmentPrefixTrie) Insert(prefix string) {
if prefix == "" {
return
}

t.mu.Lock()
defer t.mu.Unlock()

node := t.root
start := 0

Expand All @@ -197,14 +200,12 @@ func (t *segmentPrefixTrie) Insert(prefix string) {
}

// HasPrefix checks if the given string has any of the prefixes stored in the segment trie.
// Safe for concurrent use after initialization.
func (t *segmentPrefixTrie) HasPrefix(s string) (found bool) {
if s == "" {
return false
}

t.mu.RLock()
defer t.mu.RUnlock()

node := t.root
start := 0

Expand All @@ -230,11 +231,9 @@ func (t *segmentPrefixTrie) HasPrefix(s string) (found bool) {
return node.isEnd
}

// InsertAll adds multiple prefixes to the segment trie in a single operation
// InsertAll adds multiple prefixes to the segment trie in a single operation.
// This method should only be called during initialization before any concurrent access.
func (t *segmentPrefixTrie) InsertAll(prefixes []string) {
t.mu.Lock()
defer t.mu.Unlock()

for _, prefix := range prefixes {
if prefix == "" {
continue
Expand Down Expand Up @@ -263,11 +262,9 @@ func (t *segmentPrefixTrie) InsertAll(prefixes []string) {
}
}

// Size returns the number of prefixes stored in the segment trie
// Size returns the number of prefixes stored in the segment trie.
// Safe for concurrent use after initialization.
func (t *segmentPrefixTrie) Size() int {
t.mu.RLock()
defer t.mu.RUnlock()

return t.countSegmentPrefixes(t.root)
}

Expand All @@ -289,11 +286,9 @@ func (t *segmentPrefixTrie) countSegmentPrefixes(node *segmentTrieNode) int {
return count
}

// Clear removes all prefixes from the segment trie
// Clear removes all prefixes from the segment trie.
// This method should only be called during initialization before any concurrent access.
func (t *segmentPrefixTrie) Clear() {
t.mu.Lock()
defer t.mu.Unlock()

t.root = &segmentTrieNode{
children: make(map[string]*segmentTrieNode),
}
Expand Down
Loading