|
| 1 | +# EventInjection — Public API Documentation |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +`EventInjection` declares a **time-bounded event** that affects a component in the simulation. Each event targets either a **server** or a **network edge**, and is delimited by a `start` marker and an `end` marker. |
| 6 | + |
| 7 | +Supported families (per code): |
| 8 | + |
| 9 | +* **Server availability**: `SERVER_DOWN` → `SERVER_UP` |
| 10 | +* **Network latency spike (deterministic offset in seconds)**: `NETWORK_SPIKE_START` → `NETWORK_SPIKE_END` |
| 11 | + For network spikes, the `Start` marker carries the amplitude in seconds via `spike_s`. |
| 12 | + |
| 13 | +Strictness: |
| 14 | + |
| 15 | +* Models use `ConfigDict(extra="forbid", frozen=True)` |
| 16 | + → unknown fields are rejected; instances are immutable at runtime. |
| 17 | + |
| 18 | +--- |
| 19 | + |
| 20 | +## Data Model |
| 21 | + |
| 22 | +### `Start` |
| 23 | + |
| 24 | +* `kind: Literal[SERVER_DOWN, NETWORK_SPIKE_START]` |
| 25 | + Event family selector. |
| 26 | +* `t_start: NonNegativeFloat` |
| 27 | + Start time in **seconds** from simulation start; **≥ 0.0**. |
| 28 | +* `spike_s: PositiveFloat | None` |
| 29 | + **Required** and **> 0** **only** when `kind == NETWORK_SPIKE_START`. |
| 30 | + **Forbidden** (must be omitted/`None`) for any other kind. |
| 31 | + |
| 32 | +### `End` |
| 33 | + |
| 34 | +* `kind: Literal[SERVER_UP, NETWORK_SPIKE_END]` |
| 35 | + Must match the start family (see invariants). |
| 36 | +* `t_end: PositiveFloat` |
| 37 | + End time in **seconds**; **> 0.0**. |
| 38 | + |
| 39 | +### `EventInjection` |
| 40 | + |
| 41 | +* `event_id: str` |
| 42 | + Unique identifier within the simulation payload. |
| 43 | +* `target_id: str` |
| 44 | + Identifier of the affected component (server or edge) as defined in the topology. |
| 45 | +* `start: Start` |
| 46 | + Start marker. |
| 47 | +* `end: End` |
| 48 | + End marker. |
| 49 | + |
| 50 | +--- |
| 51 | + |
| 52 | +## Validation & Invariants (as implemented) |
| 53 | + |
| 54 | +### Within `EventInjection` |
| 55 | + |
| 56 | +1. **Family coherence** |
| 57 | + |
| 58 | + * `SERVER_DOWN` → `SERVER_UP` |
| 59 | + * `NETWORK_SPIKE_START` → `NETWORK_SPIKE_END` |
| 60 | + Any other pairing raises: |
| 61 | + |
| 62 | + ``` |
| 63 | + The event {event_id} must have as value of kind in end {expected} |
| 64 | + ``` |
| 65 | +2. **Temporal ordering** |
| 66 | + |
| 67 | + * `t_start < t_end` (with `t_start ≥ 0.0`, `t_end > 0.0`) |
| 68 | + Error: |
| 69 | + |
| 70 | + ``` |
| 71 | + The starting time for the event {event_id} must be smaller than the ending time |
| 72 | + ``` |
| 73 | +3. **Network spike parameter** |
| 74 | + |
| 75 | + * If `start.kind == NETWORK_SPIKE_START` ⇒ `start.spike_s` **must** be provided and be a positive float (seconds). |
| 76 | + Error: |
| 77 | + |
| 78 | + ``` |
| 79 | + The field spike_s for the event {event_id} must be defined as a positive float (seconds) |
| 80 | + ``` |
| 81 | + * Otherwise (`SERVER_DOWN`) ⇒ `start.spike_s` **must be omitted** / `None`. |
| 82 | + Error: |
| 83 | +
|
| 84 | + ``` |
| 85 | + Event {event_id}: spike_s must be omitted for non-network events |
| 86 | + ``` |
| 87 | +
|
| 88 | +### Enforced at `SimulationPayload` level |
| 89 | +
|
| 90 | +4. **Unique event IDs** |
| 91 | + Error: |
| 92 | +
|
| 93 | + ``` |
| 94 | + The id's representing different events must be unique |
| 95 | + ``` |
| 96 | +5. **Target existence & compatibility** |
| 97 | +
|
| 98 | + * For server events (`SERVER_DOWN`), `target_id` must refer to a **server**. |
| 99 | + * For network spikes (`NETWORK_SPIKE_START`), `target_id` must refer to an **edge**. |
| 100 | + Errors: |
| 101 | +
|
| 102 | + ``` |
| 103 | + The target id {target_id} related to the event {event_id} does not exist |
| 104 | + ``` |
| 105 | +
|
| 106 | + ``` |
| 107 | + The event {event_id} regarding a server does not have a compatible target id |
| 108 | + ``` |
| 109 | +
|
| 110 | + ``` |
| 111 | + The event {event_id} regarding an edge does not have a compatible target id |
| 112 | + ``` |
| 113 | +6. **Times within simulation horizon** (with `T = sim_settings.total_simulation_time`) |
| 114 | +
|
| 115 | + * `t_start >= 0.0` and `t_start <= T` |
| 116 | + * `t_end <= T` |
| 117 | + Errors: |
| 118 | +
|
| 119 | + ``` |
| 120 | + Event '{event_id}': start time t_start={t:.6f} must be >= 0.0 |
| 121 | + Event '{event_id}': start time t_start={t:.6f} exceeds simulation horizon T={T:.6f} |
| 122 | + Event '{event_id}': end time t_end={t:.6f} exceeds simulation horizon T={T:.6f} |
| 123 | + ``` |
| 124 | +7. **Global liveness rule (servers)** |
| 125 | + The payload is rejected if **all servers are down at the same moment**. |
| 126 | + Implementation detail: the timeline is ordered so that, at identical timestamps, **`END` is processed before `START`** to avoid transient all-down states. |
| 127 | + Error: |
| 128 | +
|
| 129 | + ``` |
| 130 | + At time {time:.6f} all servers are down; keep at least one up |
| 131 | + ``` |
| 132 | +
|
| 133 | +--- |
| 134 | +
|
| 135 | +## Runtime Semantics (summary) |
| 136 | +
|
| 137 | +* **Server events**: the targeted server is unavailable between the start and end markers; the system enforces that at least one server remains up at all times. |
| 138 | +* **Network spike events**: the targeted edge’s latency sampler is deterministically **shifted by `spike_s` seconds** during the event window (additive congestion model). The underlying distribution is not reshaped—samples are translated by a constant offset. |
| 139 | +
|
| 140 | +*(This reflects the agreed model: deterministic additive offset on edges.)* |
| 141 | +
|
| 142 | +--- |
| 143 | +
|
| 144 | +## Units & Precision |
| 145 | +
|
| 146 | +* All times and offsets are in **seconds** (floating-point). |
| 147 | +* Provide values with the precision your simulator supports; microsecond-level precision is acceptable if needed. |
| 148 | +
|
| 149 | +--- |
| 150 | +
|
| 151 | +## Authoring Guidelines |
| 152 | +
|
| 153 | +* **Do not include `spike_s`** for non-network events. |
| 154 | +* Use **stable, meaningful `event_id`** values for auditability. |
| 155 | +* Keep events within the **simulation horizon**. |
| 156 | +* When multiple markers share the same timestamp, rely on the engine’s **END-before-START** ordering for determinism. |
| 157 | +
|
| 158 | +--- |
| 159 | +
|
| 160 | +## Examples |
| 161 | +
|
| 162 | +### 1) Valid — Server maintenance window |
| 163 | +
|
| 164 | +```yaml |
| 165 | +event_id: ev-maint-001 |
| 166 | +target_id: srv-1 |
| 167 | +start: { kind: SERVER_DOWN, t_start: 120.0 } |
| 168 | +end: { kind: SERVER_UP, t_end: 240.0 } |
| 169 | +``` |
| 170 | + |
| 171 | +### 2) Valid — Network spike on an edge (+8 ms) |
| 172 | + |
| 173 | +```yaml |
| 174 | +event_id: ev-spike-008ms |
| 175 | +target_id: edge-12 |
| 176 | +start: { kind: NETWORK_SPIKE_START, t_start: 10.0, spike_s: 0.008 } |
| 177 | +end: { kind: NETWORK_SPIKE_END, t_end: 25.0 } |
| 178 | +``` |
| 179 | +
|
| 180 | +### 3) Invalid — Missing `spike_s` for a network spike |
| 181 | + |
| 182 | +```yaml |
| 183 | +event_id: ev-missing-spike |
| 184 | +target_id: edge-5 |
| 185 | +start: { kind: NETWORK_SPIKE_START, t_start: 5.0 } |
| 186 | +end: { kind: NETWORK_SPIKE_END, t_end: 15.0 } |
| 187 | +``` |
| 188 | + |
| 189 | +Error: |
| 190 | + |
| 191 | +``` |
| 192 | +The field spike_s for the event ev-missing-spike must be defined as a positive float (seconds) |
| 193 | +``` |
| 194 | + |
| 195 | +### 4) Invalid — `spike_s` present for a server event |
| 196 | + |
| 197 | +```yaml |
| 198 | +event_id: ev-bad-spike |
| 199 | +target_id: srv-2 |
| 200 | +start: { kind: SERVER_DOWN, t_start: 50.0, spike_s: 0.005 } |
| 201 | +end: { kind: SERVER_UP, t_end: 60.0 } |
| 202 | +``` |
| 203 | + |
| 204 | +Error: |
| 205 | + |
| 206 | +``` |
| 207 | +Event ev-bad-spike: spike_s must be omitted for non-network events |
| 208 | +``` |
| 209 | + |
| 210 | +### 5) Invalid — Mismatched families |
| 211 | + |
| 212 | +```yaml |
| 213 | +event_id: ev-bad-kinds |
| 214 | +target_id: edge-1 |
| 215 | +start: { kind: NETWORK_SPIKE_START, t_start: 5.0, spike_s: 0.010 } |
| 216 | +end: { kind: SERVER_UP, t_end: 15.0 } |
| 217 | +``` |
| 218 | + |
| 219 | +Error: |
| 220 | + |
| 221 | +``` |
| 222 | +The event ev-bad-kinds must have as value of kind in end NETWORK_SPIKE_END |
| 223 | +``` |
| 224 | + |
| 225 | +### 6) Invalid — Start not before End |
| 226 | + |
| 227 | +```yaml |
| 228 | +event_id: ev-bad-time |
| 229 | +target_id: srv-2 |
| 230 | +start: { kind: SERVER_DOWN, t_start: 300.0 } |
| 231 | +end: { kind: SERVER_UP, t_end: 300.0 } |
| 232 | +``` |
| 233 | + |
| 234 | +Error: |
| 235 | + |
| 236 | +``` |
| 237 | +The starting time for the event ev-bad-time must be smaller than the ending time |
| 238 | +``` |
| 239 | + |
| 240 | +--- |
| 241 | + |
| 242 | +## Notes for Consumers |
| 243 | + |
| 244 | +* The schema is **strict**: misspelled fields (e.g., `t_strat`) are rejected. |
| 245 | +* The engine may combine multiple active network spikes on the same edge by **summing** their `spike_s` values while they overlap (handled by runtime bookkeeping). |
| 246 | +* This document describes exactly what is present in the provided code and validators; no additional fields or OpenAPI metadata are assumed. |
0 commit comments