refactor deltatracker

- embed mutex
- add example function
This commit is contained in:
henrygd
2025-09-16 22:09:46 -04:00
parent 4e0ca7c2ba
commit 1b9e781d45
2 changed files with 32 additions and 15 deletions

View File

@@ -16,7 +16,7 @@ type Numeric interface {
// K is the key type (e.g., int, string). // K is the key type (e.g., int, string).
// V is the value type (e.g., int, int64, float32, float64). // V is the value type (e.g., int, int64, float32, float64).
type DeltaTracker[K comparable, V Numeric] struct { type DeltaTracker[K comparable, V Numeric] struct {
mu sync.RWMutex sync.RWMutex
current map[K]V current map[K]V
previous map[K]V previous map[K]V
} }
@@ -31,15 +31,15 @@ func NewDeltaTracker[K comparable, V Numeric]() *DeltaTracker[K, V] {
// Set records the current value for a given ID. // Set records the current value for a given ID.
func (t *DeltaTracker[K, V]) Set(id K, value V) { func (t *DeltaTracker[K, V]) Set(id K, value V) {
t.mu.Lock() t.Lock()
defer t.mu.Unlock() defer t.Unlock()
t.current[id] = value t.current[id] = value
} }
// Deltas returns a map of all calculated deltas for the current interval. // Deltas returns a map of all calculated deltas for the current interval.
func (t *DeltaTracker[K, V]) Deltas() map[K]V { func (t *DeltaTracker[K, V]) Deltas() map[K]V {
t.mu.RLock() t.RLock()
defer t.mu.RUnlock() defer t.RUnlock()
deltas := make(map[K]V) deltas := make(map[K]V)
for id, currentVal := range t.current { for id, currentVal := range t.current {
@@ -55,8 +55,8 @@ func (t *DeltaTracker[K, V]) Deltas() map[K]V {
// Delta returns the delta for a single key. // Delta returns the delta for a single key.
// Returns 0 if the key doesn't exist or has no previous value. // Returns 0 if the key doesn't exist or has no previous value.
func (t *DeltaTracker[K, V]) Delta(id K) V { func (t *DeltaTracker[K, V]) Delta(id K) V {
t.mu.RLock() t.RLock()
defer t.mu.RUnlock() defer t.RUnlock()
currentVal, currentOk := t.current[id] currentVal, currentOk := t.current[id]
if !currentOk { if !currentOk {
@@ -73,8 +73,8 @@ func (t *DeltaTracker[K, V]) Delta(id K) V {
// Cycle prepares the tracker for the next interval. // Cycle prepares the tracker for the next interval.
func (t *DeltaTracker[K, V]) Cycle() { func (t *DeltaTracker[K, V]) Cycle() {
t.mu.Lock() t.Lock()
defer t.mu.Unlock() defer t.Unlock()
t.previous = t.current t.previous = t.current
t.current = make(map[K]V) t.current = make(map[K]V)
} }

View File

@@ -1,11 +1,28 @@
// Package deltatracker provides a tracker for calculating differences in numeric values over time.
package deltatracker package deltatracker
import ( import (
"fmt"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func ExampleDeltaTracker() {
tracker := NewDeltaTracker[string, int]()
tracker.Set("key1", 10)
tracker.Set("key2", 20)
tracker.Cycle()
tracker.Set("key1", 15)
tracker.Set("key2", 30)
fmt.Println(tracker.Delta("key1"))
fmt.Println(tracker.Delta("key2"))
fmt.Println(tracker.Deltas())
// Output: 5
// 10
// map[key1:5 key2:10]
}
func TestNewDeltaTracker(t *testing.T) { func TestNewDeltaTracker(t *testing.T) {
tracker := NewDeltaTracker[string, int]() tracker := NewDeltaTracker[string, int]()
assert.NotNil(t, tracker) assert.NotNil(t, tracker)
@@ -17,8 +34,8 @@ func TestSet(t *testing.T) {
tracker := NewDeltaTracker[string, int]() tracker := NewDeltaTracker[string, int]()
tracker.Set("key1", 10) tracker.Set("key1", 10)
tracker.mu.RLock() tracker.RLock()
defer tracker.mu.RUnlock() defer tracker.RUnlock()
assert.Equal(t, 10, tracker.current["key1"]) assert.Equal(t, 10, tracker.current["key1"])
} }
@@ -55,21 +72,21 @@ func TestCycle(t *testing.T) {
tracker.Set("key2", 20) tracker.Set("key2", 20)
// Verify current has values // Verify current has values
tracker.mu.RLock() tracker.RLock()
assert.Equal(t, 10, tracker.current["key1"]) assert.Equal(t, 10, tracker.current["key1"])
assert.Equal(t, 20, tracker.current["key2"]) assert.Equal(t, 20, tracker.current["key2"])
assert.Empty(t, tracker.previous) assert.Empty(t, tracker.previous)
tracker.mu.RUnlock() tracker.RUnlock()
tracker.Cycle() tracker.Cycle()
// After cycle, previous should have the old current values // After cycle, previous should have the old current values
// and current should be empty // and current should be empty
tracker.mu.RLock() tracker.RLock()
assert.Empty(t, tracker.current) assert.Empty(t, tracker.current)
assert.Equal(t, 10, tracker.previous["key1"]) assert.Equal(t, 10, tracker.previous["key1"])
assert.Equal(t, 20, tracker.previous["key2"]) assert.Equal(t, 20, tracker.previous["key2"])
tracker.mu.RUnlock() tracker.RUnlock()
} }
func TestCompleteWorkflow(t *testing.T) { func TestCompleteWorkflow(t *testing.T) {