statreceiver: add v2/v3 splitter and downgrade

this allows for us to roll out monkit v3 while keeping the
old v2 code and dependencies working.

Change-Id: I0758ee2bb66685b0819502368fc2c20cb35b958a
This commit is contained in:
Jeff Wendling 2020-01-10 08:53:14 -07:00 committed by Yingrong Zhao
parent eaf3318a58
commit bd78945116
3 changed files with 255 additions and 0 deletions

View File

@ -0,0 +1,220 @@
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
package main
import (
"bytes"
"time"
)
// Note to readers: All of the tag iteration and field searching code does not bother to
// handle escaped spaces or commas because all of the keys we intend to migrate do not
// contain them. In particular, we have no package import paths with spaces or commas and
// it is impossible to have a function name with a space or comma in it. Additionally, all
// of the monkit/v3 field names do not have spaces or commas. This could become invalid if
// someone decides to break it, but this code is also temporary.
// MetricDowngrade downgrades known v3 metrics into v2 versions for backwards compat.
type MetricDowngrade struct {
dest MetricDest
}
// NewMetricDowngrade constructs a MetricDowngrade that passes known v3 metrics as
// v2 metrics to the provided dest.
func NewMetricDowngrade(dest MetricDest) *MetricDowngrade {
return &MetricDowngrade{
dest: dest,
}
}
// Metric implements MetricDest
func (k *MetricDowngrade) Metric(application, instance string, key []byte, val float64, ts time.Time) error {
comma := bytes.IndexByte(key, ',')
if comma < 0 {
return nil
}
if string(key[:comma]) == "function_times" {
return k.handleFunctionTimes(application, instance, key[comma+1:], val, ts)
}
if string(key[:comma]) == "function" {
return k.handleFunction(application, instance, key[comma+1:], val, ts)
}
v2key, ok := knownMetrics[string(key[:comma])]
if !ok {
return nil
}
space := bytes.LastIndexByte(key, ' ')
if space < 0 {
return nil
}
out := make([]byte, 0, len(v2key)+1+len(key)-space)
out = append(out, v2key...)
out = append(out, '.')
out = append(out, key[space+1:]...)
return k.dest.Metric(application, instance, out, val, ts)
}
func (k *MetricDowngrade) handleFunctionTimes(application, instance string, key []byte, val float64, ts time.Time) error {
var name, kind, scope string
iterateTags(key, func(tag []byte) {
if len(tag) < 6 {
return
}
switch {
case string(tag[:5]) == "name=":
name = string(tag[5:])
case string(tag[:5]) == "kind=":
kind = string(tag[5:])
case string(tag[:6]) == "scope=":
scope = string(tag[6:])
}
})
if name == "" || kind == "" || scope == "" {
return nil
}
space := bytes.LastIndexByte(key, ' ')
if space < 0 {
return nil
}
out := make([]byte, 0, len(scope)+1+len(name)+1+len(kind)+7+(len(key)-space))
out = append(out, scope...)
out = append(out, '.')
out = append(out, name...)
out = append(out, '.')
out = append(out, kind...)
out = append(out, "_times_"...)
out = append(out, key[space+1:]...)
return k.dest.Metric(application, instance, out, val, ts)
}
func (k *MetricDowngrade) handleFunction(application, instance string, key []byte, val float64, ts time.Time) error {
var name, scope string
iterateTags(key, func(tag []byte) {
if len(tag) < 6 {
return
}
switch {
case string(tag[:5]) == "name=":
name = string(tag[5:])
case string(tag[:6]) == "scope=":
scope = string(tag[6:])
}
})
if name == "" || scope == "" {
return nil
}
space := bytes.LastIndexByte(key, ' ')
if space < 0 {
return nil
}
out := make([]byte, 0, len(scope)+1+len(name)+1+(len(key)-space))
out = append(out, scope...)
out = append(out, '.')
out = append(out, name...)
out = append(out, '.')
out = append(out, key[space+1:]...)
return k.dest.Metric(application, instance, out, val, ts)
}
func iterateTags(key []byte, cb func([]byte)) {
for len(key) > 0 {
comma := bytes.IndexByte(key, ',')
if comma == -1 {
break
}
cb(key[:comma])
key = key[comma+1:]
}
space := bytes.IndexByte(key, ' ')
if space >= 0 {
cb(key[:space])
}
}
var knownMetrics = map[string]string{
"total.bytes": "storj.io/storj/satellite/accounting/tally.total.bytes",
"total.inline_bytes": "storj.io/storj/satellite/accounting/tally.total.inline_bytes",
"total.inline_segments": "storj.io/storj/satellite/accounting/tally.total.inline_segments",
"total.objects": "storj.io/storj/satellite/accounting/tally.total.objects",
"total.remote_bytes": "storj.io/storj/satellite/accounting/tally.total.remote_bytes",
"total.remote_segments": "storj.io/storj/satellite/accounting/tally.total.remote_segments",
"total.segments": "storj.io/storj/satellite/accounting/tally.total.segments",
"audit_contained_nodes": "storj.io/storj/satellite/audit.audit_contained_nodes",
"audit_contained_nodes_global": "storj.io/storj/satellite/audit.audit_contained_nodes_global",
"audit_contained_percentage": "storj.io/storj/satellite/audit.audit_contained_percentage",
"audit_fail_nodes": "storj.io/storj/satellite/audit.audit_fail_nodes",
"audit_fail_nodes_global": "storj.io/storj/satellite/audit.audit_fail_nodes_global",
"audit_failed_percentage": "storj.io/storj/satellite/audit.audit_failed_percentage",
"audit_offline_nodes": "storj.io/storj/satellite/audit.audit_offline_nodes",
"audit_offline_nodes_global": "storj.io/storj/satellite/audit.audit_offline_nodes_global",
"audit_offline_percentage": "storj.io/storj/satellite/audit.audit_offline_percentage",
"audit_success_nodes": "storj.io/storj/satellite/audit.audit_success_nodes",
"audit_success_nodes_global": "storj.io/storj/satellite/audit.audit_success_nodes_global",
"audit_successful_percentage": "storj.io/storj/satellite/audit.audit_successful_percentage",
"audit_total_nodes": "storj.io/storj/satellite/audit.audit_total_nodes",
"audit_total_nodes_global": "storj.io/storj/satellite/audit.audit_total_nodes_global",
"audit_total_pointer_nodes": "storj.io/storj/satellite/audit.audit_total_pointer_nodes",
"audit_total_pointer_nodes_global": "storj.io/storj/satellite/audit.audit_total_pointer_nodes_global",
"audit_unknown_nodes": "storj.io/storj/satellite/audit.audit_unknown_nodes",
"audit_unknown_nodes_global": "storj.io/storj/satellite/audit.audit_unknown_nodes_global",
"audit_unknown_percentage": "storj.io/storj/satellite/audit.audit_unknown_percentage",
"audited_percentage": "storj.io/storj/satellite/audit.audited_percentage",
"reverify_contained": "storj.io/storj/satellite/audit.reverify_contained",
"reverify_contained_global": "storj.io/storj/satellite/audit.reverify_contained_global",
"reverify_contained_in_segment": "storj.io/storj/satellite/audit.reverify_contained_in_segment",
"reverify_fails": "storj.io/storj/satellite/audit.reverify_fails",
"reverify_fails_global": "storj.io/storj/satellite/audit.reverify_fails_global",
"reverify_offlines": "storj.io/storj/satellite/audit.reverify_offlines",
"reverify_offlines_global": "storj.io/storj/satellite/audit.reverify_offlines_global",
"reverify_successes": "storj.io/storj/satellite/audit.reverify_successes",
"reverify_successes_global": "storj.io/storj/satellite/audit.reverify_successes_global",
"reverify_total_in_segment": "storj.io/storj/satellite/audit.reverify_total_in_segment",
"reverify_unknown": "storj.io/storj/satellite/audit.reverify_unknown",
"reverify_unknown_global": "storj.io/storj/satellite/audit.reverify_unknown_global",
"graceful_exit_fail_max_failures_percentage": "storj.io/storj/satellite/gracefulexit.graceful_exit_fail_max_failures_percentage",
"graceful_exit_fail_validation": "storj.io/storj/satellite/gracefulexit.graceful_exit_fail_validation",
"graceful_exit_final_bytes_transferred": "storj.io/storj/satellite/gracefulexit.graceful_exit_final_bytes_transferred",
"graceful_exit_final_pieces_failed": "storj.io/storj/satellite/gracefulexit.graceful_exit_final_pieces_failed",
"graceful_exit_final_pieces_succeess": "storj.io/storj/satellite/gracefulexit.graceful_exit_final_pieces_succeess",
"graceful_exit_init_node_age_seconds": "storj.io/storj/satellite/gracefulexit.graceful_exit_init_node_age_seconds",
"graceful_exit_init_node_audit_success_count": "storj.io/storj/satellite/gracefulexit.graceful_exit_init_node_audit_success_count",
"graceful_exit_init_node_audit_total_count": "storj.io/storj/satellite/gracefulexit.graceful_exit_init_node_audit_total_count",
"graceful_exit_init_node_piece_count": "storj.io/storj/satellite/gracefulexit.graceful_exit_init_node_piece_count",
"graceful_exit_success": "storj.io/storj/satellite/gracefulexit.graceful_exit_success",
"graceful_exit_successful_pieces_transfer_ratio": "storj.io/storj/satellite/gracefulexit.graceful_exit_successful_pieces_transfer_ratio",
"graceful_exit_transfer_piece_fail": "storj.io/storj/satellite/gracefulexit.graceful_exit_transfer_piece_fail",
"graceful_exit_transfer_piece_success": "storj.io/storj/satellite/gracefulexit.graceful_exit_transfer_piece_success",
"download_failed_not_enough_pieces_uplink": "storj.io/storj/satellite/orders.download_failed_not_enough_pieces_uplink",
"checker_segment_age": "storj.io/storj/satellite/repair/checker.checker_segment_age",
"checker_segment_healthy_count": "storj.io/storj/satellite/repair/checker.checker_segment_healthy_count",
"checker_segment_time_until_irreparable": "storj.io/storj/satellite/repair/checker.checker_segment_time_until_irreparable",
"checker_segment_total_count": "storj.io/storj/satellite/repair/checker.checker_segment_total_count",
"remote_files_checked": "storj.io/storj/satellite/repair/checker.remote_files_checked",
"remote_files_lost": "storj.io/storj/satellite/repair/checker.remote_files_lost",
"remote_segments_checked": "storj.io/storj/satellite/repair/checker.remote_segments_checked",
"remote_segments_lost": "storj.io/storj/satellite/repair/checker.remote_segments_lost",
"remote_segments_needing_repair": "storj.io/storj/satellite/repair/checker.remote_segments_needing_repair",
"download_failed_not_enough_pieces_repair": "storj.io/storj/satellite/repair/repairer.download_failed_not_enough_pieces_repair",
"audit_reputation_alpha": "storj.io/storj/satellite/satellitedb.audit_reputation_alpha",
"audit_reputation_beta": "storj.io/storj/satellite/satellitedb.audit_reputation_beta",
"open_file_in_trash": "storj.io/storj/storage/filestore.open_file_in_trash",
"satellite_contact_request": "storj.io/storj/storagenode/contact.satellite_contact_request",
"satellite_gracefulexit_request": "storj.io/storj/storagenode/gracefulexit.satellite_gracefulexit_request",
"allocated_bandwidth": "storj.io/storj/storagenode/monitor.allocated_bandwidth",
"used_bandwidth": "storj.io/storj/storagenode/monitor.used_bandwidth",
"download_stripe_failed_not_enough_pieces_uplink": "storj.io/storj/uplink/eestream.download_stripe_failed_not_enough_pieces_uplink",
}

