75 lines
3.1 KiB
Go
75 lines
3.1 KiB
Go
|
|
// Package metrics implements the three Prometheus counters the reconciler
|
||
|
|
// exposes, without pulling the prometheus/client_golang module. The /metrics
|
||
|
|
// handler prints the standard text exposition format.
|
||
|
|
package metrics
|
||
|
|
|
||
|
|
import (
|
||
|
|
"fmt"
|
||
|
|
"io"
|
||
|
|
"net/http"
|
||
|
|
"sync/atomic"
|
||
|
|
)
|
||
|
|
|
||
|
|
// Registry holds the reconciler's counters. Use New() to construct and share
|
||
|
|
// one instance across the scheduler and the HTTP handler.
|
||
|
|
type Registry struct {
|
||
|
|
dirtyRows atomic.Int64
|
||
|
|
reconciliationAttempts atomic.Int64
|
||
|
|
reconciliationFailures atomic.Int64
|
||
|
|
reconciliationNoopHits atomic.Int64
|
||
|
|
}
|
||
|
|
|
||
|
|
func New() *Registry { return &Registry{} }
|
||
|
|
|
||
|
|
// SetDirtyRows sets the current pending-row count. Called at the top of each
|
||
|
|
// tick, before any attempts land.
|
||
|
|
func (r *Registry) SetDirtyRows(n int64) { r.dirtyRows.Store(n) }
|
||
|
|
|
||
|
|
func (r *Registry) IncAttempts() { r.reconciliationAttempts.Add(1) }
|
||
|
|
func (r *Registry) IncFailures() { r.reconciliationFailures.Add(1) }
|
||
|
|
func (r *Registry) IncIdempotentNoopHits() { r.reconciliationNoopHits.Add(1) }
|
||
|
|
|
||
|
|
// Snapshot returns a point-in-time copy; useful for tests.
|
||
|
|
type Snapshot struct {
|
||
|
|
DirtyRows int64
|
||
|
|
ReconciliationAttempts int64
|
||
|
|
ReconciliationFailures int64
|
||
|
|
IdempotentNoopHits int64
|
||
|
|
}
|
||
|
|
|
||
|
|
func (r *Registry) Snapshot() Snapshot {
|
||
|
|
return Snapshot{
|
||
|
|
DirtyRows: r.dirtyRows.Load(),
|
||
|
|
ReconciliationAttempts: r.reconciliationAttempts.Load(),
|
||
|
|
ReconciliationFailures: r.reconciliationFailures.Load(),
|
||
|
|
IdempotentNoopHits: r.reconciliationNoopHits.Load(),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Handler returns an http.HandlerFunc that writes the Prometheus text format.
|
||
|
|
func (r *Registry) Handler() http.HandlerFunc {
|
||
|
|
return func(w http.ResponseWriter, _ *http.Request) {
|
||
|
|
w.Header().Set("Content-Type", "text/plain; version=0.0.4; charset=utf-8")
|
||
|
|
r.write(w)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func (r *Registry) write(w io.Writer) {
|
||
|
|
s := r.Snapshot()
|
||
|
|
fmt.Fprintln(w, "# HELP attest_reconciler_dirty_rows_total Current count of per_diem_rates rows missing an attestation reference (gauge-style, overwritten each tick).")
|
||
|
|
fmt.Fprintln(w, "# TYPE attest_reconciler_dirty_rows_total gauge")
|
||
|
|
fmt.Fprintf(w, "attest_reconciler_dirty_rows_total %d\n", s.DirtyRows)
|
||
|
|
|
||
|
|
fmt.Fprintln(w, "# HELP attest_reconciler_reconciliation_attempts_total Total pending rows the reconciler attempted to re-sign.")
|
||
|
|
fmt.Fprintln(w, "# TYPE attest_reconciler_reconciliation_attempts_total counter")
|
||
|
|
fmt.Fprintf(w, "attest_reconciler_reconciliation_attempts_total %d\n", s.ReconciliationAttempts)
|
||
|
|
|
||
|
|
fmt.Fprintln(w, "# HELP attest_reconciler_reconciliation_failures_total Total pending-row attempts that returned an error from the signer or transparency log.")
|
||
|
|
fmt.Fprintln(w, "# TYPE attest_reconciler_reconciliation_failures_total counter")
|
||
|
|
fmt.Fprintf(w, "attest_reconciler_reconciliation_failures_total %d\n", s.ReconciliationFailures)
|
||
|
|
|
||
|
|
fmt.Fprintln(w, "# HELP attest_reconciler_idempotent_noop_total Rows the reconciler re-signed where the TLog already had the entry (safe no-op; proves the idempotency contract works).")
|
||
|
|
fmt.Fprintln(w, "# TYPE attest_reconciler_idempotent_noop_total counter")
|
||
|
|
fmt.Fprintf(w, "attest_reconciler_idempotent_noop_total %d\n", s.IdempotentNoopHits)
|
||
|
|
}
|