Skip to content
Draft
Show file tree
Hide file tree
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
134 changes: 80 additions & 54 deletions db/state/changeset/state_changeset.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"math"
"strings"
"sync"
"sync/atomic"
"time"

"github.com/erigontech/erigon/common"
Expand Down Expand Up @@ -449,76 +450,101 @@ func ReadLowestUnwindableBlock(tx kv.Tx) (uint64, error) {

}

// DomainIOMetrics fields are atomic so concurrent readers (exec workers + the
// tip warmup pool) record without a shared mutex. Durations are nanoseconds.
type DomainIOMetrics struct {
CacheReadCount atomic.Int64
CacheReadDuration atomic.Int64
CacheGetCount atomic.Int64
CachePutCount atomic.Int64
CacheGetSize atomic.Int64
CacheGetKeySize atomic.Int64
CacheGetValueSize atomic.Int64
CachePutSize atomic.Int64
CachePutKeySize atomic.Int64
CachePutValueSize atomic.Int64
DbReadCount atomic.Int64
DbReadDuration atomic.Int64
FileReadCount atomic.Int64
FileReadDuration atomic.Int64
}

// DomainIOSnapshot is a plain, copyable point-in-time read of DomainIOMetrics
// for delta computation by consumers.
type DomainIOSnapshot struct {
CacheReadCount int64
CacheReadDuration time.Duration
CacheReadDuration int64
CacheGetCount int64
CachePutCount int64
CacheGetSize int
CacheGetKeySize int
CacheGetValueSize int
CachePutSize int
CachePutKeySize int
CachePutValueSize int
CacheGetSize int64
CacheGetKeySize int64
CacheGetValueSize int64
CachePutSize int64
CachePutKeySize int64
CachePutValueSize int64
DbReadCount int64
DbReadDuration time.Duration
DbReadDuration int64
FileReadCount int64
FileReadDuration time.Duration
FileReadDuration int64
}

func (m *DomainIOMetrics) snapshot() DomainIOSnapshot {
return DomainIOSnapshot{
CacheReadCount: m.CacheReadCount.Load(),
CacheReadDuration: m.CacheReadDuration.Load(),
CacheGetCount: m.CacheGetCount.Load(),
CachePutCount: m.CachePutCount.Load(),
CacheGetSize: m.CacheGetSize.Load(),
CacheGetKeySize: m.CacheGetKeySize.Load(),
CacheGetValueSize: m.CacheGetValueSize.Load(),
CachePutSize: m.CachePutSize.Load(),
CachePutKeySize: m.CachePutKeySize.Load(),
CachePutValueSize: m.CachePutValueSize.Load(),
DbReadCount: m.DbReadCount.Load(),
DbReadDuration: m.DbReadDuration.Load(),
FileReadCount: m.FileReadCount.Load(),
FileReadDuration: m.FileReadDuration.Load(),
}
}

type DomainMetrics struct {
sync.RWMutex
DomainIOMetrics
Domains map[kv.Domain]*DomainIOMetrics
DomainIOMetrics // aggregate across all domains
Domains [kv.DomainLen]DomainIOMetrics // per-domain, indexed by kv.Domain
}