View File

@ -82,6 +82,8 @@ func Main(cmd *cobra.Command, args []string) error {
scope.RegisterVal("db", NewDBDest),
scope.RegisterVal("pbufprep", NewPacketBufPrep),
scope.RegisterVal("mbufprep", NewMetricBufPrep),
scope.RegisterVal("downgrade", NewMetricDowngrade),
scope.RegisterVal("versionsplit", NewVersionSplit),
)
if err != nil {
return err

View File

@ -0,0 +1,33 @@
// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
package main
import (
"bytes"
"time"
)
// VersionSplit downgrades known v3 metrics into v2 versions for backwards compat.
type VersionSplit struct {
v2dest MetricDest
v3dest MetricDest
}
// NewVersionSplit constructs a VersionSplit that passes known v3 metrics as
// v2 metrics to the provided dest.
func NewVersionSplit(v2dest, v3dest MetricDest) *VersionSplit {
return &VersionSplit{
v2dest: v2dest,
v3dest: v3dest,
}
}
// Metric implements MetricDest
func (k *VersionSplit) Metric(application, instance string, key []byte, val float64, ts time.Time) error {
comma := bytes.IndexByte(key, ',')
if comma < 0 {
return k.v2dest.Metric(application, instance, key, val, ts)
}
return k.v3dest.Metric(application, instance, key, val, ts)
}