mirror of
				https://github.com/ClusterCockpit/cc-metric-store.git
				synced 2025-11-04 10:45:07 +01:00 
			
		
		
		
	better JSON encoding (less allocs)
This commit is contained in:
		
							
								
								
									
										23
									
								
								api.go
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								api.go
									
									
									
									
									
								
							@@ -8,6 +8,7 @@ import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"log"
 | 
			
		||||
	"math"
 | 
			
		||||
	"net/http"
 | 
			
		||||
@@ -21,13 +22,13 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type ApiMetricData struct {
 | 
			
		||||
	Error *string `json:"error,omitempty"`
 | 
			
		||||
	From  int64   `json:"from"`
 | 
			
		||||
	To    int64   `json:"to"`
 | 
			
		||||
	Data  []Float `json:"data,omitempty"`
 | 
			
		||||
	Avg   Float   `json:"avg"`
 | 
			
		||||
	Min   Float   `json:"min"`
 | 
			
		||||
	Max   Float   `json:"max"`
 | 
			
		||||
	Error *string    `json:"error,omitempty"`
 | 
			
		||||
	From  int64      `json:"from"`
 | 
			
		||||
	To    int64      `json:"to"`
 | 
			
		||||
	Data  FloatArray `json:"data,omitempty"`
 | 
			
		||||
	Avg   Float      `json:"avg"`
 | 
			
		||||
	Min   Float      `json:"min"`
 | 
			
		||||
	Max   Float      `json:"max"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TODO: Optimize this, just like the stats endpoint!
 | 
			
		||||
@@ -109,7 +110,13 @@ func handleWrite(rw http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dec := lineprotocol.NewDecoder(bufio.NewReader(r.Body))
 | 
			
		||||
	bytes, err := io.ReadAll(r.Body)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		http.Error(rw, err.Error(), http.StatusInternalServerError)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dec := lineprotocol.NewDecoderWithBytes(bytes)
 | 
			
		||||
	if err := decodeLine(dec); err != nil {
 | 
			
		||||
		http.Error(rw, err.Error(), http.StatusBadRequest)
 | 
			
		||||
		return
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										58
									
								
								float.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								float.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,58 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"math"
 | 
			
		||||
	"strconv"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Go's JSON encoder for floats does not support NaN (https://github.com/golang/go/issues/3480).
 | 
			
		||||
// This program uses NaN as a signal for missing data.
 | 
			
		||||
// For the HTTP JSON API to be able to handle NaN values,
 | 
			
		||||
// we have to use our own type which implements encoding/json.Marshaler itself.
 | 
			
		||||
type Float float32
 | 
			
		||||
 | 
			
		||||
var NaN Float = Float(math.NaN())
 | 
			
		||||
var nullAsBytes []byte = []byte("null")
 | 
			
		||||
 | 
			
		||||
func (f Float) IsNaN() bool {
 | 
			
		||||
	return math.IsNaN(float64(f))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f Float) MarshalJSON() ([]byte, error) {
 | 
			
		||||
	if math.IsNaN(float64(f)) {
 | 
			
		||||
		return nullAsBytes, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return strconv.AppendFloat(make([]byte, 0, 10), float64(f), 'f', 1, 32), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *Float) UnmarshalJSON(input []byte) error {
 | 
			
		||||
	if string(input) == "null" {
 | 
			
		||||
		*f = NaN
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	val, err := strconv.ParseFloat(string(input), 64)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	*f = Float(val)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Same as `[]Float`, but can be marshaled to JSON with less allocations.
 | 
			
		||||
type FloatArray []Float
 | 
			
		||||
 | 
			
		||||
func (fa FloatArray) MarshalJSON() ([]byte, error) {
 | 
			
		||||
	buf := make([]byte, 0, 2+len(fa)*8)
 | 
			
		||||
	buf = append(buf, '[')
 | 
			
		||||
	if len(fa) > 0 {
 | 
			
		||||
		buf = strconv.AppendFloat(buf, float64(fa[0]), 'f', 1, 32)
 | 
			
		||||
		for i := 1; i < len(fa); i++ {
 | 
			
		||||
			buf = append(buf, ',')
 | 
			
		||||
			buf = strconv.AppendFloat(buf, float64(fa[i]), 'f', 1, 32)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	buf = append(buf, ']')
 | 
			
		||||
	return buf, nil
 | 
			
		||||
}
 | 
			
		||||
@@ -4,8 +4,6 @@ import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
	"math"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
@@ -13,41 +11,6 @@ import (
 | 
			
		||||
	"github.com/nats-io/nats.go"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Go's JSON encoder for floats does not support NaN (https://github.com/golang/go/issues/3480).
 | 
			
		||||
// This program uses NaN as a signal for missing data.
 | 
			
		||||
// For the HTTP JSON API to be able to handle NaN values,
 | 
			
		||||
// we have to use our own type which implements encoding/json.Marshaler itself.
 | 
			
		||||
type Float float64
 | 
			
		||||
 | 
			
		||||
var NaN Float = Float(math.NaN())
 | 
			
		||||
 | 
			
		||||
func (f Float) IsNaN() bool {
 | 
			
		||||
	return math.IsNaN(float64(f))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f Float) MarshalJSON() ([]byte, error) {
 | 
			
		||||
	if math.IsNaN(float64(f)) {
 | 
			
		||||
		return []byte("null"), nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return []byte(strconv.FormatFloat(float64(f), 'f', 2, 64)), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *Float) UnmarshalJSON(input []byte) error {
 | 
			
		||||
	s := string(input)
 | 
			
		||||
	if s == "null" {
 | 
			
		||||
		*f = NaN
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	val, err := strconv.ParseFloat(s, 64)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	*f = Float(val)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Metric struct {
 | 
			
		||||
	Name  string
 | 
			
		||||
	minfo metricInfo
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user