Skip to content

Commit 52bff29

Browse files
committed
definition of state and RqsGeneratorRuntime
1 parent 953e318 commit 52bff29

9 files changed

Lines changed: 136 additions & 6 deletions

File tree

src/app/config/constants.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,8 @@ class SystemNodes(StrEnum):
164164
Each member represents a *macro-component* that may have its own SimPy
165165
resources (CPU cores, DB pool, etc.).
166166
"""
167-
167+
168+
GENERATOR = "generator"
168169
SERVER = "server"
169170
CLIENT = "client"
170171
LOAD_BALANCER = "load_balancer"

src/app/config/rqs_state.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
"""
2+
defining a state in a one to one correspondence
3+
with the requests generated that will go through
4+
all the node necessary to accomplish the user request
5+
"""
6+
7+
from __future__ import annotations
8+
from dataclasses import dataclass, field
9+
10+
11+
@dataclass
12+
class RequestState:
13+
"""
14+
State object carried by each request through the simulation.
15+
16+
Attributes:
17+
id: Unique identifier of the request.
18+
t0: Timestamp (simulated env.now) when the request was generated.
19+
history: List of hop records, each noting a node/edge visit.
20+
"""
21+
id: int # Unique request identifier
22+
initial_time: float # Generation timestamp (env.now)
23+
finish_time: float | None = None # a requests might be dropped
24+
history: list[str] = field(default_factory=list) # Trace of hops
25+
26+
def record_hop(self, node_name: str) -> None:
27+
"""
28+
Append a record of visiting a node or edge.
29+
30+
Args:
31+
node_name: Name of the node or edge being recorded.
32+
"""
33+
# Record hop as "NodeName@Timestamp"
34+
self.history.append(f"{node_name}@{self.t0:.3f}")
35+
36+
@property
37+
def latency(self) -> float | None:
38+
"""
39+
Return the total time in the system (finish_time - initial_time),
40+
or None if the request hasn’t completed yet.
41+
"""
42+
if self.finish_time is None:
43+
return None
44+
return self.finish_time - self.initial_time

src/app/core/event_samplers/gaussian_poisson.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def gaussian_poisson_sampling(
2424
input_data: RqsGeneratorInput,
2525
sim_settings: SimulationSettings,
2626
*,
27-
rng: np.random.Generator | None = None,
27+
rng: np.random.Generator,
2828
) -> Generator[float, None, None]:
2929
"""
3030
Yield inter-arrival gaps (seconds) for the compound Gaussian-Poisson process.
@@ -39,7 +39,6 @@ def gaussian_poisson_sampling(
3939
Δt ~ Exponential(Λ) using inverse-CDF.
4040
4. Stop once the virtual clock exceeds *total_simulation_time*.
4141
"""
42-
rng = rng or np.random.default_rng()
4342

4443
simulation_time = sim_settings.total_simulation_time
4544
user_sampling_window = input_data.user_sampling_window

src/app/core/event_samplers/poisson_poisson.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def poisson_poisson_sampling(
2121
input_data: RqsGeneratorInput,
2222
sim_settings: SimulationSettings,
2323
*,
24-
rng: np.random.Generator | None = None,
24+
rng: np.random.Generator,
2525
) -> Generator[float, None, None]:
2626
"""
2727
Yield inter-arrival gaps (seconds) for the compound Poisson-Poisson process.
@@ -36,8 +36,7 @@ def poisson_poisson_sampling(
3636
Δt ~ Exponential(Λ) using inverse-CDF.
3737
4. Stop once the virtual clock exceeds *total_simulation_time*.
3838
"""
39-
rng = rng or np.random.default_rng()
40-
39+
4140
simulation_time = sim_settings.total_simulation_time
4241
user_sampling_window = input_data.user_sampling_window
4342

File renamed without changes.
File renamed without changes.

src/app/core/runtime/edge.py

Whitespace-only changes.
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
"""
2+
definition of the class representing the rqs generator
3+
that will be passed as a process in the simpy simulation
4+
"""
5+
6+
from __future__ import annotations
7+
8+
from typing import Generator, TYPE_CHECKING
9+
10+
import simpy
11+
12+
from app.config.constants import Distribution
13+
from app.config.rqs_state import RequestState
14+
from app.core.event_samplers.gaussian_poisson import gaussian_poisson_sampling
15+
from app.core.event_samplers.poisson_poisson import poisson_poisson_sampling
16+
17+
if TYPE_CHECKING:
18+
from collections.abc import Generator
19+
20+
import numpy as np
21+
22+
from app.schemas.requests_generator_input import RqsGeneratorInput
23+
from app.schemas.simulation_settings_input import SimulationSettings
24+
25+
26+
class RqsGeneratorRuntime():
27+
"""
28+
A “node” that produces request contexts at stochastic inter‐arrival times
29+
and immediately pushes them down the pipeline via an EdgeRuntime.
30+
"""
31+
def __init__(
32+
self,
33+
env: simpy.Environment,
34+
state: RequestState,
35+
rqs_generator_data: RqsGeneratorInput,
36+
sim_settings: SimulationSettings,
37+
*,
38+
rng: np.random.Generator | None = None
39+
):
40+
41+
self.rqs_generator_data = rqs_generator_data
42+
self.sim_settings = sim_settings
43+
self.rng = rng or np.random.default_rng()
44+
self.state = state
45+
self.env = env
46+
47+
48+
def _requests_generator(self) -> Generator[float, None, None]:
49+
"""
50+
Return an iterator of inter-arrival gaps (seconds) according to the model
51+
chosen in *input_data*.
52+
53+
Notes
54+
-----
55+
* If ``avg_active_users.distribution`` is ``"gaussian"`` or ``"normal"``,
56+
the Gaussian-Poisson sampler is used.
57+
* Otherwise the default Poisson-Poisson sampler is returned.
58+
59+
"""
60+
dist = self.rqs_generator_data.avg_active_users.distribution.lower()
61+
62+
if dist == Distribution.NORMAL:
63+
#Gaussian-Poisson model
64+
return gaussian_poisson_sampling(
65+
input_data=self.rqs_generator_data,
66+
sim_settings=self.sim_settings,
67+
rng=self.rng,
68+
69+
)
70+
71+
# Poisson + Poisson
72+
return poisson_poisson_sampling(
73+
input_data=self.rqs_generator_data,
74+
sim_settings=self.sim_settings,
75+
rng=self.rng,
76+
)
77+
78+
def _event_arrival(self) -> Generator[simpy.Event, None, None]:
79+
"""simulating the process of event generation"""
80+
time_gaps = self._requests_generator()
81+
82+
for gap in time_gaps:
83+
yield self.env.timeout(gap)
84+
85+
def run(self) -> simpy.Process:
86+
"""passing the structure as a simpy process"""
87+
return self.env.process(self._event_arrival())

0 commit comments

Comments
 (0)