proxy-pool/internal/controller/extraction/metrics.go
2026-08-02 14:08:45 +08:00

81 lines
2.6 KiB
Go

package extraction
import (
"errors"
domain "proxy-pool/internal/domain/extraction"
)
// MetricResult is a fixed, low-cardinality classification of one extraction
// request after application-level validation, admission, and storage handling.
type MetricResult string
const (
MetricComplete MetricResult = "complete"
MetricPartial MetricResult = "partial"
MetricEmpty MetricResult = "empty"
MetricInsufficient MetricResult = "insufficient"
MetricIdempotencyConflict MetricResult = "idempotency_conflict"
MetricRateLimited MetricResult = "rate_limited"
MetricUnavailable MetricResult = "unavailable"
MetricInvalid MetricResult = "invalid"
MetricError MetricResult = "error"
)
// MetricsEvent contains only aggregate request and response counts. It must
// never contain a Client, Proxy, Upstream, filter, identifier, or error text.
type MetricsEvent struct {
Result MetricResult
Requested int
Returned int
}
// MetricsObserver receives one completed extraction request. Implementations
// must return promptly and must not perform network or persistent storage I/O.
type MetricsObserver interface {
ObserveExtraction(MetricsEvent)
}
func classifyMetricsEvent(response Response, resultErr error) MetricsEvent {
event := MetricsEvent{Requested: response.Requested, Returned: response.Returned}
if resultErr == nil {
switch {
case response.Returned == response.Requested:
event.Result = MetricComplete
case response.Returned > 0 && response.Returned < response.Requested:
event.Result = MetricPartial
case response.Returned == 0:
event.Result = MetricEmpty
default:
event.Result = MetricError
}
return event
}
switch {
case errors.Is(resultErr, domain.ErrInsufficientProxies):
event.Result = MetricInsufficient
case errors.Is(resultErr, domain.ErrIdempotencyConflict):
event.Result = MetricIdempotencyConflict
case errors.Is(resultErr, ErrAdmissionRejected):
event.Result = MetricRateLimited
case errors.Is(resultErr, ErrUnavailable):
event.Result = MetricUnavailable
case errors.Is(resultErr, ErrInvalidRequest), errors.Is(resultErr, ErrCountExceeded),
errors.Is(resultErr, ErrInvalidFulfillment), errors.Is(resultErr, domain.ErrInvalidCommand):
event.Result = MetricInvalid
default:
event.Result = MetricError
}
return event
}
func validMetricResult(result MetricResult) bool {
switch result {
case MetricComplete, MetricPartial, MetricEmpty, MetricInsufficient, MetricIdempotencyConflict,
MetricRateLimited, MetricUnavailable, MetricInvalid, MetricError:
return true
default:
return false
}
}