Files
ingot/internal/head/striped.go
T
david 42b03db2fa Immutable blocks with mmap reads
Flush sealed head chunks to ULID-named block directories on disk. Each
block contains CRC'd chunk segment files (mmap'd for reads), a binary
index (symbol table, series, postings with TOC), and a meta.json written
last as the immutability gate. WAL is truncated after block fsync,
preserving the crash-safety ording invariant.
2026-07-04 14:59:39 -04:00

106 lines
2.3 KiB
Go

package head
import (
"sync"
"git.dvdt.dev/david/ingot/labels"
)
const numStripes = 128
// seriesMap is a concurrent map of series, striped by label hash for hash
// lookups and by ref for ref lookups.
type seriesMap struct {
hashStripes [numStripes]hashStripe
refStripes [numStripes]refStripe
}
type hashStripe struct {
mu sync.RWMutex
m map[uint64][]*memSeries // label hash -> series (slice for collision)
}
type refStripe struct {
mu sync.RWMutex
m map[uint64]*memSeries
}
func newSeriesMap() *seriesMap {
sm := &seriesMap{}
for i := range sm.hashStripes {
sm.hashStripes[i].m = make(map[uint64][]*memSeries)
}
for i := range sm.refStripes {
sm.refStripes[i].m = make(map[uint64]*memSeries)
}
return sm
}
// getByRef returns the series with the given ref, or nil.
func (sm *seriesMap) getByRef(ref uint64) *memSeries {
s := &sm.refStripes[ref%numStripes]
s.mu.RLock()
defer s.mu.RUnlock()
return s.m[ref]
}
// getByHash returns the series with the given hash and labels, or nil.
func (sm *seriesMap) getByHash(hash uint64, ls []labels.Label) *memSeries {
s := &sm.hashStripes[hash%numStripes]
s.mu.RLock()
defer s.mu.RUnlock()
for _, series := range s.m[hash] {
if labels.Equal(series.labels, ls) {
return series
}
}
return nil
}
// set inserts a series into both maps. Caller must ensure the ref is unique.
func (sm *seriesMap) set(hash uint64, s *memSeries) {
hs := &sm.hashStripes[hash%numStripes]
hs.mu.Lock()
hs.m[hash] = append(hs.m[hash], s)
hs.mu.Unlock()
rs := &sm.refStripes[s.ref%numStripes]
rs.mu.Lock()
rs.m[s.ref] = s
rs.mu.Unlock()
}
// forEach calls fn for every series in the map. The series lock is NOT held.
func (sm *seriesMap) forEach(fn func(s *memSeries)) {
for i := range sm.refStripes {
rs := &sm.refStripes[i]
rs.mu.RLock()
for _, s := range rs.m {
fn(s)
}
rs.mu.RUnlock()
}
}
// remove deletes a series from both maps.
func (sm *seriesMap) remove(hash uint64, s *memSeries) {
hs := &sm.hashStripes[hash%numStripes]
hs.mu.Lock()
bucket := hs.m[hash]
for i, existing := range bucket {
if existing == s {
hs.m[hash] = append(bucket[:i], bucket[i+1:]...)
break
}
}
if len(hs.m[hash]) == 0 {
delete(hs.m, hash)
}
hs.mu.Unlock()
rs := &sm.refStripes[s.ref%numStripes]
rs.mu.Lock()
delete(rs.m, s.ref)
rs.mu.Unlock()
}