func (dm *DomainMetrics) UpdateCacheReads(domain kv.Domain, start time.Time) {
dm.Lock()
defer dm.Unlock()
dm.CacheReadCount++
readDuration := time.Since(start)
dm.CacheReadDuration += readDuration
if d, ok := dm.Domains[domain]; ok {
d.CacheReadCount++
d.CacheReadDuration += readDuration
} else {
dm.Domains[domain] = &DomainIOMetrics{
CacheReadCount: 1,
CacheReadDuration: readDuration,
}
type DomainMetricsSnapshot struct {
DomainIOSnapshot
Domains [kv.DomainLen]DomainIOSnapshot
}

func (dm *DomainMetrics) Snapshot() DomainMetricsSnapshot {
s := DomainMetricsSnapshot{DomainIOSnapshot: dm.DomainIOMetrics.snapshot()}
for i := range dm.Domains {
s.Domains[i] = dm.Domains[i].snapshot()
}
return s
}

func (dm *DomainMetrics) UpdateCacheReads(domain kv.Domain, start time.Time) {
d := int64(time.Since(start))
dm.CacheReadCount.Add(1)
dm.CacheReadDuration.Add(d)
dm.Domains[domain].CacheReadCount.Add(1)
dm.Domains[domain].CacheReadDuration.Add(d)
}

func (dm *DomainMetrics) UpdateDbReads(domain kv.Domain, start time.Time) {
dm.Lock()
defer dm.Unlock()
dm.DbReadCount++
readDuration := time.Since(start)
dm.DbReadDuration += readDuration
if d, ok := dm.Domains[domain]; ok {
d.DbReadCount++
d.DbReadDuration += readDuration
} else {
dm.Domains[domain] = &DomainIOMetrics{
DbReadCount: 1,
DbReadDuration: readDuration,
}
}
d := int64(time.Since(start))
dm.DbReadCount.Add(1)
dm.DbReadDuration.Add(d)
dm.Domains[domain].DbReadCount.Add(1)
dm.Domains[domain].DbReadDuration.Add(d)
}

func (dm *DomainMetrics) UpdateFileReads(domain kv.Domain, start time.Time) {
dm.Lock()
defer dm.Unlock()
dm.FileReadCount++
readDuration := time.Since(start)
dm.FileReadDuration += readDuration
if d, ok := dm.Domains[domain]; ok {
d.FileReadCount++
d.FileReadDuration += readDuration
} else {
dm.Domains[domain] = &DomainIOMetrics{
FileReadCount: 1,
FileReadDuration: readDuration,
}
}
d := int64(time.Since(start))
dm.FileReadCount.Add(1)
dm.FileReadDuration.Add(d)
dm.Domains[domain].FileReadCount.Add(1)
dm.Domains[domain].FileReadDuration.Add(d)
}
10 changes: 1 addition & 9 deletions db/state/domain.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ import (
"github.com/erigontech/erigon/db/state/changeset"
"github.com/erigontech/erigon/db/state/statecfg"
"github.com/erigontech/erigon/db/version"
"github.com/erigontech/erigon/diagnostics/metrics"
)

var (
Expand Down Expand Up @@ -498,16 +497,9 @@ type DomainRoTx struct {
getFromFileCache *DomainGetFromFileCache
}

func domainReadMetric(name kv.Domain, level int) metrics.Summary {
if level > 4 {
level = 5
}
return mxsKVGet[name][level]
}

func (dt *DomainRoTx) getLatestFromFile(i int, filekey []byte, hi, lo uint64) (v []byte, ok bool, offset uint64, err error) {
if dbg.KVReadLevelledMetrics {
defer domainReadMetric(dt.name, i).ObserveDuration(time.Now())
defer recordKVGet(dt.name, i, time.Now())
}

if dt.d.Accessors.Has(statecfg.AccessorBTree) {
Expand Down
36 changes: 17 additions & 19 deletions db/state/execctx/domain_shared.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,6 @@ func NewSharedDomains(ctx context.Context, tx kv.TemporalTx, logger log.Logger,
sd := &SharedDomains{
logger: logger,
//trace: true,
metrics: changeset.DomainMetrics{Domains: map[kv.Domain]*changeset.DomainIOMetrics{}},
stepSize: tx.Debug().StepSize(),
}

Expand Down Expand Up @@ -767,24 +766,23 @@ func (sd *SharedDomains) Metrics() *changeset.DomainMetrics {
func (sd *SharedDomains) LogMetrics() []any {
var metrics []any

sd.metrics.RLock()
defer sd.metrics.RUnlock()
m := sd.metrics.Snapshot()

if readCount := sd.metrics.CacheReadCount; readCount > 0 {
if readCount := m.CacheReadCount; readCount > 0 {
metrics = append(metrics, "cache", common.PrettyCounter(readCount),
"puts", common.PrettyCounter(sd.metrics.CachePutCount),
"puts", common.PrettyCounter(m.CachePutCount),
"size", fmt.Sprintf("%s(%s/%s)",
common.PrettyCounter(sd.metrics.CachePutSize), common.PrettyCounter(sd.metrics.CachePutKeySize), common.PrettyCounter(sd.metrics.CachePutValueSize)),
"gets", common.PrettyCounter(sd.metrics.CacheGetCount), "size", common.PrettyCounter(sd.metrics.CacheGetSize),
"cdur", common.Round(sd.metrics.CacheReadDuration/time.Duration(readCount), 0))
common.PrettyCounter(m.CachePutSize), common.PrettyCounter(m.CachePutKeySize), common.PrettyCounter(m.CachePutValueSize)),
"gets", common.PrettyCounter(m.CacheGetCount), "size", common.PrettyCounter(m.CacheGetSize),
"cdur", common.Round(time.Duration(m.CacheReadDuration)/time.Duration(readCount), 0))
}

if readCount := sd.metrics.DbReadCount; readCount > 0 {
metrics = append(metrics, "db", common.PrettyCounter(readCount), "dbdur", common.Round(sd.metrics.DbReadDuration/time.Duration(readCount), 0))
if readCount := m.DbReadCount; readCount > 0 {
metrics = append(metrics, "db", common.PrettyCounter(readCount), "dbdur", common.Round(time.Duration(m.DbReadDuration)/time.Duration(readCount), 0))
}

if readCount := sd.metrics.FileReadCount; readCount > 0 {
metrics = append(metrics, "files", common.PrettyCounter(readCount), "fdur", common.Round(sd.metrics.FileReadDuration/time.Duration(readCount), 0))
if readCount := m.FileReadCount; readCount > 0 {
metrics = append(metrics, "files", common.PrettyCounter(readCount), "fdur", common.Round(time.Duration(m.FileReadDuration)/time.Duration(readCount), 0))
}

return metrics
Expand All @@ -793,26 +791,26 @@ func (sd *SharedDomains) LogMetrics() []any {
func (sd *SharedDomains) DomainLogMetrics() map[kv.Domain][]any {
var logMetrics = map[kv.Domain][]any{}

sd.metrics.RLock()
defer sd.metrics.RUnlock()
snap := sd.metrics.Snapshot()

for domain, dm := range sd.metrics.Domains {
for d := range snap.Domains {
dm := snap.Domains[d]
var metrics []any

if readCount := dm.CacheReadCount; readCount > 0 {
metrics = append(metrics, "cache", common.PrettyCounter(readCount), "cdur", common.Round(dm.CacheReadDuration/time.Duration(readCount), 0))
metrics = append(metrics, "cache", common.PrettyCounter(readCount), "cdur", common.Round(time.Duration(dm.CacheReadDuration)/time.Duration(readCount), 0))
}

if readCount := dm.DbReadCount; readCount > 0 {
metrics = append(metrics, "db", common.PrettyCounter(readCount), "dbdur", common.Round(dm.DbReadDuration/time.Duration(readCount), 0))
metrics = append(metrics, "db", common.PrettyCounter(readCount), "dbdur", common.Round(time.Duration(dm.DbReadDuration)/time.Duration(readCount), 0))
}

if readCount := dm.FileReadCount; readCount > 0 {
metrics = append(metrics, "files", common.PrettyCounter(readCount), "fdur", common.Round(dm.DbReadDuration/time.Duration(readCount), 0))
metrics = append(metrics, "files", common.PrettyCounter(readCount), "fdur", common.Round(time.Duration(dm.FileReadDuration)/time.Duration(readCount), 0))
}

if len(metrics) > 0 {
logMetrics[domain] = metrics
logMetrics[kv.Domain(d)] = metrics
}
}

Expand Down
22 changes: 22 additions & 0 deletions db/state/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,32 @@
package state

import (
"sync/atomic"
"time"

"github.com/erigontech/erigon/db/kv"
"github.com/erigontech/erigon/diagnostics/metrics"
)

// kvReadAcc is a lock-free per-(domain,level) read-time accumulator. Under the
// tip warmup pool the per-read prometheus Summary mutex serializes hundreds of
// concurrent readers; plain atomics avoid that contention.
type kvReadAcc struct {
count atomic.Int64
nanos atomic.Int64
}

var mxsKVGetLF [kv.DomainLen][6]kvReadAcc

func recordKVGet(name kv.Domain, level int, start time.Time) {
if level > 4 {
level = 5
}
a := &mxsKVGetLF[name][level]
a.count.Add(1)
a.nanos.Add(int64(time.Since(start)))
}

var (
//LatestStateReadWarm = metrics.GetOrCreateSummary(`latest_state_read{type="warm",found="yes"}`) //nolint
//LatestStateReadWarmNotFound = metrics.GetOrCreateSummary(`latest_state_read{type="warm",found="no"}`) //nolint
Expand Down
49 changes: 18 additions & 31 deletions db/state/temporal_mem_batch.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,24 +151,12 @@ func (sd *TemporalMemBatch) putLatest(domain kv.Domain, key string, val []byte,
defer sd.latestStateLock.Unlock()

var updateMetrics = func(domain kv.Domain, putKeySize int, putValueSize int) {
sd.metrics.Lock()
defer sd.metrics.Unlock()
sd.metrics.CachePutCount++
sd.metrics.CachePutSize += putKeySize + putValueSize
sd.metrics.CachePutKeySize += putKeySize
sd.metrics.CachePutValueSize += putValueSize
if dm, ok := sd.metrics.Domains[domain]; ok {
dm.CachePutCount++
dm.CachePutSize += putKeySize + putValueSize
dm.CachePutKeySize += putKeySize
dm.CachePutValueSize += putValueSize
} else {
sd.metrics.Domains[domain] = &changeset.DomainIOMetrics{
CachePutCount: 1,
CachePutSize: putKeySize + putValueSize,
CachePutKeySize: putKeySize,
CachePutValueSize: putValueSize,
}
putSize := int64(putKeySize + putValueSize)
for _, m := range []*changeset.DomainIOMetrics{&sd.metrics.DomainIOMetrics, &sd.metrics.Domains[domain]} {
m.CachePutCount.Add(1)
m.CachePutSize.Add(putSize)
m.CachePutKeySize.Add(int64(putKeySize))
m.CachePutValueSize.Add(int64(putValueSize))
}
}

Expand Down Expand Up @@ -327,7 +315,7 @@ func (sd *TemporalMemBatch) GetAsOf(domain kv.Domain, key []byte, ts uint64) (v
func (sd *TemporalMemBatch) SizeEstimate() uint64 {
sd.latestStateLock.RLock()
defer sd.latestStateLock.RUnlock()
return uint64(sd.metrics.CachePutSize)
return uint64(sd.metrics.CachePutSize.Load())
}

func (sd *TemporalMemBatch) ClearRam() {
Expand All @@ -342,18 +330,17 @@ func (sd *TemporalMemBatch) ClearRam() {
sd.unwindChangeset = nil
sd.unwindChangesetRaw = nil

sd.metrics.Lock()
defer sd.metrics.Unlock()
sd.metrics.CachePutCount = 0
sd.metrics.CachePutSize = 0
sd.metrics.CachePutKeySize = 0
sd.metrics.CachePutValueSize = 0
for _, dm := range sd.metrics.Domains {
dm.CachePutCount = 0
dm.CachePutSize = 0
dm.CachePutKeySize = 0
dm.CachePutValueSize = 0
}
for i := range sd.metrics.Domains {
m := &sd.metrics.Domains[i]
m.CachePutCount.Store(0)
m.CachePutSize.Store(0)
m.CachePutKeySize.Store(0)
m.CachePutValueSize.Store(0)
}
sd.metrics.CachePutCount.Store(0)
sd.metrics.CachePutSize.Store(0)
sd.metrics.CachePutKeySize.Store(0)
sd.metrics.CachePutValueSize.Store(0)
}

func (sd *TemporalMemBatch) IteratePrefix(domain kv.Domain, prefix []byte, roTx kv.Tx, it func(k []byte, v []byte) (cont bool, err error)) error {
Expand Down
Loading
Loading