From 3e8965d527a12c9cb34135f9ca796bc2c825211e Mon Sep 17 00:00:00 2001 From: Stephanie Gredell Date: Mon, 23 Jun 2025 18:35:43 -0700 Subject: [PATCH] Add GetQueue method to simulation nodes and clean up nodes --- internal/simulation/cachenode.go | 4 + internal/simulation/cdnnode.go | 4 + internal/simulation/datapipelinenode.go | 4 + internal/simulation/loadbalancer.go | 29 ++- internal/simulation/messagequeuenode.go | 4 + internal/simulation/microservicenode.go | 14 +- internal/simulation/monitoringnode.go | 4 + .../simulation/testdata/complex_design.json | 221 +++++++++++++++++- .../simulation/testdata/simple_design.json | 33 +++ internal/simulation/thirdpartyservicenode.go | 6 + 10 files changed, 307 insertions(+), 16 deletions(-) create mode 100644 internal/simulation/testdata/simple_design.json diff --git a/internal/simulation/cachenode.go b/internal/simulation/cachenode.go index 90791cb..370bcc2 100644 --- a/internal/simulation/cachenode.go +++ b/internal/simulation/cachenode.go @@ -109,3 +109,7 @@ func (c *CacheNode) AddTarget(targetID string) { func (c *CacheNode) GetTargets() []string { return c.Targets } + +func (n *CacheNode) GetQueue() []*Request { + return n.Queue +} diff --git a/internal/simulation/cdnnode.go b/internal/simulation/cdnnode.go index fd3afba..af75b25 100644 --- a/internal/simulation/cdnnode.go +++ b/internal/simulation/cdnnode.go @@ -87,3 +87,7 @@ func (n *CDNNode) Emit() []*Request { func (n *CDNNode) GetTargets() []string { return n.Targets } + +func (n *CDNNode) GetQueue() []*Request { + return n.Queue +} diff --git a/internal/simulation/datapipelinenode.go b/internal/simulation/datapipelinenode.go index 190cd4d..02e1142 100644 --- a/internal/simulation/datapipelinenode.go +++ b/internal/simulation/datapipelinenode.go @@ -108,3 +108,7 @@ func (n *DataPipelineNode) IsAlive() bool { return n.Alive } func (n *DataPipelineNode) GetTargets() []string { return n.Targets } + +func (n *DataPipelineNode) GetQueue() []*Request { + return n.Queue +} diff --git a/internal/simulation/loadbalancer.go b/internal/simulation/loadbalancer.go index 30de532..53cdaf4 100644 --- a/internal/simulation/loadbalancer.go +++ b/internal/simulation/loadbalancer.go @@ -1,7 +1,14 @@ package simulation +import ( + "fmt" + "math/rand" +) + type LoadBalancerNode struct { ID string + Label string + Algorithm string Queue []*Request Targets []string Counter int @@ -22,7 +29,7 @@ func (lb *LoadBalancerNode) IsAlive() bool { } func (lb *LoadBalancerNode) Receive(req *Request) { - lb.Queue = append(lb.Queue) + lb.Queue = append(lb.Queue, req) } func (lb *LoadBalancerNode) Tick(tick int, currentTimeMs int) { @@ -33,8 +40,16 @@ func (lb *LoadBalancerNode) Tick(tick int, currentTimeMs int) { continue } - target := lb.Targets[lb.Counter%len(lb.Targets)] - lb.Counter++ + var target string + switch lb.Algorithm { + case "random": + target = lb.Targets[rand.Intn(len(lb.Targets))] + case "round-robin": + fallthrough + default: + target = lb.Targets[lb.Counter%len(lb.Targets)] + lb.Counter++ + } req.Path = append([]string{target}, req.Path...) @@ -51,3 +66,11 @@ func (lb *LoadBalancerNode) Emit() []*Request { lb.Processed = nil return out } + +func (lb *LoadBalancerNode) GetTargets() []string { + return lb.Targets +} + +func (lb *LoadBalancerNode) GetQueue() []*Request { + return lb.Queue +} diff --git a/internal/simulation/messagequeuenode.go b/internal/simulation/messagequeuenode.go index b5db695..50fe648 100644 --- a/internal/simulation/messagequeuenode.go +++ b/internal/simulation/messagequeuenode.go @@ -95,3 +95,7 @@ func (n *MessageQueueNode) Emit() []*Request { func (n *MessageQueueNode) GetTargets() []string { return n.Targets } + +func (n *MessageQueueNode) GetQueue() []*Request { + return n.Queue +} diff --git a/internal/simulation/microservicenode.go b/internal/simulation/microservicenode.go index 20dc5f6..b47028b 100644 --- a/internal/simulation/microservicenode.go +++ b/internal/simulation/microservicenode.go @@ -70,16 +70,6 @@ func (n *MicroserviceNode) Tick(tick int, currentTimeMs int) { n.Queue = n.Queue[toProcess:] } -func simulateFailure() bool { - // Simulate 10% failure rate - return randInt(1, 100) <= 10 -} - -func simulateSuccess() bool { - // Simulate 90% success rate - return randInt(1, 100) <= 90 -} - -func randInt(min, max int) int { - return min + rand.Intn(max-min+1) +func (n *MicroserviceNode) GetQueue() []*Request { + return n.Queue } diff --git a/internal/simulation/monitoringnode.go b/internal/simulation/monitoringnode.go index d44552f..052cd20 100644 --- a/internal/simulation/monitoringnode.go +++ b/internal/simulation/monitoringnode.go @@ -67,3 +67,7 @@ func (n *MonitoringNode) Tick(tick int, currentTimeMs int) { func (n *MonitoringNode) GetTargets() []string { return n.Targets } + +func (n *MonitoringNode) GetQueue() []*Request { + return n.Queue +} diff --git a/internal/simulation/testdata/complex_design.json b/internal/simulation/testdata/complex_design.json index fd0c1ec..9fabc59 100644 --- a/internal/simulation/testdata/complex_design.json +++ b/internal/simulation/testdata/complex_design.json @@ -1 +1,220 @@ -{"nodes":[{"id":"node-1","type":"loadBalancer","position":{"x":-4,"y":0},"props":{"label":"Load Balancer","algorithm":"round-robin"}},{"id":"node-2","type":"webserver","position":{"x":-1,"y":0},"props":{"label":"Web Server","instanceSize":"medium"}},{"id":"node-3","type":"database","position":{"x":177,"y":-176},"props":{"label":"Database","replication":1}},{"id":"node-4","type":"cache","position":{"x":204,"y":-78},"props":{"label":"Cache","cacheTTL":60,"maxEntries":100000,"evictionPolicy":"LRU"}},{"id":"node-5","type":"messageQueue","position":{"x":0,"y":0},"props":{"label":"MQ","maxSize":10000,"retentionSeconds":600}},{"id":"node-6","type":"cdn","position":{"x":20,"y":69},"props":{"label":"CDN","ttl":3600,"geoReplication":"global","cachingStrategy":"cache-first","compression":"brotli","http2":"enabled"}},{"id":"node-7","type":"microservice","position":{"x":0,"y":0},"props":{"label":"Service","instanceCount":3,"instanceSize":"medium","scalingStrategy":"auto","apiVersion":"v1"}},{"id":"node-8","type":"data pipeline","position":{"x":-453,"y":-121},"props":{"label":"pipeline","batchSize":500,"transformation":"map"}},{"id":"node-9","type":"monitoring/alerting","position":{"x":0,"y":0},"props":{"label":"monitor","tool":"Prometheus","alertThreshold":80}},{"id":"node-10","type":"third party service","position":{"x":0,"y":0},"props":{"label":"third party service","provider":"Stripe","latency":200}}],"connections":[{"source":"node-0","target":"node-1","label":"Read traffic","direction":"forward","protocol":"HTTP","tls":false,"capacity":1000},{"source":"node-1","target":"node-2","label":"Read traffic","direction":"forward","protocol":"HTTP","tls":false,"capacity":1000},{"source":"node-2","target":"node-3","label":"Read traffic","direction":"forward","protocol":"HTTP","tls":false,"capacity":1000},{"source":"node-3","target":"node-4","label":"Read traffic","direction":"forward","protocol":"HTTP","tls":false,"capacity":1000},{"source":"node-4","target":"node-5","label":"Read traffic","direction":"forward","protocol":"HTTP","tls":false,"capacity":1000},{"source":"node-5","target":"node-6","label":"Read traffic","direction":"forward","protocol":"HTTP","tls":false,"capacity":1000},{"source":"node-6","target":"node-7","label":"Read traffic","direction":"forward","protocol":"HTTP","tls":false,"capacity":1000},{"source":"node-8","target":"node-7","label":"Read traffic","direction":"forward","protocol":"HTTP","tls":false,"capacity":1000},{"source":"node-8","target":"node-10","label":"Read traffic","direction":"forward","protocol":"HTTP","tls":false,"capacity":1000},{"source":"node-10","target":"node-9","label":"Read traffic","direction":"forward","protocol":"HTTP","tls":false,"capacity":1000},{"source":"node-9","target":"node-0","label":"Read traffic","direction":"forward","protocol":"HTTP","tls":false,"capacity":1000}]} +{ + "nodes": [ + { + "id": "node-1", + "type": "loadBalancer", + "position": { + "x": -4, + "y": 0 + }, + "props": { + "label": "Load Balancer", + "algorithm": "round-robin" + } + }, + { + "id": "node-2", + "type": "webserver", + "position": { + "x": -1, + "y": 0 + }, + "props": { + "label": "Web Server", + "instanceSize": "medium" + } + }, + { + "id": "node-3", + "type": "database", + "position": { + "x": 177, + "y": -176 + }, + "props": { + "label": "Database", + "replication": 1 + } + }, + { + "id": "node-4", + "type": "cache", + "position": { + "x": 204, + "y": -78 + }, + "props": { + "label": "Cache", + "cacheTTL": 60, + "maxEntries": 100000, + "evictionPolicy": "LRU" + } + }, + { + "id": "node-5", + "type": "messageQueue", + "position": { + "x": 0, + "y": 0 + }, + "props": { + "label": "MQ", + "maxSize": 10000, + "retentionSeconds": 600 + } + }, + { + "id": "node-6", + "type": "cdn", + "position": { + "x": 20, + "y": 69 + }, + "props": { + "label": "CDN", + "ttl": 3600, + "geoReplication": "global", + "cachingStrategy": "cache-first", + "compression": "brotli", + "http2": "enabled" + } + }, + { + "id": "node-7", + "type": "microservice", + "position": { + "x": 0, + "y": 0 + }, + "props": { + "label": "Service", + "instanceCount": 3, + "instanceSize": "medium", + "scalingStrategy": "auto", + "apiVersion": "v1" + } + }, + { + "id": "node-8", + "type": "data pipeline", + "position": { + "x": -453, + "y": -121 + }, + "props": { + "label": "pipeline", + "batchSize": 500, + "transformation": "map" + } + }, + { + "id": "node-9", + "type": "monitoring/alerting", + "position": { + "x": 0, + "y": 0 + }, + "props": { + "label": "monitor", + "tool": "Prometheus", + "alertThreshold": 80 + } + }, + { + "id": "node-10", + "type": "third party service", + "position": { + "x": 0, + "y": 0 + }, + "props": { + "label": "third party service", + "provider": "Stripe", + "latency": 200 + } + } + ], + "connections": [ + { + "source": "node-1", + "target": "node-2", + "label": "Read traffic", + "direction": "forward", + "protocol": "HTTP", + "tls": false, + "capacity": 1000 + }, + { + "source": "node-2", + "target": "node-3", + "label": "Read traffic", + "direction": "forward", + "protocol": "HTTP", + "tls": false, + "capacity": 1000 + }, + { + "source": "node-3", + "target": "node-4", + "label": "Read traffic", + "direction": "forward", + "protocol": "HTTP", + "tls": false, + "capacity": 1000 + }, + { + "source": "node-4", + "target": "node-5", + "label": "Read traffic", + "direction": "forward", + "protocol": "HTTP", + "tls": false, + "capacity": 1000 + }, + { + "source": "node-5", + "target": "node-6", + "label": "Read traffic", + "direction": "forward", + "protocol": "HTTP", + "tls": false, + "capacity": 1000 + }, + { + "source": "node-6", + "target": "node-7", + "label": "Read traffic", + "direction": "forward", + "protocol": "HTTP", + "tls": false, + "capacity": 1000 + }, + { + "source": "node-8", + "target": "node-7", + "label": "Read traffic", + "direction": "forward", + "protocol": "HTTP", + "tls": false, + "capacity": 1000 + }, + { + "source": "node-8", + "target": "node-10", + "label": "Read traffic", + "direction": "forward", + "protocol": "HTTP", + "tls": false, + "capacity": 1000 + }, + { + "source": "node-10", + "target": "node-9", + "label": "Read traffic", + "direction": "forward", + "protocol": "HTTP", + "tls": false, + "capacity": 1000 + } + ] +} diff --git a/internal/simulation/testdata/simple_design.json b/internal/simulation/testdata/simple_design.json new file mode 100644 index 0000000..c3fec4e --- /dev/null +++ b/internal/simulation/testdata/simple_design.json @@ -0,0 +1,33 @@ +{ + "nodes": [ + { + "id": "node-1", + "type": "loadBalancer", + "position": { "x": 0, "y": 0 }, + "props": { + "label": "LB", + "algorithm": "round-robin" + } + }, + { + "id": "node-2", + "type": "webserver", + "position": { "x": 100, "y": 0 }, + "props": { + "label": "Web Server", + "instanceSize": "medium" + } + } + ], + "connections": [ + { + "source": "node-1", + "target": "node-2", + "label": "Traffic", + "direction": "forward", + "protocol": "HTTP", + "tls": false, + "capacity": 1000 + } + ] +} diff --git a/internal/simulation/thirdpartyservicenode.go b/internal/simulation/thirdpartyservicenode.go index d302966..3e354a3 100644 --- a/internal/simulation/thirdpartyservicenode.go +++ b/internal/simulation/thirdpartyservicenode.go @@ -1,5 +1,7 @@ package simulation +import "math/rand" + type ThirdPartyServiceNode struct { ID string Label string @@ -79,3 +81,7 @@ func simulateThirdPartySuccess(req *Request) bool { // 90% success rate return randInt(0, 100) < 90 } + +func randInt(min, max int) int { + return min + int(rand.Float64()*float64(max-min)) +}