Edge cooperation: an orchestrator on the edge that forwards to a neighbour
What it shows
custom_orchestrator puts one policy in a single global object. Here the
orchestrator itself lives on an edge data center (deploy_orchestrator=EDGE with
enable_orchestrators=true), and the interesting question is whether that data
center keeps a task or hands it to a neighbour through the MAN ring. Open
CooperativeEdgeOrchestrator, especially eligible_for.
The Python core’s base rule (Orchestrator.offloading_is_possible) restricts an
edge data center to the one already serving the device, so on a ring no task would
ever leave its own server and the links would stay idle. eligible_for relaxes
that: any edge data center the device still reaches inside
edge_datacenters_coverage is offered as a destination, scored by the same
trade_off rule, which is how a task gets forwarded to a neighbour. The counters
local, forwarded, and cloud and the MAN traffic are printed in
on_simulation_end, proving the ring carried work.
flowchart LR
dev["device"] -->|"nearest, EDGE"| sdc["serving edge data center"]
sdc --> elig["eligible_for, any DC in range"]
elig --> local["local, 23679"]
elig --> ring["forwarded over the MAN ring, 19821"]
elig --> cloud["cloud over the WAN, 7250"]
classDef coop fill:#cfe8ff,stroke:#333;
class elig,ring coop;
The scenario
500 devices on a 2000 by 2000 map, four edge data centers at the corners wired in
a ring through the MAN (dc1 to dc2 to dc3 to dc4 to dc1), and one cloud data
center. Three applications generate the load: Health (Hard Real-time, 20 requests
per minute, 0.02 s deadline, 500 MI), Augmented reality (Soft Real-time, 30 per
minute, 0.5 s, 5000 MI, 1500 KB request), and HEAVY_COMP_APP (Normal, 3 per
minute, 300 s, 30000 MI). The architecture is EDGE_AND_CLOUD and the algorithm
TRADE_OFF, the run is 10 simulated minutes, and random_seed is left empty, so
the numbers vary between runs. edge_devices_range is only 10 m, which is why the
mist tier never appears in the results.
Run it
From the repository root, after pip install -e .:
python examples/edge_cooperation/edge_cooperation.py
The same script runs without installing the package, as long as src is on the
path:
PYTHONPATH=src python examples/edge_cooperation/edge_cooperation.py
Set enable_orchestrators=false in the settings to make every device decide for
itself; then nothing is forwarded and the ring stays empty, which is the clean
comparison for this example.
What to look for
CooperativeEdgeOrchestrator - served by 4 edge orchestrators: 23679 local, 19821 forwarded over the MAN ring, 7250 to cloud, MAN usage 1995.600 s
EDGE_AND_CLOUD devices= 500 tasks= 50750 ok= 32093 avg_delay=0.1586 cloud= 7250 edge=43500 mist= 0 failed(delay)=18657 energy=238.7441
Results go to examples/edge_cooperation/output/. Both blocks above come from the
same measured launch of 2026-10-28 and they agree: the two counters add up to the
CSV edge total (23679 local + 19821 forwarded = 43500), and the printed energy,
238.7441 Wh, is the value of the CSV column. random_seed is still empty, so a
re-run moves the split between local and forwarded and the energy column, while the
task totals repeat because the workload and the topology do not.
- Cooperation really happens. All 500 devices fall within the 1000 m coverage of the corner data centers, so all four act as orchestrators, and of the 43500 tasks that ran at the edge tier, 19821 were forwarded to a neighbour over the ring (45.6 percent of the edge work left the data center that owned the decision) against 23679 kept locally. Those two counts add to the CSV edge total (23679 local + 19821 forwarded = 43500), and adding the 7250 cloud tasks accounts for all 50750 generated tasks.
- Success is bounded by the tightest deadline, not by the ring: 32093 of the 50750 tasks, about 63 percent, finish inside their budget and 18657 fail on delay, while the columns for mobility and for dead batteries are both 0 (no device died, the fleet ends at 98.70% of its charge). The Health application demands a 0.02 s response, and a forwarded task pays 0.01 s per MAN hop on top of queuing, so the hard-real-time requests are what set the floor. What decides the rest is the destination: edge placements succeed 66.6 percent of the time (28970 of 43500) against 43.1 percent on the cloud (3123 of 7250), so handing a task to a neighbour now beats shipping it to the WAN, and cooperation is a win instead of a rescue attempt. The average completed delay of 0.1586 s is an average over successes only, so it says nothing about the 18657 tasks that missed their bound, and the 902.61 s of total waiting time (0.0178 s per task) shows the four data centers are loaded, not saturated.
- The MAN carries the time, not the energy. MAN usage is 1995.600 s of transfer
time in the printed run, sitting between the 2466.41 s of the LAN and the 287.05 s
of the WAN inside the 4749.06 s of total network usage, and it now stops when the
last hop is really finished instead of waiting for the next 1 s polling tick. The
CSV “MAN energy consumption” column is still 0.00 Wh because
man_nanojoules_per_bit=0. Most of the 238.74 Wh is spent by the mobile fleet (the mist energy column is 150.94 Wh, the edge column 78.32 Wh, the cloud column 9.48 Wh), and the separate per-link accounting puts 644.43 Wh on the cellular access and 24.25 Wh on Wi-Fi, while the edge tier runs at 27.69% average CPU and the run moves 98234.27 MB of traffic. - Mist is empty (
mist=0): with a 10 m device range no device can host a neighbour’s task, so the whole fleet is split between edge and cloud only. The mist energy column above is the devices drawing power for their own tasks, not for hosting one another’s.
Files
edge_cooperation.pywithCooperativeEdgeOrchestratorand the counterssettings/with the four ring data centers, the 500-device fleet, and the three applications
Where to go next
- Custom orchestrator for a single global placement policy instead of one running on each edge data center
- Dynamic repartition for an edge ring that is re-planned live instead of decided task by task