diff --git a/internal/simulation/cdnnode.go b/internal/simulation/cdnnode.go new file mode 100644 index 0000000..7dc0b5b --- /dev/null +++ b/internal/simulation/cdnnode.go @@ -0,0 +1,89 @@ +package simulation + +import ( + "math/rand" +) + +type CDNNode struct { + ID string + Label string + TTL int + GeoReplication string + CachingStrategy string + Compression string + HTTP2 string + CacheHitRate float64 // % of requests served from edge cache + CurrentLoad int // optional: can track active queue length + Queue []*Request // incoming request queue + EdgeNodes map[string]*CDNNode // future expansion: simulate geographic edge clusters + Alive bool + Targets []string + output []*Request // cache HIT responses + missQueue []*Request // cache MISSes to forward +} + +func (n *CDNNode) GetID() string { return n.ID } +func (n *CDNNode) Type() string { return "cdn" } +func (n *CDNNode) IsAlive() bool { return n.Alive } +func (n *CDNNode) QueueState() []*Request { return n.Queue } + +func (n *CDNNode) Tick(tick int) { + if len(n.Queue) == 0 { + return + } + + maxProcessPerTick := 10 + processCount := min(len(n.Queue), maxProcessPerTick) + + // Avoid slice leak by reusing a cleared slice + queue := n.Queue + n.Queue = n.Queue[:0] + + for i := 0; i < processCount; i++ { + req := queue[i] + + // Simulate cache hit or miss + hitRate := n.CacheHitRate + if hitRate == 0 { + hitRate = 0.8 // default if unset + } + + if rand.Float64() < hitRate { + // Cache HIT + req.LatencyMS += 5 + req.Path = append(req.Path, n.ID) + n.output = append(n.output, req) + } else { + // Cache MISS + req.LatencyMS += 10 + req.Path = append(req.Path, n.ID+"(miss)") + n.missQueue = append(n.missQueue, req) + } + } + + // Optionally simulate cache expiration every 100 ticks + if tick%100 == 0 { + // e.g., simulate reduced hit rate or TTL expiration + } +} + +func (n *CDNNode) Receive(req *Request) { + if req == nil { + return + } + + geoLatency := rand.Intn(20) + 5 // 5–25ms routing delay + req.LatencyMS += geoLatency + + n.Queue = append(n.Queue, req) +} + +func (n *CDNNode) Emit() []*Request { + out := append(n.output, n.missQueue...) + + // Clear for next tick + n.output = n.output[:0] + n.missQueue = n.missQueue[:0] + + return out +}