Files
upstream-mirror 89f5be005d
Some checks failed
go: build dev binaries / cleanup (push) Successful in 4s
Codespell / Check for spelling errors (push) Failing after 3s
docker: build dev containers / build-rust-binaries (amd64, x86_64-unknown-linux-musl) (push) Failing after 3s
docker: build dev containers / build-dev-containers (push) Has been skipped
docker: build dev containers / build-rust-binaries (arm64, true, aarch64-unknown-linux-musl) (push) Failing after 3s
End to End / FUSE Mount (push) Failing after 2s
EC Integration Tests / EC Integration Tests (push) Failing after 30s
FUSE DLM Integration Tests / FUSE DLM Integration Tests (push) Failing after 3s
FUSE Volume Server Failover Tests / FUSE Volume Server Failover (push) Failing after 3s
FUSE Integration Tests / FUSE Integration Testing (push) Failing after 2s
FUSE P2P Peer Chunk Sharing Integration Tests / FUSE P2P Peer Chunk Sharing (push) Failing after 3s
go: build binary / Go Vet (push) Failing after 2s
go: build binary / Go Vet 32-bit (push) Failing after 3s
go: build binary / Build (push) Failing after 4s
go: build binary / Build cross-platform (freebsd/amd64) (push) Failing after 3s
go: build binary / Build cross-platform (windows/amd64) (push) Failing after 3s
go: build binary / Build cross-platform (darwin/arm64) (push) Failing after 3s
go: build binary / Build cross-platform (windows/arm64) (push) Failing after 3s
go: build binary / Test (push) Failing after 2s
go: build binary / Test 32-bit (push) Failing after 3s
helm: lint and test charts / lint-test (push) Failing after 3s
Java Client Integration Tests / Java Integration Tests (11) (push) Failing after 3s
Java Client Integration Tests / Java Integration Tests (17) (push) Failing after 3s
go: build dev binaries / build_dev_linux_windows (amd64, linux) (push) Failing after 2s
go: build dev binaries / build_dev_linux_windows (amd64, windows) (push) Failing after 3s
go: build dev binaries / build_dev_darwin (amd64, darwin) (push) Has been skipped
go: build dev binaries / build_dev_darwin (arm64, darwin) (push) Has been skipped
Java Client Unit Tests / Java Unit Tests (11) (push) Failing after 3s
Java Client Unit Tests / Java Unit Tests (17) (push) Failing after 2s
Java Client Unit Tests / Java Unit Tests (21) (push) Failing after 3s
Java Client Unit Tests / Java Unit Tests (8) (push) Failing after 3s
Kafka Quick Test (Load Test with Schema Registry) / Kafka Client Load Test (Quick) (push) Failing after 3s
Kafka Gateway Tests / Kafka Unit Tests (unit-tests-1) (push) Failing after 11s
Kafka Gateway Tests / Kafka Integration Tests (Critical) (integration-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka End-to-End Tests (with SMQ) (e2e-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka Consumer Group Tests (Highly Isolated) (consumer-group-1) (push) Failing after 2s
Kafka Gateway Tests / Kafka Client Compatibility (with SMQ) (client-compat-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka SMQ Integration Tests (Full Stack) (smq-integration-1) (push) Failing after 3s
Kafka Gateway Tests / Kafka Protocol Tests (Isolated) (protocol-1) (push) Failing after 3s
KMS Tests / KMS Provider Integration Tests (push) Failing after 2s
KMS Tests / S3 KMS End-to-End Tests (push) Failing after 3s
Master Cold Start Tests / Master Cold Start Tests (push) Failing after 4s
Metadata Subscribe Integration Tests / Metadata Subscribe Integration Tests (push) Failing after 3s
Multi-Master Tests / Multi-Master Failover Tests (push) Failing after 3s
Performance / CPU and Heap Profile (push) Failing after 2s
Performance / S3 Read/Write Benchmark (rust volume) (push) Failing after 3s
pjdfstest POSIX Compliance / pjdfstest (push) Failing after 2s
Plugin Worker Integration Tests / Plugin Worker: erasure_coding (push) Failing after 3s
Plugin Worker Integration Tests / Plugin Worker: vacuum (push) Failing after 3s
Rust Volume Server Tests / Rust Integration Tests (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (grpc - Shard 1) (push) Failing after 2s
Rust Volume Server Tests / Go Tests with Rust Volume (http - Shard 1) (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (grpc - Shard 2) (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (http - Shard 2) (push) Failing after 3s
Rust Volume Server Tests / Go Tests with Rust Volume (grpc - Shard 3) (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Distributed Tests (push) Failing after 2s
S3 IAM Integration Tests / S3 IAM Performance Tests (push) Failing after 3s
Performance / Throughput Benchmark (go volume) (push) Failing after 3s
Performance / Throughput Benchmark (rust volume) (push) Failing after 2s
Performance / S3 Read/Write Benchmark (go volume) (push) Failing after 3s
Plugin Worker Integration Tests / Plugin Worker: volume_balance (push) Failing after 3s
PostgreSQL Gateway Tests / PostgreSQL Basic Tests (push) Failing after 3s
Rust Volume Server Tests / Rust Unit Tests (push) Failing after 2s
Rust Volume Server Tests / Go Tests with Rust Volume (http - Shard 3) (push) Failing after 3s
Rust Plugin Worker Tests / Rust Plugin Worker Build and Unit Tests (push) Failing after 3s
rust: build dev volume server binaries / cleanup (push) Successful in 3s
S3 ETag and ACL Tests / S3 ETag + ACL Integration Tests (push) Failing after 3s
S3 IAM Integration Tests / IAM Unit Tests (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Integration Tests (advanced) (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Integration Tests (basic) (push) Failing after 2s
S3 IAM Integration Tests / S3 IAM Integration Tests (group) (push) Failing after 3s
S3 IAM Integration Tests / S3 IAM Integration Tests (policy-enforcement) (push) Failing after 2s
S3 IAM Integration Tests / S3 IAM Integration Tests (sts) (push) Failing after 4s
S3 Keycloak Integration Tests / S3 Keycloak Integration Tests (push) Failing after 3s
S3 PyArrow Parquet Tests / PyArrow Parquet Tests (Python 3.11) (push) Failing after 3s
S3 PyArrow Parquet Tests / Go Unit Tests (Implicit Directory) (push) Has been cancelled
S3 PyArrow Parquet Tests / PyArrow Parquet Tests (Python 3.12) (push) Has been cancelled
rust: build dev volume server binaries / build-rust-volume-dev-darwin (darwin-amd64, x86_64-apple-darwin) (push) Has been cancelled
rust: build dev volume server binaries / build-rust-volume-dev-darwin (darwin-arm64, aarch64-apple-darwin) (push) Has been cancelled
rust: build dev volume server binaries / build-rust-volume-dev-linux (linux-amd64, x86_64-unknown-linux-gnu) (push) Has been cancelled
S3 PyArrow Parquet Tests / PyArrow Parquet Tests (Python 3.9) (push) Has been cancelled
S3 Proxy Signature Tests / S3 Proxy Signature Verification Tests (push) Has been cancelled
S3 SDK V2 Route Disambiguation Tests / S3 SDK V2 Routing Tests (push) Has been cancelled
S3 SSE Tests / S3 SSE Integration Tests (quick) (push) Has been cancelled
S3 SSE Tests / S3 SSE Compatibility Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Metadata Persistence Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Copy Operations Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Multipart Upload Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Performance Test (push) Has been cancelled
S3 SSE Tests / S3 Volume Encryption Test (push) Has been cancelled
S3 SSE Tests / S3 SSE Integration Tests (comprehensive) (push) Has been cancelled
Spark Integration Tests / Spark Integration Tests (push) Failing after 2s
Telemetry Integration Tests / telemetry-integration-test (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (grpc - Shard 1) (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (http - Shard 1) (push) Failing after 3s
Ceph S3 tests / Basic S3 tests (KV store) (push) Has been cancelled
Ceph S3 tests / S3 Versioning & Object Lock tests (push) Has been cancelled
S3 Policy Integration Tests / S3 Policy Unit Tests (push) Failing after 3s
Ceph S3 tests / S3 CORS tests (push) Has been cancelled
Ceph S3 tests / SeaweedFS Custom S3 Copy tests (push) Has been cancelled
Ceph S3 tests / Basic S3 tests (SQL store) (push) Has been cancelled
test s3 over https using aws-cli / awscli-tests (push) Has been cancelled
TLS Rotation Integration Tests / TLS Rotation Integration Tests (push) Has been cancelled
Vacuum Integration Tests / Vacuum Integration Tests (push) Has been cancelled
S3 Policy Integration Tests / S3 Policy Variables Integration Tests (push) Failing after 3s
S3 Policy Integration Tests / S3 Policy Enforcement Integration Tests (advanced-policy) (push) Failing after 3s
S3 Policy Integration Tests / S3 Policy Enforcement Integration Tests (basic-policy) (push) Failing after 2s
S3 Policy Integration Tests / S3 Policy Enforcement Integration Tests (contextual-policy) (push) Failing after 4s
S3 Policy Integration Tests / Trusted Proxy Detection Tests (push) Failing after 2s
S3 Policy Integration Tests / S3 Policy Shell Integration Tests (push) Failing after 3s
Samba on FUSE Integration / samba-integration (push) Failing after 2s
SFTP Integration Tests / SFTP Integration Testing (push) Failing after 3s
terraform: validate and test modules / fmt, validate, plan-level tests (push) Failing after 3s
terraform: validate and test modules / local cluster smoke test (real weed) (push) Failing after 2s
Volume Server Integration Tests / Volume Server Integration Tests (grpc - Shard 3) (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (grpc - Shard 2) (push) Failing after 3s
Volume Server Integration Tests / Volume Server Integration Tests (http - Shard 2) (push) Failing after 2s
Volume Server Integration Tests / Volume Server Integration Tests (http - Shard 3) (push) Failing after 5s
mount: windows conformance / WinFsp conformance (push) Has been cancelled
mount: windows / Mount on Windows (push) Has been cancelled
snapshot of https://github.com/seaweedfs/seaweedfs.git (master, 2026-09-16; blobs > 131072B omitted)
2026-09-16 03:23:42 +05:00

354 lines
11 KiB
Go

package metrics
import (
"fmt"
"io"
"sort"
"sync"
"sync/atomic"
"time"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)
// Collector handles metrics collection for the load test
type Collector struct {
// Atomic counters for thread-safe operations
messagesProduced int64
messagesConsumed int64
bytesProduced int64
bytesConsumed int64
producerErrors int64
consumerErrors int64
// Latency tracking
latencies []time.Duration
latencyMutex sync.RWMutex
// Consumer lag tracking
consumerLag map[string]int64
consumerLagMutex sync.RWMutex
// Test timing
startTime time.Time
// Prometheus metrics
prometheusMetrics *PrometheusMetrics
}
// PrometheusMetrics holds all Prometheus metric definitions
type PrometheusMetrics struct {
MessagesProducedTotal prometheus.Counter
MessagesConsumedTotal prometheus.Counter
BytesProducedTotal prometheus.Counter
BytesConsumedTotal prometheus.Counter
ProducerErrorsTotal prometheus.Counter
ConsumerErrorsTotal prometheus.Counter
MessageLatencyHistogram prometheus.Histogram
ProducerThroughput prometheus.Gauge
ConsumerThroughput prometheus.Gauge
ConsumerLagGauge *prometheus.GaugeVec
ActiveProducers prometheus.Gauge
ActiveConsumers prometheus.Gauge
}
// NewCollector creates a new metrics collector
func NewCollector() *Collector {
return &Collector{
startTime: time.Now(),
consumerLag: make(map[string]int64),
prometheusMetrics: &PrometheusMetrics{
MessagesProducedTotal: promauto.NewCounter(prometheus.CounterOpts{
Name: "kafka_loadtest_messages_produced_total",
Help: "Total number of messages produced",
}),
MessagesConsumedTotal: promauto.NewCounter(prometheus.CounterOpts{
Name: "kafka_loadtest_messages_consumed_total",
Help: "Total number of messages consumed",
}),
BytesProducedTotal: promauto.NewCounter(prometheus.CounterOpts{
Name: "kafka_loadtest_bytes_produced_total",
Help: "Total bytes produced",
}),
BytesConsumedTotal: promauto.NewCounter(prometheus.CounterOpts{
Name: "kafka_loadtest_bytes_consumed_total",
Help: "Total bytes consumed",
}),
ProducerErrorsTotal: promauto.NewCounter(prometheus.CounterOpts{
Name: "kafka_loadtest_producer_errors_total",
Help: "Total number of producer errors",
}),
ConsumerErrorsTotal: promauto.NewCounter(prometheus.CounterOpts{
Name: "kafka_loadtest_consumer_errors_total",
Help: "Total number of consumer errors",
}),
MessageLatencyHistogram: promauto.NewHistogram(prometheus.HistogramOpts{
Name: "kafka_loadtest_message_latency_seconds",
Help: "Message end-to-end latency in seconds",
Buckets: prometheus.ExponentialBuckets(0.001, 2, 15), // 1ms to ~32s
}),
ProducerThroughput: promauto.NewGauge(prometheus.GaugeOpts{
Name: "kafka_loadtest_producer_throughput_msgs_per_sec",
Help: "Current producer throughput in messages per second",
}),
ConsumerThroughput: promauto.NewGauge(prometheus.GaugeOpts{
Name: "kafka_loadtest_consumer_throughput_msgs_per_sec",
Help: "Current consumer throughput in messages per second",
}),
ConsumerLagGauge: promauto.NewGaugeVec(prometheus.GaugeOpts{
Name: "kafka_loadtest_consumer_lag_messages",
Help: "Consumer lag in messages",
}, []string{"consumer_group", "topic", "partition"}),
ActiveProducers: promauto.NewGauge(prometheus.GaugeOpts{
Name: "kafka_loadtest_active_producers",
Help: "Number of active producers",
}),
ActiveConsumers: promauto.NewGauge(prometheus.GaugeOpts{
Name: "kafka_loadtest_active_consumers",
Help: "Number of active consumers",
}),
},
}
}
// RecordProducedMessage records a successfully produced message
func (c *Collector) RecordProducedMessage(size int, latency time.Duration) {
atomic.AddInt64(&c.messagesProduced, 1)
atomic.AddInt64(&c.bytesProduced, int64(size))
c.prometheusMetrics.MessagesProducedTotal.Inc()
c.prometheusMetrics.BytesProducedTotal.Add(float64(size))
c.prometheusMetrics.MessageLatencyHistogram.Observe(latency.Seconds())
// Store latency for percentile calculations
c.latencyMutex.Lock()
c.latencies = append(c.latencies, latency)
// Keep only recent latencies to avoid memory bloat
if len(c.latencies) > 100000 {
c.latencies = c.latencies[50000:]
}
c.latencyMutex.Unlock()
}
// RecordConsumedMessage records a successfully consumed message
func (c *Collector) RecordConsumedMessage(size int) {
atomic.AddInt64(&c.messagesConsumed, 1)
atomic.AddInt64(&c.bytesConsumed, int64(size))
c.prometheusMetrics.MessagesConsumedTotal.Inc()
c.prometheusMetrics.BytesConsumedTotal.Add(float64(size))
}
// RecordProducerError records a producer error
func (c *Collector) RecordProducerError() {
atomic.AddInt64(&c.producerErrors, 1)
c.prometheusMetrics.ProducerErrorsTotal.Inc()
}
// RecordConsumerError records a consumer error
func (c *Collector) RecordConsumerError() {
atomic.AddInt64(&c.consumerErrors, 1)
c.prometheusMetrics.ConsumerErrorsTotal.Inc()
}
// UpdateConsumerLag updates consumer lag metrics
func (c *Collector) UpdateConsumerLag(consumerGroup, topic string, partition int32, lag int64) {
key := fmt.Sprintf("%s-%s-%d", consumerGroup, topic, partition)
c.consumerLagMutex.Lock()
c.consumerLag[key] = lag
c.consumerLagMutex.Unlock()
c.prometheusMetrics.ConsumerLagGauge.WithLabelValues(
consumerGroup, topic, fmt.Sprintf("%d", partition),
).Set(float64(lag))
}
// UpdateThroughput updates throughput gauges
func (c *Collector) UpdateThroughput(producerRate, consumerRate float64) {
c.prometheusMetrics.ProducerThroughput.Set(producerRate)
c.prometheusMetrics.ConsumerThroughput.Set(consumerRate)
}
// UpdateActiveClients updates active client counts
func (c *Collector) UpdateActiveClients(producers, consumers int) {
c.prometheusMetrics.ActiveProducers.Set(float64(producers))
c.prometheusMetrics.ActiveConsumers.Set(float64(consumers))
}
// GetStats returns current statistics
func (c *Collector) GetStats() Stats {
produced := atomic.LoadInt64(&c.messagesProduced)
consumed := atomic.LoadInt64(&c.messagesConsumed)
bytesProduced := atomic.LoadInt64(&c.bytesProduced)
bytesConsumed := atomic.LoadInt64(&c.bytesConsumed)
producerErrors := atomic.LoadInt64(&c.producerErrors)
consumerErrors := atomic.LoadInt64(&c.consumerErrors)
duration := time.Since(c.startTime)
// Calculate throughput
producerThroughput := float64(produced) / duration.Seconds()
consumerThroughput := float64(consumed) / duration.Seconds()
// Calculate latency percentiles
var latencyPercentiles map[float64]time.Duration
c.latencyMutex.RLock()
if len(c.latencies) > 0 {
latencyPercentiles = c.calculatePercentiles(c.latencies)
}
c.latencyMutex.RUnlock()
// Get consumer lag summary
c.consumerLagMutex.RLock()
totalLag := int64(0)
maxLag := int64(0)
for _, lag := range c.consumerLag {
totalLag += lag
if lag > maxLag {
maxLag = lag
}
}
avgLag := float64(0)
if len(c.consumerLag) > 0 {
avgLag = float64(totalLag) / float64(len(c.consumerLag))
}
c.consumerLagMutex.RUnlock()
return Stats{
Duration: duration,
MessagesProduced: produced,
MessagesConsumed: consumed,
BytesProduced: bytesProduced,
BytesConsumed: bytesConsumed,
ProducerErrors: producerErrors,
ConsumerErrors: consumerErrors,
ProducerThroughput: producerThroughput,
ConsumerThroughput: consumerThroughput,
LatencyPercentiles: latencyPercentiles,
TotalConsumerLag: totalLag,
MaxConsumerLag: maxLag,
AvgConsumerLag: avgLag,
}
}
// PrintSummary prints a summary of the test statistics
func (c *Collector) PrintSummary() {
stats := c.GetStats()
fmt.Printf("\n=== Load Test Summary ===\n")
fmt.Printf("Test Duration: %v\n", stats.Duration)
fmt.Printf("\nMessages:\n")
fmt.Printf(" Produced: %d (%.2f MB)\n", stats.MessagesProduced, float64(stats.BytesProduced)/1024/1024)
fmt.Printf(" Consumed: %d (%.2f MB)\n", stats.MessagesConsumed, float64(stats.BytesConsumed)/1024/1024)
fmt.Printf(" Producer Errors: %d\n", stats.ProducerErrors)
fmt.Printf(" Consumer Errors: %d\n", stats.ConsumerErrors)
fmt.Printf("\nThroughput:\n")
fmt.Printf(" Producer: %.2f msgs/sec\n", stats.ProducerThroughput)
fmt.Printf(" Consumer: %.2f msgs/sec\n", stats.ConsumerThroughput)
if stats.LatencyPercentiles != nil {
fmt.Printf("\nLatency Percentiles:\n")
percentiles := []float64{50, 90, 95, 99, 99.9}
for _, p := range percentiles {
if latency, exists := stats.LatencyPercentiles[p]; exists {
fmt.Printf(" p%.1f: %v\n", p, latency)
}
}
}
fmt.Printf("\nConsumer Lag:\n")
fmt.Printf(" Total: %d messages\n", stats.TotalConsumerLag)
fmt.Printf(" Max: %d messages\n", stats.MaxConsumerLag)
fmt.Printf(" Average: %.2f messages\n", stats.AvgConsumerLag)
fmt.Printf("=========================\n")
}
// WriteStats writes statistics to a writer (for HTTP endpoint)
func (c *Collector) WriteStats(w io.Writer) {
stats := c.GetStats()
fmt.Fprintf(w, "# Load Test Statistics\n")
fmt.Fprintf(w, "duration_seconds %v\n", stats.Duration.Seconds())
fmt.Fprintf(w, "messages_produced %d\n", stats.MessagesProduced)
fmt.Fprintf(w, "messages_consumed %d\n", stats.MessagesConsumed)
fmt.Fprintf(w, "bytes_produced %d\n", stats.BytesProduced)
fmt.Fprintf(w, "bytes_consumed %d\n", stats.BytesConsumed)
fmt.Fprintf(w, "producer_errors %d\n", stats.ProducerErrors)
fmt.Fprintf(w, "consumer_errors %d\n", stats.ConsumerErrors)
fmt.Fprintf(w, "producer_throughput_msgs_per_sec %f\n", stats.ProducerThroughput)
fmt.Fprintf(w, "consumer_throughput_msgs_per_sec %f\n", stats.ConsumerThroughput)
fmt.Fprintf(w, "total_consumer_lag %d\n", stats.TotalConsumerLag)
fmt.Fprintf(w, "max_consumer_lag %d\n", stats.MaxConsumerLag)
fmt.Fprintf(w, "avg_consumer_lag %f\n", stats.AvgConsumerLag)
if stats.LatencyPercentiles != nil {
for percentile, latency := range stats.LatencyPercentiles {
fmt.Fprintf(w, "latency_p%g_seconds %f\n", percentile, latency.Seconds())
}
}
}
// calculatePercentiles calculates latency percentiles
func (c *Collector) calculatePercentiles(latencies []time.Duration) map[float64]time.Duration {
if len(latencies) == 0 {
return nil
}
// Make a copy and sort
sorted := make([]time.Duration, len(latencies))
copy(sorted, latencies)
sort.Slice(sorted, func(i, j int) bool {
return sorted[i] < sorted[j]
})
percentiles := map[float64]time.Duration{
50: calculatePercentile(sorted, 50),
90: calculatePercentile(sorted, 90),
95: calculatePercentile(sorted, 95),
99: calculatePercentile(sorted, 99),
99.9: calculatePercentile(sorted, 99.9),
}
return percentiles
}
// calculatePercentile calculates a specific percentile from sorted data
func calculatePercentile(sorted []time.Duration, percentile float64) time.Duration {
if len(sorted) == 0 {
return 0
}
index := percentile / 100.0 * float64(len(sorted)-1)
if index == float64(int(index)) {
return sorted[int(index)]
}
lower := sorted[int(index)]
upper := sorted[int(index)+1]
weight := index - float64(int(index))
return time.Duration(float64(lower) + weight*float64(upper-lower))
}
// Stats represents the current test statistics
type Stats struct {
Duration time.Duration
MessagesProduced int64
MessagesConsumed int64
BytesProduced int64
BytesConsumed int64
ProducerErrors int64
ConsumerErrors int64
ProducerThroughput float64
ConsumerThroughput float64
LatencyPercentiles map[float64]time.Duration
TotalConsumerLag int64
MaxConsumerLag int64
AvgConsumerLag float64
}