github.com/thanos-io/thanos@v0.32.5/pkg/extprom/tx_gauge.go (about)

     1  // Copyright (c) The Thanos Authors.
     2  // Licensed under the Apache License 2.0.
     3  
     4  package extprom
     5  
     6  import (
     7  	"sync"
     8  
     9  	"github.com/prometheus/client_golang/prometheus"
    10  	"github.com/prometheus/client_golang/prometheus/promauto"
    11  )
    12  
    13  type TxGaugeVec struct {
    14  	current      *prometheus.GaugeVec
    15  	mtx          sync.Mutex
    16  	newMetricVal func() *prometheus.GaugeVec
    17  
    18  	tx *prometheus.GaugeVec
    19  }
    20  
    21  // NewTxGaugeVec is a prometheus.GaugeVec that allows to start atomic metric value transaction.
    22  // It might be useful if long process that wants to update a GaugeVec but wants to build/accumulate those metrics
    23  // in a concurrent way without exposing partial state to Prometheus.
    24  // Caller can also use this as normal GaugeVec.
    25  //
    26  // Additionally it allows to init LabelValues on each transaction.
    27  // NOTE: This is quite naive implementation creating new prometheus.GaugeVec on each `ResetTx`, use wisely.
    28  func NewTxGaugeVec(reg prometheus.Registerer, opts prometheus.GaugeOpts, labelNames []string, initLabelValues ...[]string) *TxGaugeVec {
    29  	// Nil as we will register it on our own later.
    30  	f := func() *prometheus.GaugeVec {
    31  		g := promauto.With(nil).NewGaugeVec(opts, labelNames)
    32  		for _, vals := range initLabelValues {
    33  			g.WithLabelValues(vals...)
    34  		}
    35  		return g
    36  	}
    37  	tx := &TxGaugeVec{
    38  		current:      f(),
    39  		newMetricVal: f,
    40  	}
    41  	if reg != nil {
    42  		reg.MustRegister(tx)
    43  	}
    44  	return tx
    45  }
    46  
    47  // ResetTx starts new transaction. Not goroutine-safe.
    48  func (tx *TxGaugeVec) ResetTx() {
    49  	tx.tx = tx.newMetricVal()
    50  }
    51  
    52  // Submit atomically and fully applies new values from existing transaction GaugeVec. Not goroutine-safe.
    53  func (tx *TxGaugeVec) Submit() {
    54  	if tx.tx == nil {
    55  		return
    56  	}
    57  
    58  	tx.mtx.Lock()
    59  	tx.current = tx.tx
    60  	tx.mtx.Unlock()
    61  }
    62  
    63  // Describe is used in Register.
    64  func (tx *TxGaugeVec) Describe(ch chan<- *prometheus.Desc) {
    65  	tx.mtx.Lock()
    66  	defer tx.mtx.Unlock()
    67  
    68  	tx.current.Describe(ch)
    69  }
    70  
    71  // Collect is used by Registered.
    72  func (tx *TxGaugeVec) Collect(ch chan<- prometheus.Metric) {
    73  	tx.mtx.Lock()
    74  	defer tx.mtx.Unlock()
    75  
    76  	tx.current.Collect(ch)
    77  }
    78  
    79  // With works as GetMetricWith, but panics where GetMetricWithLabels would have
    80  // returned an error. Not returning an error allows shortcuts like
    81  //
    82  //	myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Add(42)
    83  func (tx *TxGaugeVec) With(labels prometheus.Labels) prometheus.Gauge {
    84  	if tx.tx == nil {
    85  		tx.ResetTx()
    86  	}
    87  	return tx.tx.With(labels)
    88  }
    89  
    90  // WithLabelValues works as GetMetricWithLabelValues, but panics where
    91  // GetMetricWithLabelValues would have returned an error. Not returning an
    92  // error allows shortcuts like
    93  //
    94  //	myVec.WithLabelValues("404", "GET").Add(42)
    95  func (tx *TxGaugeVec) WithLabelValues(lvs ...string) prometheus.Gauge {
    96  	if tx.tx == nil {
    97  		tx.ResetTx()
    98  	}
    99  	return tx.tx.WithLabelValues(lvs...)
   100  }