Skip to content

Commit 2d529aa

Browse files
committed
initiated the server runtime plus correction of tghe structure
1 parent 31dec2e commit 2d529aa

File tree

14 files changed

+264
-18
lines changed

14 files changed

+264
-18
lines changed

src/app/config/constants.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -128,15 +128,11 @@ class StepOperation(StrEnum):
128128
"""
129129
Keys used inside the ``metrics`` dictionary of a *step*.
130130
131-
* ``NETWORK_LATENCY`` - Mean latency (seconds) incurred on a network edge
132-
*outside* the service (used mainly for validation when steps model
133-
short in-service hops).
134131
* ``CPU_TIME`` - Service time (seconds) during which the coroutine occupies
135132
the CPU / GIL.
136133
* ``NECESSARY_RAM`` - Peak memory (MB) required by the step.
137134
"""
138135

139-
NETWORK_LATENCY = "network_latency"
140136
CPU_TIME = "cpu_time"
141137
IO_WAITING_TIME = "io_waiting_time"
142138
NECESSARY_RAM = "necessary_ram"
@@ -165,6 +161,7 @@ class NetworkParameters:
165161
DROPOUT_RATE = 0.01
166162
MAX_DROPOUT_RATE = 1.0
167163

164+
168165
# ======================================================================
169166
# CONSTANTS FOR THE MACRO-TOPOLOGY GRAPH
170167
# ======================================================================
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@
66
import simpy
77

88
from app.config.constants import SystemNodes
9-
from app.core.runtime.edge import EdgeRuntime
9+
from app.runtime.engine.edge import EdgeRuntime
1010
from app.schemas.system_topology_schema.full_system_topology_schema import Client
1111

1212
if TYPE_CHECKING:
13-
from app.config.rqs_state import RequestState
13+
from app.runtime.rqs_state import RequestState
1414

1515

1616

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313
import simpy
1414

1515
from app.config.constants import SystemEdges
16-
from app.config.rqs_state import RequestState
1716
from app.core.event_samplers.common_helpers import general_sampler
17+
from app.runtime.rqs_state import RequestState
1818
from app.schemas.system_topology_schema.full_system_topology_schema import Edge
1919

2020
if TYPE_CHECKING:
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,17 @@
1010
import numpy as np
1111

1212
from app.config.constants import Distribution, SystemNodes
13-
from app.config.rqs_state import RequestState
1413
from app.core.event_samplers.gaussian_poisson import gaussian_poisson_sampling
1514
from app.core.event_samplers.poisson_poisson import poisson_poisson_sampling
15+
from app.runtime.rqs_state import RequestState
1616

1717
if TYPE_CHECKING:
1818

1919
from collections.abc import Generator
2020

2121
import simpy
2222

23-
from app.core.runtime.edge import EdgeRuntime
23+
from app.runtime.engine.edge import EdgeRuntime
2424
from app.schemas.requests_generator_input import RqsGeneratorInput
2525
from app.schemas.simulation_settings_input import SimulationSettings
2626

