Skip to content

Commit

Permalink
cache package moved to models
Browse files Browse the repository at this point in the history
  • Loading branch information
Francesco Cosentino committed Jan 15, 2023
1 parent f739134 commit 09e7005
Show file tree
Hide file tree
Showing 2 changed files with 169 additions and 0 deletions.
109 changes: 109 additions & 0 deletions models/item.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package models

// Item represents an item in the cache. It has a key, value, expiration duration, and a last access time field.

import (
"reflect"
"strings"
"sync"
"sync/atomic"
"time"

// "https://github.com/kelindar/binary"
"github.com/hyp3rd/hypercache/errors"
"github.com/shamaton/msgpack/v2"
)

// Item is a struct that represents an item in the cache. It has a key, value, expiration duration, and a last access time field.
type Item struct {
Key string // key of the item
Value any // Value of the item
Expiration time.Duration // Expiration duration of the item
LastAccess time.Time // LastAccess time of the item
AccessCount uint // AccessCount of times the item has been accessed
}

// ItemPool is a pool of Item values.
var ItemPool = sync.Pool{
New: func() any {
return &Item{}
},
}

// FieldByName returns the value of the field of the Item struct with the given name.
// If the field does not exist, an empty reflect.Value is returned.
func (item *Item) FieldByName(name string) reflect.Value {
// Get the reflect.Value of the item pointer
v := reflect.ValueOf(item)

// Get the reflect.Value of the item struct itself by calling Elem() on the pointer value
f := v.Elem().FieldByName(name)

// If the field does not exist, return an empty reflect.Value
if !f.IsValid() {
return reflect.Value{}
}
// Return the field value
return f
}

// Valid returns an error if the item is invalid, nil otherwise.
func (item *Item) Valid() error {
// Check for empty key
if item.Key == "" || strings.TrimSpace(item.Key) == "" {
return errors.ErrInvalidKey
}

// Check for nil value
if item.Value == nil {
return errors.ErrNilValue
}

// Check for negative expiration
if atomic.LoadInt64((*int64)(&item.Expiration)) < 0 {
atomic.StoreInt64((*int64)(&item.Expiration), 0)
return errors.ErrInvalidExpiration
}
return nil
}

// Touch updates the last access time of the item and increments the access count.
func (item *Item) Touch() {
item.LastAccess = time.Now()
item.AccessCount++
}

// Expired returns true if the item has expired, false otherwise.
func (item *Item) Expired() bool {
// If the expiration duration is 0, the item never expires
return item.Expiration > 0 && time.Since(item.LastAccess) > item.Expiration
}

// MarshalBinary implements the encoding.BinaryMarshaler interface.
// func (item *Item) MarshalBinary() (data []byte, err error) {
// buf := bytes.NewBuffer([]byte{})
// enc := gob.NewEncoder(buf)
// err = enc.Encode(item)
// if err != nil {
// return nil, err
// }
// return buf.Bytes(), nil
// }

// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface.
//
// func (item *Item) UnmarshalBinary(data []byte) error {
// buf := bytes.NewBuffer(data)
// dec := gob.NewDecoder(buf)
// return dec.Decode(item)
// }
//
// MarshalBinary implements the encoding.BinaryMarshaler interface.
func (item *Item) MarshalBinary() (data []byte, err error) {
return msgpack.Marshal(item)
}

// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface.
func (item *Item) UnmarshalBinary(data []byte) error {
return msgpack.Unmarshal(data, item)
}
60 changes: 60 additions & 0 deletions stats/collector.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package stats

import (
"fmt"

"github.com/hyp3rd/hypercache/errors"
"github.com/hyp3rd/hypercache/types"
)

// Collector is an interface that defines the methods that a stats collector should implement.
type Collector interface {
// Incr increments the count of a statistic by the given value.
Incr(stat types.Stat, value int64)
// Decr decrements the count of a statistic by the given value.
Decr(stat types.Stat, value int64)
// Timing records the time it took for an event to occur.
Timing(stat types.Stat, value int64)
// Gauge records the current value of a statistic.
Gauge(stat types.Stat, value int64)
// Histogram records the statistical distribution of a set of values.
Histogram(stat types.Stat, value int64)
// GetStats returns the collected statistics.
GetStats() Stats
}

// StatsCollectorRegistry holds the a registry of stats collectors.
var StatsCollectorRegistry = make(map[string]func() (Collector, error))

// NewStatsCollector creates a new stats collector.
// The statsCollectorName parameter is used to select the stats collector from the registry.
func NewStatsCollector(statsCollectorName string) (Collector, error) {
// Check the parameters.
if statsCollectorName == "" {
return nil, fmt.Errorf("%s: %s", errors.ErrParamCannotBeEmpty, "statsCollectorName")
}

createFunc, ok := StatsCollectorRegistry[statsCollectorName]
if !ok {
return nil, fmt.Errorf("%s: %s", errors.ErrStatsCollectorNotFound, statsCollectorName)
}

return createFunc()
}

// RegisterStatsCollector registers a new stats collector with the given name.
func RegisterStatsCollector(name string, createFunc func() (Collector, error)) {
StatsCollectorRegistry[name] = createFunc
}

func init() {
// Register the default stats collector.
RegisterStatsCollector("default", func() (Collector, error) {
var err error
collector := NewHistogramStatsCollector()
if collector == nil {
err = fmt.Errorf("%s: %s", errors.ErrStatsCollectorNotFound, "default")
}
return NewHistogramStatsCollector(), err
})
}

0 comments on commit 09e7005

Please sign in to comment.