Repository: qdrant/qdrant
File: src/common/metrics.rs (Rust)

Code before:
                vec![gauge(self.pending_operations as f64, &[])],
                prefix,
            ));
            metrics.push_metric(metric_family(
                "cluster_voter",
                "is cluster peer a voter or learner",
                MetricType::GAUGE,
                vec![gauge(if self.is_voter { 1.0 } else { 0.0 }, &[])],
                prefix,
            ));
        }

        // Initialize all states so that every state has a zeroed metric by default.
        let mut state_working = 0.0;
        let mut state_stopped = 0.0;

        match &self.consensus_thread_status {
            ConsensusThreadStatus::Working { last_update } => {
                let delta = Utc::now() - last_update;
                let delta_secs = delta.as_seconds_f64();

                metrics.push_metric(metric_family(
                    "cluster_last_update_seconds",
                    "time since last update",
                    MetricType::GAUGE,
                    vec![gauge(delta_secs, &[])],
                    prefix,
                ));

                state_working = 1.0;
            }
            ConsensusThreadStatus::Stopped | ConsensusThreadStatus::StoppedWithErr { err: _ } => {
                state_stopped = 1.0;
            }
        }

        let working_states = vec![
            gauge(state_working, &[("state", "working")]),
            gauge(state_stopped, &[("state", "stopped")]),
        ];

        metrics.push_metric(metric_family(
            "cluster_working_state",
            "working state of the cluster",
            MetricType::GAUGE,
            working_states,
            prefix,
        ));
    }
}


Review comment (suggestion):
According to a quick ChatGPT search, Prometheus prefers a timestamp, not an age in seconds.

So I guess it's best to change it to be a Unix timestamp: `cluster_last_update_timestamp_seconds`



Code after:
                prefix,
            ));
            metrics.push_metric(metric_family(
                "cluster_voter",
                "is cluster peer a voter or learner",
                MetricType::GAUGE,
                vec![gauge(if self.is_voter { 1.0 } else { 0.0 }, &[])],
                prefix,
            ));
        }

        // Initialize all states so that every state has a zeroed metric by default.
        let mut state_working = 0.0;
        let mut state_stopped = 0.0;

        match &self.consensus_thread_status {
            ConsensusThreadStatus::Working { last_update } => {
                let timestamp = last_update.timestamp();

                metrics.push_metric(metric_family(
                    "cluster_last_update_timestamp_seconds",
                    "unix timestamp of last update",
                    MetricType::COUNTER,
                    vec![counter(timestamp as f64, &[])],
                    prefix,
                ));

                state_working = 1.0;
            }
            ConsensusThreadStatus::Stopped | ConsensusThreadStatus::StoppedWithErr { err: _ } => {
                state_stopped = 1.0;
            }
        }

        let working_states = vec![
            gauge(state_working, &[("state", "working")]),
            gauge(state_stopped, &[("state", "stopped")]),
        ];

        metrics.push_metric(metric_family(
            "cluster_working_state",
            "working state of the cluster",
            MetricType::GAUGE,
            working_states,
            prefix,
        ));
    }
}

impl MetricsProvider for RequestsTelemetry {
    fn add_metrics(&self, metrics: &mut MetricsData, prefix: Option<&str>) {