src/app/runtime/engine/server.py

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
"""
2+
definition of the class necessary to manage the server
3+
during the simulation
4+
"""
5+
6+
from collections.abc import Generator
7+
from typing import TYPE_CHECKING
8+
9+
import numpy as np
10+
import simpy
11+
12+
from app.config.constants import (
13+
EndpointStepCPU,
14+
EndpointStepIO,
15+
EndpointStepRAM,
16+
StepOperation,
17+
SystemNodes,
18+
)
19+
from app.runtime.engine.edge import EdgeRuntime
20+
from app.runtime.types import ServerContainers, ServerResourceName
21+
from app.schemas.system_topology_schema.full_system_topology_schema import Server
22+
23+
if TYPE_CHECKING:
24+
from app.runtime.rqs_state import RequestState
25+
26+
27+
class ServerRuntime:
28+
"""class to define the server during the simulation"""
29+
30+
def __init__( # noqa: PLR0913
31+
self,
32+
env: simpy.Environment,
33+
server_resources: ServerContainers,
34+
server_config: Server,
35+
out_edge: EdgeRuntime,
36+
server_box: simpy.Store,
37+
rng: np.random.Generator | None = None,
38+
) -> None:
39+
"""Server attributes
40+
41+
Args:
42+
env (simpy.Environment): _description_
43+
server_resources (ServerContainers): _description_
44+
server_config (Server): _description_
45+
out_edge (EdgeRuntime): _description_
46+
server_box (simpy.Store): _description_
47+
rng (np.random.Generator | None, optional): _description_. Defaults to None.
48+
49+
"""
50+
self.env = env
51+
self.server_resources = server_resources
52+
self.server_config = server_config
53+
self.out_edge = out_edge
54+
self.server_box = server_box
55+
self.rng = rng or np.random.default_rng()
56+
57+
58+
def _forwarder(self) -> Generator[simpy.Event, None, None]:
59+
"""
60+
Define all the step each request has to do ones reach
61+
the server
62+
"""
63+
# Define the length of the endpoint list
64+
endpoints_list = self.server_config.endpoints
65+
endpoints_number = len(endpoints_list)
66+
67+
68+
while True:
69+
state: RequestState = yield self.server_box.get() # type: ignore[assignment]
70+
71+
#register the history for the state:
72+
state.record_hop(
73+
SystemNodes.SERVER,
74+
self.server_config.id,
75+
self.env.now,
76+
)
77+
# select the endpoint where the requests is directed at the moment we use
78+
# a uniform distribution, in the future we will allow the user to define a
79+
# custom distribution
80+
selected_endpoint_idx = self.rng.integers(low=0, high=endpoints_number)
81+
selected_endpoint = endpoints_list[selected_endpoint_idx]
82+
83+
# RAM management:
84+
# first calculate the ram needed
85+
# Ask if it is available
86+
# Release everything when the operation completed
87+
88+
total_ram = sum(
89+
step.step_operation[StepOperation.NECESSARY_RAM]
90+
for step in selected_endpoint.steps
91+
if isinstance(step.kind, EndpointStepRAM)
92+
)
93+
94+
# 4) prenoto quella RAM TUTTA INSIEME
95+
if total_ram:
96+
yield self.server_resources[ServerResourceName.RAM.value].get(total_ram)
97+
98+
99+
for step in selected_endpoint.steps:
100+
if isinstance(step.kind, EndpointStepCPU):
101+
102+
# operation do not continue until the core is busy
103+
yield self.server_resources[ServerResourceName.CPU.value].get(1)
104+
105+
# in a cpu bound task we wait the necessary time keeping the core
106+
# busy to simulate the fact that the event loop cannot elaborate
107+
# another task
108+
cpu_time = step.step_operation[StepOperation.CPU_TIME]
109+
yield self.env.timeout(cpu_time)
110+
111+
# the core is again free
112+
yield self.server_resources[ServerResourceName.CPU.value].put(1)
113+
114+
elif isinstance(step.kind, EndpointStepIO):
115+
# here we do not require the core to be busy to simulate
116+
# the fact that other requests to the endpoint can be elaborated
117+
# in the event loop in a I/O operation
118+
yield self.env.timeout(
119+
step.step_operation[StepOperation.IO_WAITING_TIME],
120+
)
121+
122+
# release the ram
123+
if total_ram:
124+
yield self.server_resources[ServerResourceName.RAM.value].put(total_ram)
125+
126+
self.out_edge.transport(state)
127+
128+
129+
130+
def server_runtime(self) -> simpy.Process:
131+
"""Generate the process to simulate the server inside simpy env"""
132+
return self.env.process(self._forwarder())
133+
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
"""
2+
Runtime resource registry for server nodes.
3+
4+
This module defines the ResourcesRuntime class, which takes a validated
5+
TopologyGraph and a SimPy environment, then builds and stores a map
6+
from each server's unique identifier to its SimPy resource containers.
7+
Processes can later retrieve CPU and RAM containers by indexing this registry.
8+
"""
9+
10+
import simpy
11+
12+
from app.runtime.types import ServerContainers, build_containers
13+
from app.schemas.system_topology_schema.full_system_topology_schema import TopologyGraph
14+
15+
16+
class ResourcesRuntime:
17+
"""definition of the class to associate resources to various nodes"""
18+
19+
def __init__(
20+
self,
21+
env: simpy.Environment,
22+
data: TopologyGraph,
23+
24+
) -> None:
25+
"""Initialization of the attributes"""
26+
self.env = env
27+
self.data = data
28+
self._by_server: dict[str, ServerContainers] = {
29+
server.id: build_containers(env, server.server_resources)
30+
for server in data.nodes.servers
31+
}
32+
33+
def __getitem__(self, server_id: str) -> ServerContainers:
34+
"""
35+
Useful map to pass to each server the resources based
36+
on the server unique id
37+
"""
38+
return self._by_server[server_id]

