[#271] Add namespace label to billing metrics

Signed-off-by: Marina Biryukova <m.biryukova@yadro.com>
This commit is contained in:
Marina Biryukova 2023-12-01 14:16:19 +03:00
parent 6c5f9b2764
commit 93cf7c462b
8 changed files with 41 additions and 18 deletions

View file

@ -42,6 +42,7 @@ This document outlines major changes between releases.
- Support frostfsid contract. See `frostfsid` config section (#260) - Support frostfsid contract. See `frostfsid` config section (#260)
- Support per namespace placement policies configuration (see `namespaces.config` config param) (#266) - Support per namespace placement policies configuration (see `namespaces.config` config param) (#266)
- Support control api to manage policies. See `control` config section (#258) - Support control api to manage policies. See `control` config section (#258)
- Add `namespace` label to billing metrics (#271)
### Changed ### Changed
- Update prometheus to v1.15.0 (#94) - Update prometheus to v1.15.0 (#94)

View file

@ -17,10 +17,6 @@ import (
) )
type ( type (
UsersStat interface {
Update(user, bucket, cnrID string, reqType int, in, out uint64)
}
readCounter struct { readCounter struct {
io.ReadCloser io.ReadCloser
countBytes uint64 countBytes uint64
@ -39,6 +35,10 @@ type (
startTime time.Time startTime time.Time
} }
AliasResolver interface {
ResolveNamespaceAlias(namespace string) string
}
// BucketResolveFunc is a func to resolve bucket info by name. // BucketResolveFunc is a func to resolve bucket info by name.
BucketResolveFunc func(ctx context.Context, bucket string) (*data.BucketInfo, error) BucketResolveFunc func(ctx context.Context, bucket string) (*data.BucketInfo, error)
@ -49,14 +49,14 @@ type (
const systemPath = "/system" const systemPath = "/system"
// Metrics wraps http handler for api with basic statistics collection. // Metrics wraps http handler for api with basic statistics collection.
func Metrics(log *zap.Logger, resolveBucket BucketResolveFunc, appMetrics *metrics.AppMetrics) Func { func Metrics(log *zap.Logger, resolveBucket BucketResolveFunc, appMetrics *metrics.AppMetrics, aliasResolver AliasResolver) Func {
return func(h http.Handler) http.Handler { return func(h http.Handler) http.Handler {
return stats(h.ServeHTTP, resolveCID(log, resolveBucket), appMetrics) return stats(h.ServeHTTP, resolveCID(log, resolveBucket), appMetrics, aliasResolver)
} }
} }
// Stats is a handler that update metrics. // Stats is a handler that update metrics.
func stats(f http.HandlerFunc, resolveCID cidResolveFunc, appMetrics *metrics.AppMetrics) http.HandlerFunc { func stats(f http.HandlerFunc, resolveCID cidResolveFunc, appMetrics *metrics.AppMetrics, aliasResolver AliasResolver) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
reqInfo := GetReqInfo(r.Context()) reqInfo := GetReqInfo(r.Context())
@ -82,7 +82,8 @@ func stats(f http.HandlerFunc, resolveCID cidResolveFunc, appMetrics *metrics.Ap
user := resolveUser(r.Context()) user := resolveUser(r.Context())
cnrID := resolveCID(r.Context(), reqInfo) cnrID := resolveCID(r.Context(), reqInfo)
appMetrics.Update(user, reqInfo.BucketName, cnrID, requestTypeFromAPI(reqInfo.API), in.countBytes, out.countBytes) appMetrics.Update(user, reqInfo.BucketName, cnrID, aliasResolver.ResolveNamespaceAlias(reqInfo.Namespace),
requestTypeFromAPI(reqInfo.API), in.countBytes, out.countBytes)
code := statsWriter.statusCode code := statsWriter.statusCode
// A successful request has a 2xx response code // A successful request has a 2xx response code

View file

@ -98,6 +98,8 @@ type Config struct {
RequestMiddlewareSettings s3middleware.RequestSettings RequestMiddlewareSettings s3middleware.RequestSettings
AliasResolver s3middleware.AliasResolver
// Domains optional. If empty no virtual hosted domains will be attached. // Domains optional. If empty no virtual hosted domains will be attached.
Domains []string Domains []string
@ -112,7 +114,7 @@ func NewRouter(cfg Config) *chi.Mux {
middleware.ThrottleWithOpts(cfg.Throttle), middleware.ThrottleWithOpts(cfg.Throttle),
middleware.Recoverer, middleware.Recoverer,
s3middleware.Tracing(), s3middleware.Tracing(),
s3middleware.Metrics(cfg.Log, cfg.Handler.ResolveBucket, cfg.Metrics), s3middleware.Metrics(cfg.Log, cfg.Handler.ResolveBucket, cfg.Metrics, cfg.AliasResolver),
s3middleware.LogSuccessResponse(cfg.Log), s3middleware.LogSuccessResponse(cfg.Log),
s3middleware.Auth(cfg.Center, cfg.Log), s3middleware.Auth(cfg.Center, cfg.Log),
) )

View file

@ -385,6 +385,16 @@ func (s *appSettings) setAuthorizedControlAPIKeys(keys keys.PublicKeys) {
s.mu.Unlock() s.mu.Unlock()
} }
func (s *appSettings) ResolveNamespaceAlias(namespace string) string {
s.mu.RLock()
namespaces := s.defaultNamespaces
s.mu.RUnlock()
if slices.Contains(namespaces, namespace) {
return defaultMetricsNamespace
}
return namespace
}
func (a *App) initAPI(ctx context.Context) { func (a *App) initAPI(ctx context.Context) {
a.initLayer(ctx) a.initLayer(ctx)
a.initHandler() a.initHandler()
@ -620,6 +630,7 @@ func (a *App) Serve(ctx context.Context) {
Domains: domains, Domains: domains,
RequestMiddlewareSettings: a.settings, RequestMiddlewareSettings: a.settings,
AliasResolver: a.settings,
} }
// We cannot make direct assignment if frostfsid.FrostFSID is nil // We cannot make direct assignment if frostfsid.FrostFSID is nil

View file

@ -55,6 +55,8 @@ const (
defaultNamespaceHeader = "X-Frostfs-Namespace" defaultNamespaceHeader = "X-Frostfs-Namespace"
defaultConstraintName = "default" defaultConstraintName = "default"
defaultMetricsNamespace = ""
) )
var ( var (

View file

@ -65,12 +65,12 @@ func (m *AppMetrics) Handler() http.Handler {
return m.gate.Handler() return m.gate.Handler()
} }
func (m *AppMetrics) Update(user, bucket, cnrID string, reqType RequestType, in, out uint64) { func (m *AppMetrics) Update(user, bucket, cnrID, ns string, reqType RequestType, in, out uint64) {
if !m.isEnabled() { if !m.isEnabled() {
return return
} }
m.gate.Billing.apiStat.Update(user, bucket, cnrID, reqType, in, out) m.gate.Billing.apiStat.Update(user, bucket, cnrID, ns, reqType, in, out)
} }
func (m *AppMetrics) Statistic() *APIStatMetrics { func (m *AppMetrics) Statistic() *APIStatMetrics {

View file

@ -71,6 +71,7 @@ type (
bucketKey struct { bucketKey struct {
name string name string
cid string cid string
namespace string
} }
bucketStat struct { bucketStat struct {
@ -88,6 +89,7 @@ type (
User string User string
Bucket string Bucket string
ContainerID string ContainerID string
Namespace string
} }
UserMetricsInfo struct { UserMetricsInfo struct {
@ -108,7 +110,7 @@ type (
} }
) )
func (u *UsersAPIStats) Update(user, bucket, cnrID string, reqType RequestType, in, out uint64) { func (u *UsersAPIStats) Update(user, bucket, cnrID, ns string, reqType RequestType, in, out uint64) {
u.Lock() u.Lock()
defer u.Unlock() defer u.Unlock()
@ -127,6 +129,7 @@ func (u *UsersAPIStats) Update(user, bucket, cnrID string, reqType RequestType,
key := bucketKey{ key := bucketKey{
name: bucket, name: bucket,
cid: cnrID, cid: cnrID,
namespace: ns,
} }
bktStat := usersStat.buckets[key] bktStat := usersStat.buckets[key]
@ -151,6 +154,7 @@ func (u *UsersAPIStats) DumpMetrics() UserMetrics {
User: user, User: user,
Bucket: key.name, Bucket: key.name,
ContainerID: key.cid, ContainerID: key.cid,
Namespace: key.namespace,
} }
if bktStat.InTraffic != 0 { if bktStat.InTraffic != 0 {
@ -232,6 +236,7 @@ func (b *billingMetrics) Collect(ch chan<- prometheus.Metric) {
value.Bucket, value.Bucket,
value.ContainerID, value.ContainerID,
value.Operation.String(), value.Operation.String(),
value.Namespace,
) )
} }
@ -244,6 +249,7 @@ func (b *billingMetrics) Collect(ch chan<- prometheus.Metric) {
value.Bucket, value.Bucket,
value.ContainerID, value.ContainerID,
value.Type.String(), value.Type.String(),
value.Namespace,
) )
} }
} }

View file

@ -64,7 +64,7 @@ var appMetricsDesc = map[string]map[string]Description{
Subsystem: billingSubsystem, Subsystem: billingSubsystem,
Name: userRequestsMetric, Name: userRequestsMetric,
Help: "Accumulated user requests", Help: "Accumulated user requests",
VariableLabels: []string{"user", "bucket", "cid", "operation"}, VariableLabels: []string{"user", "bucket", "cid", "operation", "namespace"},
}, },
userTrafficMetric: Description{ userTrafficMetric: Description{
Type: dto.MetricType_GAUGE, Type: dto.MetricType_GAUGE,
@ -72,7 +72,7 @@ var appMetricsDesc = map[string]map[string]Description{
Subsystem: billingSubsystem, Subsystem: billingSubsystem,
Name: userTrafficMetric, Name: userTrafficMetric,
Help: "Accumulated user traffic", Help: "Accumulated user traffic",
VariableLabels: []string{"user", "bucket", "cid", "direction"}, VariableLabels: []string{"user", "bucket", "cid", "direction", "namespace"},
}, },
}, },
stateSubsystem: { stateSubsystem: {