github.com/go-graphite/carbonapi@v0.17.0/expr/functions/nonNegativeDerivative/function.go (about)

     1  package nonNegativeDerivative
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"math"
     7  	"strconv"
     8  
     9  	"github.com/go-graphite/carbonapi/expr/helper"
    10  	"github.com/go-graphite/carbonapi/expr/interfaces"
    11  	"github.com/go-graphite/carbonapi/expr/types"
    12  	"github.com/go-graphite/carbonapi/pkg/parser"
    13  )
    14  
    15  type nonNegativeDerivative struct{}
    16  
    17  func GetOrder() interfaces.Order {
    18  	return interfaces.Any
    19  }
    20  
    21  func New(configFile string) []interfaces.FunctionMetadata {
    22  	res := make([]interfaces.FunctionMetadata, 0)
    23  	f := &nonNegativeDerivative{}
    24  	functions := []string{"nonNegativeDerivative"}
    25  	for _, n := range functions {
    26  		res = append(res, interfaces.FunctionMetadata{Name: n, F: f})
    27  	}
    28  	return res
    29  }
    30  
    31  func (f *nonNegativeDerivative) Do(ctx context.Context, eval interfaces.Evaluator, e parser.Expr, from, until int64, values map[parser.MetricRequest][]*types.MetricData) ([]*types.MetricData, error) {
    32  	args, err := helper.GetSeriesArg(ctx, eval, e.Arg(0), from, until, values)
    33  	if err != nil {
    34  		return nil, err
    35  	}
    36  
    37  	maxValue, err := e.GetFloatNamedOrPosArgDefault("maxValue", 1, math.NaN())
    38  	if err != nil {
    39  		return nil, err
    40  	}
    41  	minValue, err := e.GetFloatNamedOrPosArgDefault("minValue", 2, math.NaN())
    42  	if err != nil {
    43  		return nil, err
    44  	}
    45  	hasMax := !math.IsNaN(maxValue)
    46  	hasMin := !math.IsNaN(minValue)
    47  
    48  	if hasMax && hasMin && maxValue <= minValue {
    49  		return nil, errors.New("minValue must be lower than maxValue")
    50  	}
    51  	if hasMax && !hasMin {
    52  		minValue = 0
    53  	}
    54  
    55  	argMask := 0
    56  	if _, ok := e.NamedArg("maxValue"); ok || e.ArgsLen() > 1 {
    57  		argMask |= 1
    58  	}
    59  	if _, ok := e.NamedArg("minValue"); ok || e.ArgsLen() > 2 {
    60  		argMask |= 2
    61  	}
    62  
    63  	var maxValueStr string
    64  	var minValueStr string
    65  	if hasMax {
    66  		maxValueStr = strconv.FormatFloat(maxValue, 'g', -1, 64)
    67  	}
    68  	if hasMin {
    69  		minValueStr = strconv.FormatFloat(minValue, 'g', -1, 64)
    70  	}
    71  
    72  	result := make([]*types.MetricData, len(args))
    73  	for i, a := range args {
    74  		var name string
    75  		switch argMask {
    76  		case 3:
    77  			name = "nonNegativeDerivative(" + a.Name + "," + maxValueStr + "," + minValueStr + ")"
    78  		case 2:
    79  			name = "nonNegativeDerivative(" + a.Name + ",minValue=" + minValueStr + ")"
    80  		case 1:
    81  			name = "nonNegativeDerivative(" + a.Name + "," + maxValueStr + ")"
    82  		case 0:
    83  			name = "nonNegativeDerivative(" + a.Name + ")"
    84  		}
    85  
    86  		r := a.CopyLink()
    87  		r.Name = name
    88  		r.Values = make([]float64, len(a.Values))
    89  		r.Tags["nonNegativeDerivative"] = "1"
    90  		result[i] = r
    91  
    92  		if len(a.Values) == 0 {
    93  			continue
    94  		}
    95  
    96  		prev := a.Values[0]
    97  		for i, v := range a.Values {
    98  			if i == 0 || math.IsNaN(a.Values[i]) || math.IsNaN(a.Values[i-1]) {
    99  				r.Values[i] = math.NaN()
   100  				prev = v
   101  				continue
   102  			}
   103  			// TODO(civil): Figure out if we can optimize this now when we have NaNs
   104  			diff := v - prev
   105  			if diff >= 0 {
   106  				r.Values[i] = diff
   107  			} else if hasMax && maxValue >= v {
   108  				r.Values[i] = ((maxValue - prev) + (v - minValue) + 1)
   109  			} else if hasMin && minValue <= v {
   110  				r.Values[i] = (v - minValue)
   111  			} else {
   112  				r.Values[i] = math.NaN()
   113  			}
   114  			prev = v
   115  		}
   116  	}
   117  	return result, nil
   118  }
   119  
   120  // Description is auto-generated description, based on output of https://github.com/graphite-project/graphite-web
   121  func (f *nonNegativeDerivative) Description() map[string]types.FunctionDescription {
   122  	return map[string]types.FunctionDescription{
   123  		"nonNegativeDerivative": {
   124  			Description: "Same as the derivative function above, but ignores datapoints that trend\ndown.  Useful for counters that increase for a long time, then wrap or\nreset. (Such as if a network interface is destroyed and recreated by unloading\nand re-loading a kernel module, common with USB / WiFi cards.\n\nExample:\n\n.. code-block:: none\n\n  &target=nonNegativederivative(company.server.application01.ifconfig.TXPackets)",
   125  			Function:    "nonNegativeDerivative(seriesList, maxValue=None)",
   126  			Group:       "Transform",
   127  			Module:      "graphite.render.functions",
   128  			Name:        "nonNegativeDerivative",
   129  			Params: []types.FunctionParam{
   130  				{
   131  					Name:     "seriesList",
   132  					Required: true,
   133  					Type:     types.SeriesList,
   134  				},
   135  				{
   136  					Name: "maxValue",
   137  					Type: types.Float,
   138  				},
   139  				{
   140  					Name: "minValue",
   141  					Type: types.Float,
   142  				},
   143  			},
   144  			NameChange:   true, // name changed
   145  			ValuesChange: true, // values changed
   146  		},
   147  	}
   148  }