src/app/runtime/types.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
"""
2+
Definition of support structures for the simulation runtime.
3+
4+
After Pydantic validation, this module provides TypedDicts and helpers
5+
to build SimPy Containers for each server in the topology, improving
6+
readability and ensuring a single point of truth for resource setup.
7+
"""
8+
9+
from enum import StrEnum
10+
from typing import TypedDict
11+
12+
import simpy
13+
14+
from app.schemas.system_topology_schema.full_system_topology_schema import (
15+
ServerResources,
16+
)
17+
18+
# ==============================================================
19+
# DICT FOR THE REGISTRY TO INITIALIZE RESOURCES FOR EACH SERVER
20+
# ==============================================================
21+
22+
# enum in this case is to have the availabale key and improve
23+
#code readability not for the validation
24+
25+
class ServerResourceName(StrEnum):
26+
"""Keys for each server resource type, used when building the container map."""
27+
28+
CPU = "CPU"
29+
RAM = "RAM"
30+
31+
class ServerContainers(TypedDict):
32+
"""
33+
Mapping of resource names to their SimPy Container instances for a server.
34+
35+
- CPU: simpy.Container for CPU cores.
36+
- RAM: simpy.Container for RAM in megabytes.
37+
"""
38+
39+
CPU: simpy.Container
40+
RAM: simpy.Container
41+
42+
# Central funcrion to initialize the dictionary with ram and cpu container
43+
def build_containers(
44+
env: simpy.Environment,
45+
spec: ServerResources,
46+
) -> ServerContainers:
47+
"""
48+
Construct and return a mapping of SimPy Containers for a server's CPU and RAM.
49+
50+
Given a SimPy environment and a validated ServerResources spec, this function
51+
initializes one simpy.Container for CPU (with capacity equal to cpu_cores)
52+
and one for RAM (with capacity equal to ram_mb), then returns them in a
53+
ServerContainers TypedDict keyed by "CPU" and "RAM".
54+
55+
Parameters
56+
----------
57+
env : simpy.Environment
58+
The simulation environment in which the Containers will be created.
59+
spec : ServerResources
60+
A Pydantic model instance defining the server's cpu_cores and ram_mb.
61+
62+
Returns
63+
-------
64+
ServerContainers
65+
A TypedDict with exactly two entries:
66+
- "CPU": simpy.Container initialized with spec.cpu_cores
67+
- "RAM": simpy.Container initialized with spec.ram_mb
68+
69+
"""
70+
return {
71+
ServerResourceName.CPU.value: simpy.Container(env, capacity=spec.cpu_cores),
72+
ServerResourceName.RAM.value: simpy.Container(env, capacity=spec.ram_mb),
73+
}
74+
75+
76+
77+
78+

0 commit comments

Comments
 (0)