Skip to content

Commit 51f1316

Browse files
committed
added exaple for event inj yaml + builder added int tests
1 parent b638d43 commit 51f1316

42 files changed

Lines changed: 992 additions & 9 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
"""
2+
AsyncFlow builder example — LB + 2 servers (medium load) with events.
3+
4+
Topology
5+
generator → client → LB → srv-1
6+
└→ srv-2
7+
srv-1 → client
8+
srv-2 → client
9+
10+
Workload
11+
~40 rps (120 users × 20 req/min ÷ 60).
12+
13+
Events
14+
- Edge spike on client→LB (+15 ms) @ [100s, 160s]
15+
- srv-1 outage @ [180s, 240s]
16+
- Edge spike on LB→srv-2 (+20 ms) @ [300s, 360s]
17+
- srv-2 outage @ [360s, 420s]
18+
- Edge spike on gen→client (+10 ms) @ [480s, 540s]
19+
20+
Outputs
21+
PNGs saved under `lb_two_servers_events_plots/` next to this script:
22+
- dashboard (latency + throughput)
23+
- per-server plots: ready queue, I/O queue, RAM
24+
"""
25+
26+
from __future__ import annotations
27+
28+
from pathlib import Path
29+
30+
import matplotlib.pyplot as plt
31+
import simpy
32+
33+
# Public builder API
34+
from asyncflow import AsyncFlow
35+
from asyncflow.components import Client, Server, Edge, Endpoint, LoadBalancer
36+
from asyncflow.settings import SimulationSettings
37+
from asyncflow.workload import RqsGenerator
38+
39+
# Runner + Analyzer
40+
from asyncflow.metrics.analyzer import ResultsAnalyzer
41+
from asyncflow.runtime.simulation_runner import SimulationRunner
42+
43+
44+
def build_and_run() -> ResultsAnalyzer:
45+
"""Build the scenario via the builder and run the simulation."""
46+
# ── Workload (generator) ───────────────────────────────────────────────
47+
generator = RqsGenerator(
48+
id="rqs-1",
49+
avg_active_users={"mean": 120},
50+
avg_request_per_minute_per_user={"mean": 20},
51+
user_sampling_window=60,
52+
)
53+
54+
# ── Client ────────────────────────────────────────────────────────────
55+
client = Client(id="client-1")
56+
57+
# ── Servers (identical endpoint: CPU 2ms → RAM 128MB → IO 12ms) ───────
58+
endpoint = Endpoint(
59+
endpoint_name="/api",
60+
steps=[
61+
{"kind": "initial_parsing", "step_operation": {"cpu_time": 0.002}},
62+
{"kind": "ram", "step_operation": {"necessary_ram": 128}},
63+
{"kind": "io_wait", "step_operation": {"io_waiting_time": 0.012}},
64+
],
65+
)
66+
srv1 = Server(
67+
id="srv-1",
68+
server_resources={"cpu_cores": 1, "ram_mb": 2048},
69+
endpoints=[endpoint],
70+
)
71+
srv2 = Server(
72+
id="srv-2",
73+
server_resources={"cpu_cores": 1, "ram_mb": 2048},
74+
endpoints=[endpoint],
75+
)
76+
77+
# ── Load Balancer ─────────────────────────────────────────────────────
78+
lb = LoadBalancer(
79+
id="lb-1",
80+
algorithms="round_robin",
81+
server_covered=["srv-1", "srv-2"],
82+
)
83+
84+
# ── Edges (exponential latency) ───────────────────────────────────────
85+
e_gen_client = Edge(
86+
id="gen-client",
87+
source="rqs-1",
88+
target="client-1",
89+
latency={"mean": 0.003, "distribution": "exponential"},
90+
)
91+
e_client_lb = Edge(
92+
id="client-lb",
93+
source="client-1",
94+
target="lb-1",
95+
latency={"mean": 0.002, "distribution": "exponential"},
96+
)
97+
e_lb_srv1 = Edge(
98+
id="lb-srv1",
99+
source="lb-1",
100+
target="srv-1",
101+
latency={"mean": 0.002, "distribution": "exponential"},
102+
)
103+
e_lb_srv2 = Edge(
104+
id="lb-srv2",
105+
source="lb-1",
106+
target="srv-2",
107+
latency={"mean": 0.002, "distribution": "exponential"},
108+
)
109+
e_srv1_client = Edge(
110+
id="srv1-client",
111+
source="srv-1",
112+
target="client-1",
113+
latency={"mean": 0.003, "distribution": "exponential"},
114+
)
115+
e_srv2_client = Edge(
116+
id="srv2-client",
117+
source="srv-2",
118+
target="client-1",
119+
latency={"mean": 0.003, "distribution": "exponential"},
120+
)
121+
122+
# ── Simulation settings ───────────────────────────────────────────────
123+
settings = SimulationSettings(
124+
total_simulation_time=600,
125+
sample_period_s=0.05,
126+
enabled_sample_metrics=[
127+
"ready_queue_len",
128+
"event_loop_io_sleep",
129+
"ram_in_use",
130+
"edge_concurrent_connection",
131+
],
132+
enabled_event_metrics=["rqs_clock"],
133+
)
134+
135+
# ── Assemble payload + events via builder ─────────────────────────────
136+
payload = (
137+
AsyncFlow()
138+
.add_generator(generator)
139+
.add_client(client)
140+
.add_servers(srv1, srv2)
141+
.add_load_balancer(lb)
142+
.add_edges(
143+
e_gen_client,
144+
e_client_lb,
145+
e_lb_srv1,
146+
e_lb_srv2,
147+
e_srv1_client,
148+
e_srv2_client,
149+
)
150+
.add_simulation_settings(settings)
151+
# Events
152+
.add_network_spike(
153+
event_id="ev-spike-1",
154+
edge_id="client-lb",
155+
t_start=100.0,
156+
t_end=160.0,
157+
spike_s=0.015, # +15 ms
158+
)
159+
.add_server_outage(
160+
event_id="ev-srv1-down",
161+
server_id="srv-1",
162+
t_start=180.0,
163+
t_end=240.0,
164+
)
165+
.add_network_spike(
166+
event_id="ev-spike-2",
167+
edge_id="lb-srv2",
168+
t_start=300.0,
169+
t_end=360.0,
170+
spike_s=0.020, # +20 ms
171+
)
172+
.add_server_outage(
173+
event_id="ev-srv2-down",
174+
server_id="srv-2",
175+
t_start=360.0,
176+
t_end=420.0,
177+
)
178+
.add_network_spike(
179+
event_id="ev-spike-3",
180+
edge_id="gen-client",
181+
t_start=480.0,
182+
t_end=540.0,
183+
spike_s=0.010, # +10 ms
184+
)
185+
.build_payload()
186+
)
187+
188+
# ── Run ───────────────────────────────────────────────────────────────
189+
env = simpy.Environment()
190+
runner = SimulationRunner(env=env, simulation_input=payload)
191+
results: ResultsAnalyzer = runner.run()
192+
return results
193+
194+
195+
def main() -> None:
196+
res = build_and_run()
197+
print(res.format_latency_stats())
198+
199+
# Output directory next to this script
200+
script_dir = Path(__file__).parent
201+
out_dir = script_dir / "lb_two_servers_events_plots"
202+
out_dir.mkdir(parents=True, exist_ok=True)
203+
204+
# Dashboard (latency + throughput)
205+
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
206+
res.plot_base_dashboard(axes[0], axes[1])
207+
fig.tight_layout()
208+
dash_path = out_dir / "lb_two_servers_events_dashboard.png"
209+
fig.savefig(dash_path)
210+
print(f"Saved: {dash_path}")
211+
212+
# Per-server plots
213+
for sid in res.list_server_ids():
214+
# Ready queue
215+
f1, a1 = plt.subplots(figsize=(10, 5))
216+
res.plot_single_server_ready_queue(a1, sid)
217+
f1.tight_layout()
218+
p1 = out_dir / f"lb_two_servers_events_ready_queue_{sid}.png"
219+
f1.savefig(p1)
220+
print(f"Saved: {p1}")
221+
222+
# I/O queue
223+
f2, a2 = plt.subplots(figsize=(10, 5))
224+
res.plot_single_server_io_queue(a2, sid)
225+
f2.tight_layout()
226+
p2 = out_dir / f"lb_two_servers_events_io_queue_{sid}.png"
227+
f2.savefig(p2)
228+
print(f"Saved: {p2}")
229+
230+
# RAM usage
231+
f3, a3 = plt.subplots(figsize=(10, 5))
232+
res.plot_single_server_ram(a3, sid)
233+
f3.tight_layout()
234+
p3 = out_dir / f"lb_two_servers_events_ram_{sid}.png"
235+
f3.savefig(p3)
236+
print(f"Saved: {p3}")
237+
238+
239+
if __name__ == "__main__":
240+
main()
149 KB
Loading
36.9 KB
Loading
36.6 KB
Loading
36.9 KB
Loading
38.8 KB
Loading
55.4 KB
Loading
53.6 KB
Loading

0 commit comments

Comments
 (0)