perf: batch marker-def fan-out to one PUT /nios/batch per compute

When a marker definition is created (or re-applied on data_link_type
change), the fan-out used to call inherit_marker -> update() on every
link, issuing one PUT /nio per link end (5000+ round-trips on a 2500-link
project). On started nodes each round-trip also reconfigured uBridge.

Two-phase fan-out:
- inherit_marker/start_marker gain memory_only: writes the marker into
  link._markers and refreshes _link_data without any HTTP/emit/dump.
- _apply_def_to_all_links applies memory-only to every link, then
  _batch_update_link_nios groups the updated NIO specs by compute and
  sends a single PUT /projects/{id}/nios/batch per compute.

compute: new PUT /projects/{id}/nios/batch endpoint with _get_existing_nio
+ _update_nio_binding dispatch (mirrors create_batch_nios), re-applies
filters+markers to uBridge on started nodes.

Precise per-marker operations (update_marker bpf change, stop_marker on
def delete) are untouched — they deliberately avoid a full reapply to
preserve sibling marker pcaps.
This commit is contained in:
YueGuobin committed 2026-08-11 08:44:57 +08:00
1 parent c877ed87a6
commit 48991f2d29
4 files changed
+167 -10

No files matched your search

+27 -1
View File
@@ -407,7 +407,7 @@ class UDPLink(Link):
# explicitly deletes a marker via the REST API, and a marker is torn
# down automatically only when its link is deleted.
async def start_marker(self, name, bpf, tag=None, direction=None, data_link_type="DLT_EN10MB", capture_node_id=None, color=None, highlight_duration=None, enabled=True, inherited_from=None, dump=True):
async def start_marker(self, name, bpf, tag=None, direction=None, data_link_type="DLT_EN10MB", capture_node_id=None, color=None, highlight_duration=None, enabled=True, inherited_from=None, dump=True, memory_only=False):
"""
Attach a traffic-insight marker to this link.
@@ -463,6 +463,12 @@ class UDPLink(Link):
if inherited_from:
marker_entry["inherited_from"] = inherited_from
self._markers[name] = marker_entry
if memory_only:
# Project-open prepare / marker-def fan-out: only refresh the
# in-memory NIO specs so a later batch dispatch carries the new
# markers — no per-link update HTTP, notification or dump.
self._refresh_link_data()
return
if self._created:
await self.update()
self._project.emit_notification("link.updated", self.asdict())
@@ -471,6 +477,26 @@ class UDPLink(Link):
if dump:
self._project.dump()
def _refresh_link_data(self):
"""
Recompute the filters / markers / suspend fields of ``_link_data`` from
the current link state without pushing to computes. Used by the
memory-only marker path so a batch dispatch picks up the new markers.
"""
if len(self._link_data) < 2:
return
node1 = self._nodes[0]["node"]
node2 = self._nodes[1]["node"]
node1_filters, node2_filters = self._get_node_filters(node1, node2)
node1_markers, node2_markers = self._get_node_markers(node1, node2)
self._link_data[0]["filters"] = node1_filters
self._link_data[0]["markers"] = node1_markers
self._link_data[0]["suspend"] = self._suspended
self._link_data[1]["filters"] = node2_filters
self._link_data[1]["markers"] = node2_markers
self._link_data[1]["suspend"] = self._suspended
async def stop_marker(self, name, inherited=False, dump=True):
"""
Remove a traffic-insight marker from this link.