refactor(metrics): remove pointers from type parameter T to avoid unnecessary indirection

This commit is contained in:
yusing
2025-08-28 22:30:40 +08:00
parent 199b8fad20
commit 755cbd7aec
4 changed files with 20 additions and 23 deletions

View File

@@ -6,7 +6,7 @@ import (
) )
type Entries[T any] struct { type Entries[T any] struct {
entries [maxEntries]*T entries [maxEntries]T
index int index int
count int count int
interval time.Duration interval time.Duration
@@ -16,17 +16,14 @@ type Entries[T any] struct {
const maxEntries = 100 const maxEntries = 100
func newEntries[T any](duration time.Duration) *Entries[T] { func newEntries[T any](duration time.Duration) *Entries[T] {
interval := duration / maxEntries interval := max(duration/maxEntries, time.Second)
if interval < time.Second {
interval = time.Second
}
return &Entries[T]{ return &Entries[T]{
interval: interval, interval: interval,
lastAdd: time.Now(), lastAdd: time.Now(),
} }
} }
func (e *Entries[T]) Add(now time.Time, info *T) { func (e *Entries[T]) Add(now time.Time, info T) {
if now.Sub(e.lastAdd) < e.interval { if now.Sub(e.lastAdd) < e.interval {
return return
} }
@@ -38,11 +35,11 @@ func (e *Entries[T]) Add(now time.Time, info *T) {
e.lastAdd = now e.lastAdd = now
} }
func (e *Entries[T]) Get() []*T { func (e *Entries[T]) Get() []T {
if e.count < maxEntries { if e.count < maxEntries {
return e.entries[:e.count] return e.entries[:e.count]
} }
res := make([]*T, maxEntries) res := make([]T, maxEntries)
copy(res, e.entries[e.index:]) copy(res, e.entries[e.index:])
copy(res[maxEntries-e.index:], e.entries[:e.index]) copy(res[maxEntries-e.index:], e.entries[:e.index])
return res return res
@@ -57,7 +54,7 @@ func (e *Entries[T]) MarshalJSON() ([]byte, error) {
func (e *Entries[T]) UnmarshalJSON(data []byte) error { func (e *Entries[T]) UnmarshalJSON(data []byte) error {
var v struct { var v struct {
Entries []*T `json:"entries"` Entries []T `json:"entries"`
Interval time.Duration `json:"interval"` Interval time.Duration `json:"interval"`
} }
if err := json.Unmarshal(data, &v); err != nil { if err := json.Unmarshal(data, &v); err != nil {

View File

@@ -3,6 +3,7 @@ package period
import ( import (
"errors" "errors"
"net/http" "net/http"
"net/url"
"time" "time"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@@ -29,6 +30,7 @@ type ResponseType[AggregateT any] struct {
// //
// If the request is a websocket request, it serves the data for the given period for every interval. // If the request is a websocket request, it serves the data for the given period for every interval.
func (p *Poller[T, AggregateT]) ServeHTTP(c *gin.Context) { func (p *Poller[T, AggregateT]) ServeHTTP(c *gin.Context) {
period := Filter(c.Query("period"))
query := c.Request.URL.Query() query := c.Request.URL.Query()
if httpheaders.IsWebsocket(c.Request.Header) { if httpheaders.IsWebsocket(c.Request.Header) {
@@ -42,10 +44,10 @@ func (p *Poller[T, AggregateT]) ServeHTTP(c *gin.Context) {
interval = minInterval interval = minInterval
} }
websocket.PeriodicWrite(c, interval, func() (any, error) { websocket.PeriodicWrite(c, interval, func() (any, error) {
return p.getRespData(c.Request) return p.getRespData(period, query)
}) })
} else { } else {
data, err := p.getRespData(c.Request) data, err := p.getRespData(period, query)
if err != nil { if err != nil {
c.Error(apitypes.InternalServerError(err, "failed to get response data")) c.Error(apitypes.InternalServerError(err, "failed to get response data"))
return return
@@ -58,13 +60,11 @@ func (p *Poller[T, AggregateT]) ServeHTTP(c *gin.Context) {
} }
} }
func (p *Poller[T, AggregateT]) getRespData(r *http.Request) (any, error) { func (p *Poller[T, AggregateT]) getRespData(period Filter, query url.Values) (any, error) {
query := r.URL.Query()
period := query.Get("period")
if period == "" { if period == "" {
return p.GetLastResult(), nil return p.GetLastResult(), nil
} }
rangeData, ok := p.Get(Filter(period)) rangeData, ok := p.Get(period)
if !ok { if !ok {
return nil, errors.New("invalid period") return nil, errors.New("invalid period")
} }

View File

@@ -32,7 +32,7 @@ func NewPeriod[T any]() *Period[T] {
} }
} }
func (p *Period[T]) Add(info *T) { func (p *Period[T]) Add(info T) {
p.mu.Lock() p.mu.Lock()
defer p.mu.Unlock() defer p.mu.Unlock()
now := time.Now() now := time.Now()
@@ -41,7 +41,7 @@ func (p *Period[T]) Add(info *T) {
} }
} }
func (p *Period[T]) Get(filter Filter) ([]*T, bool) { func (p *Period[T]) Get(filter Filter) ([]T, bool) {
p.mu.RLock() p.mu.RLock()
defer p.mu.RUnlock() defer p.mu.RUnlock()
period, ok := p.Entries[filter] period, ok := p.Entries[filter]

View File

@@ -17,16 +17,16 @@ import (
) )
type ( type (
PollFunc[T any] func(ctx context.Context, lastResult *T) (*T, error) PollFunc[T any] func(ctx context.Context, lastResult T) (T, error)
AggregateFunc[T any, AggregateT json.Marshaler] func(entries []*T, query url.Values) (total int, result AggregateT) AggregateFunc[T any, AggregateT json.Marshaler] func(entries []T, query url.Values) (total int, result AggregateT)
FilterFunc[T any] func(entries []*T, keyword string) (filtered []*T) FilterFunc[T any] func(entries []T, keyword string) (filtered []T)
Poller[T any, AggregateT json.Marshaler] struct { Poller[T any, AggregateT json.Marshaler] struct {
name string name string
poll PollFunc[T] poll PollFunc[T]
aggregate AggregateFunc[T, AggregateT] aggregate AggregateFunc[T, AggregateT]
resultFilter FilterFunc[T] resultFilter FilterFunc[T]
period *Period[T] period *Period[T]
lastResult atomic.Value[*T] lastResult atomic.Value[T]
errs []pollErr errs []pollErr
} }
pollErr struct { pollErr struct {
@@ -188,10 +188,10 @@ func (p *Poller[T, AggregateT]) Start() {
}() }()
} }
func (p *Poller[T, AggregateT]) Get(filter Filter) ([]*T, bool) { func (p *Poller[T, AggregateT]) Get(filter Filter) ([]T, bool) {
return p.period.Get(filter) return p.period.Get(filter)
} }
func (p *Poller[T, AggregateT]) GetLastResult() *T { func (p *Poller[T, AggregateT]) GetLastResult() T {
return p.lastResult.Load() return p.lastResult.Load()
} }