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
69 changes: 65 additions & 4 deletions pkg/feeds/factory/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,88 @@
package factory

import (
"context"
"errors"
"time"

"github.com/ethersphere/bee/v2/pkg/feeds"
"github.com/ethersphere/bee/v2/pkg/feeds/epochs"
"github.com/ethersphere/bee/v2/pkg/feeds/sequence"
m "github.com/ethersphere/bee/v2/pkg/metrics"
storage "github.com/ethersphere/bee/v2/pkg/storage"
"github.com/ethersphere/bee/v2/pkg/swarm"
)

var _ m.Collector = (*factory)(nil)

type factory struct {
storage.Getter
metrics metrics
}

func New(getter storage.Getter) feeds.Factory {
return &factory{getter}
return &factory{
Getter: getter,
metrics: newMetrics(),
}
}

func (f *factory) NewLookup(t feeds.Type, feed *feeds.Feed) (feeds.Lookup, error) {
var lookup feeds.Lookup
switch t {
case feeds.Sequence:
return sequence.NewAsyncFinder(f.Getter, feed), nil
lookup = sequence.NewAsyncFinder(f.Getter, feed)
case feeds.Epoch:
return epochs.NewAsyncFinder(f.Getter, feed), nil
lookup = epochs.NewAsyncFinder(f.Getter, feed)
default:
return nil, feeds.ErrFeedTypeNotFound
}

return f.wrapLookup(t, lookup), nil
}

func (f *factory) wrapLookup(t feeds.Type, lookup feeds.Lookup) feeds.Lookup {
return &instrumentedLookup{
lookup: lookup,
typ: lookupType(t),
m: f.metrics,
}
}

type instrumentedLookup struct {
lookup feeds.Lookup
typ string
m metrics
}

return nil, feeds.ErrFeedTypeNotFound
func (l *instrumentedLookup) At(ctx context.Context, at int64, after uint64) (swarm.Chunk, feeds.Index, feeds.Index, error) {
l.m.LookupStarted.WithLabelValues(l.typ).Inc()
start := time.Now()
ch, cur, next, err := l.lookup.At(ctx, at, after)
l.m.LookupDuration.WithLabelValues(l.typ, lookupResult(ch, err)).Observe(time.Since(start).Seconds())
return ch, cur, next, err
}

func lookupType(t feeds.Type) string {
switch t {
case feeds.Sequence:
return "sequence"
case feeds.Epoch:
return "epoch"
default:
return "unknown"
}
}

func lookupResult(ch swarm.Chunk, err error) string {
switch {
case err != nil && errors.Is(err, context.Canceled):
return "canceled"
case err != nil:
return "error"
case ch == nil:
return "not_found"
default:
return "found"
}
}
49 changes: 49 additions & 0 deletions pkg/feeds/factory/metrics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright 2026 The Swarm Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package factory

import (
"github.com/prometheus/client_golang/prometheus"

m "github.com/ethersphere/bee/v2/pkg/metrics"
)

type metrics struct {
// all metrics fields must be exported
// to be able to return them by Metrics()
// using reflection
LookupStarted *prometheus.CounterVec
LookupDuration *prometheus.HistogramVec
}

func newMetrics() metrics {
subsystem := "feeds"

return metrics{
LookupStarted: prometheus.NewCounterVec(
prometheus.CounterOpts{
Namespace: m.Namespace,
Subsystem: subsystem,
Name: "lookup_started_total",
Help: "Number of feed lookup attempts started.",
},
[]string{"type"},
),
LookupDuration: prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Namespace: m.Namespace,
Subsystem: subsystem,
Name: "lookup_duration_seconds",
Help: "Histogram of feed lookup durations.",
Buckets: []float64{0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, 15, 30, 60},
},
[]string{"type", "result"},
),
}
}

func (f *factory) Metrics() []prometheus.Collector {
return m.PrometheusCollectorsFromFields(f.metrics)
}
1 change: 1 addition & 0 deletions pkg/node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -1414,6 +1414,7 @@ func NewBee(
apiService.MustRegisterMetrics(retrieval.Metrics()...)
apiService.MustRegisterMetrics(lightNodes.Metrics()...)
apiService.MustRegisterMetrics(hive.Metrics()...)
apiService.MustRegisterMetrics(feedFactory.(metrics.Collector).Metrics()...)

if bs, ok := batchStore.(metrics.Collector); ok {
apiService.MustRegisterMetrics(bs.Metrics()...)
Expand Down
Loading