From f60495ab40248aa441ef19c4025dba2726fcfee1 Mon Sep 17 00:00:00 2001 From: Stephanie Gredell Date: Wed, 18 Jun 2025 11:02:50 -0700 Subject: [PATCH] Fix up cachenode --- internal/simulation/cachenode.go | 93 +++++++++++++++++++++++--------- 1 file changed, 69 insertions(+), 24 deletions(-) diff --git a/internal/simulation/cachenode.go b/internal/simulation/cachenode.go index 7668a9c..2f9d6c1 100644 --- a/internal/simulation/cachenode.go +++ b/internal/simulation/cachenode.go @@ -1,5 +1,17 @@ package simulation +import ( + "math/rand" + "sort" +) + +type CacheEntry struct { + Value interface{} + Timestamp int + ExpireAt int + AccessCount int +} + type CacheNode struct { ID string Label string @@ -7,10 +19,11 @@ type CacheNode struct { MaxEntries int EvictionPolicy string CurrentLoad int - Queue []Request - Cache map[string]interface{} + Queue []*Request + Cache map[string]CacheEntry Alive bool Targets []string + Output []*Request } func (c *CacheNode) GetID() string { @@ -21,42 +34,74 @@ func (c *CacheNode) Type() string { return "cache" } -func (c *CacheNode) GetQueue() []Request { - return c.Queue +func (c *CacheNode) IsAlive() bool { + return c.Alive } -func (c *CacheNode) Tick(tick int) { + +func (c *CacheNode) Tick(tick int, currentTimeMs int) { + for key, entry := range c.Cache { + if currentTimeMs > entry.ExpireAt { + delete(c.Cache, key) + } + } + if len(c.Cache) > c.MaxEntries { evictCount := len(c.Cache) - c.MaxEntries + keys := make([]string, 0, len(c.Cache)) + for k := range c.Cache { + keys = append(keys, k) + } - i := 0 + switch c.EvictionPolicy { + case "Random": + rand.Shuffle(len(keys), func(i, j int) { keys[i], keys[j] = keys[j], keys[i] }) + case "LRU": + sort.Slice(keys, func(i, j int) bool { + return c.Cache[keys[i]].Timestamp < c.Cache[keys[j]].Timestamp + }) + case "LFU": + sort.Slice(keys, func(i, j int) bool { + return c.Cache[keys[i]].AccessCount < c.Cache[keys[j]].AccessCount + }) + } - for k := range c.Cache { - delete(c.Cache, k) - i++ - if i >= evictCount { - break + for i := 0; i < evictCount && i < len(keys); i++ { + delete(c.Cache, keys[i]) + } + } + + toProcess := min(len(c.Queue), 10) + for i := 0; i < toProcess; i++ { + req := c.Queue[i] + if entry, found := c.Cache[req.ID]; found && currentTimeMs <= entry.ExpireAt { + // Cache hit + req.LatencyMS += 2 + req.Path = append(req.Path, c.ID+"(hit)") + } else { + // Cache miss + req.LatencyMS += 5 + req.Path = append(req.Path, c.ID+"(miss)") + c.Cache[req.ID] = CacheEntry{ + Value: req, + Timestamp: currentTimeMs, + ExpireAt: currentTimeMs + c.CacheTTL*1000, } + c.Output = append(c.Output, req) } } + c.Queue = c.Queue[toProcess:] } func (c *CacheNode) Receive(req *Request) { - c.Queue = append(c.Queue, *req) + c.Queue = append(c.Queue, req) } func (c *CacheNode) Emit() []*Request { - var out []*Request - - // emit everything in a single tick. - for _, req := range c.Queue { - // check cache - if _, ok := c.Cache[req.ID]; !ok { - c.Cache[req.ID] = struct{}{} - out = append(out, &req) - } - } - c.Queue = nil + out := append([]*Request(nil), c.Output...) + c.Output = c.Output[:0] return out } -func (c *CacheNode) IsAlive() bool { return c.Alive } +func (c *CacheNode) AddTarget(targetID string) { + c.Targets = append(c.Targets, targetID) +}