mirror of
https://github.com/hanzoai/traces.git
synced 2026-10-30 18:20:41 +00:00
* Update dskit for gRPC interface change * Update vendor * Update vendor * bump dskit * update go.sum * manifest --------- Co-authored-by: javiermolinar <[email protected]>
119 lines
3.4 KiB
Go
119 lines
3.4 KiB
Go
package ring
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
|
|
"github.com/go-kit/log"
|
|
"github.com/go-kit/log/level"
|
|
"github.com/pkg/errors"
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
|
|
|
"github.com/grafana/dskit/kv"
|
|
"github.com/grafana/dskit/services"
|
|
)
|
|
|
|
// PartitionRingWatcher watches the partitions ring for changes in the KV store.
|
|
type PartitionRingWatcher struct {
|
|
services.Service
|
|
|
|
key string
|
|
kv kv.Client
|
|
delegate PartitionRingWatcherDelegate
|
|
logger log.Logger
|
|
|
|
ringMx sync.Mutex
|
|
ring *PartitionRing
|
|
|
|
// Metrics.
|
|
numPartitionsGaugeVec *prometheus.GaugeVec
|
|
}
|
|
|
|
type PartitionRingWatcherDelegate interface {
|
|
// OnPartitionRingChanged provides the old and new partition ring descriptions, which must not be modified.
|
|
OnPartitionRingChanged(oldRing, newRing *PartitionRingDesc)
|
|
}
|
|
|
|
func NewPartitionRingWatcher(name, key string, kv kv.Client, logger log.Logger, reg prometheus.Registerer) *PartitionRingWatcher {
|
|
r := &PartitionRingWatcher{
|
|
key: key,
|
|
kv: kv,
|
|
logger: logger,
|
|
ring: NewPartitionRing(*NewPartitionRingDesc()),
|
|
numPartitionsGaugeVec: promauto.With(reg).NewGaugeVec(prometheus.GaugeOpts{
|
|
Name: "partition_ring_partitions",
|
|
Help: "Number of partitions by state in the partitions ring.",
|
|
ConstLabels: map[string]string{"name": name},
|
|
}, []string{"state"}),
|
|
}
|
|
|
|
r.Service = services.NewBasicService(r.starting, r.loop, nil).WithName("partitions-ring-watcher")
|
|
return r
|
|
}
|
|
|
|
// WithDelegate adds the delegate to be called when the partition ring changes.
|
|
//
|
|
// Not concurrency safe.
|
|
func (w *PartitionRingWatcher) WithDelegate(delegate PartitionRingWatcherDelegate) *PartitionRingWatcher {
|
|
w.delegate = delegate
|
|
return w
|
|
}
|
|
|
|
func (w *PartitionRingWatcher) starting(ctx context.Context) error {
|
|
// Get the initial ring state so that, as soon as the service will be running, the in-memory
|
|
// ring would be already populated and there's no race condition between when the service is
|
|
// running and the WatchKey() callback is called for the first time.
|
|
value, err := w.kv.Get(ctx, w.key)
|
|
if err != nil {
|
|
return errors.Wrap(err, "unable to initialise ring state")
|
|
}
|
|
|
|
if value == nil {
|
|
level.Info(w.logger).Log("msg", "partition ring doesn't exist in KV store yet")
|
|
value = NewPartitionRingDesc()
|
|
}
|
|
|
|
w.updatePartitionRing(value.(*PartitionRingDesc))
|
|
return nil
|
|
}
|
|
|
|
func (w *PartitionRingWatcher) loop(ctx context.Context) error {
|
|
w.kv.WatchKey(ctx, w.key, func(value interface{}) bool {
|
|
if value == nil {
|
|
level.Info(w.logger).Log("msg", "partition ring doesn't exist in KV store yet")
|
|
return true
|
|
}
|
|
|
|
w.updatePartitionRing(value.(*PartitionRingDesc))
|
|
return true
|
|
})
|
|
return nil
|
|
}
|
|
|
|
func (w *PartitionRingWatcher) updatePartitionRing(desc *PartitionRingDesc) {
|
|
newRing := NewPartitionRing(*desc)
|
|
w.ringMx.Lock()
|
|
oldRing := w.ring
|
|
w.ring = newRing
|
|
w.ringMx.Unlock()
|
|
|
|
if w.delegate != nil {
|
|
w.delegate.OnPartitionRingChanged(&oldRing.desc, desc)
|
|
}
|
|
|
|
// Update metrics.
|
|
for state, count := range desc.countPartitionsByState() {
|
|
w.numPartitionsGaugeVec.WithLabelValues(state.CleanName()).Set(float64(count))
|
|
}
|
|
}
|
|
|
|
// PartitionRing returns the most updated snapshot of the PartitionRing. The returned instance
|
|
// is immutable and will not be updated if new changes are done to the ring.
|
|
func (w *PartitionRingWatcher) PartitionRing() *PartitionRing {
|
|
w.ringMx.Lock()
|
|
defer w.ringMx.Unlock()
|
|
|
|
return w.ring
|
|
}
|