Higher-level suggestions that were not applied directly.
- add RegionLink::is_empty and same for link conditions to remove manual > 0 checks at callsites. break region link should be async. never use run_closure_in with Command if you can easily make the call site async, use async command then to not block calling thread on sync cmds
DeviceSetupData clone — dev: dev.clone() in
DeviceBuilder::build(). All fields (id, name, ns, mtu, interfaces) are used
in setup_device_async, so the clone is structurally required.
add_host(cidr, host) replaces only the last octet, which only works for /24
subnets. If the allocator ever moves to /16 or /25, this will silently produce
wrong addresses.
Rust crates for nftables via netlink (nftnl, rustables, mnl) exist but
have immature APIs. Not worth replacing Command::new("nft") for now.
from_config() parses regions from TOML but does not call add_region()
or link_regions(). Region topologies can only be built programmatically.
tc netem loss on a device interface only affects outbound packets. A "50%
loss" link actually delivers ~50% in one direction and 100% in the other,
which does not match how real lossy links (e.g. WiFi) behave. We should
either apply netem on both ends of the veth pair, or use a single ingress +
egress qdisc setup (e.g. ifb mirroring) so that LinkCondition::Wifi gives
symmetric loss. The test bounds in loss_udp_moderate are currently widened
to paper over this. Needs a design decision on where to apply qdiscs.
spawn_reflector enqueues a task on the namespace worker and returns
immediately. There is no guarantee the reflector socket is bound by the time
callers start sending probes, which causes intermittent test failures. The
API should be async (return once the socket is bound) and return a drop
guard that stops the reflector when dropped. This would eliminate the manual
sleep(300ms) after every spawn_reflector call in tests.
break_region_link() and restore_region_link() use Command::new("ip")
to replace routes. These could use the netlink API (nl_run + Netlink)
for consistency, but the sync run_closure_in path avoids async overhead
for these rare operations.
- Dead
apply_region_latency_dual+ Qdisc builder — already deleted in earlier refactors;Qdiscstruct remains, used byapply_impair/remove_qdisc. Ipv6Profileredundant variants — added doc comment noting future divergence intent (RA intervals, prefix delegation, SLAAC vs DHCPv6). Variants kept forRouterPreset::recommended_ipv6_profile()mapping.- OutDir enum + runner uses Exact — replaced separate
outdir/run_dirfields onLabOptswithOutDirenum (Nestedcreates timestamped subdir,Exactwrites directly). Runner usesOutDir::Exact, removing need forPATCHBAY_OUTDIRenv var in e2e tests. - Server recursive scan —
discover_runsscans up to 3 levels deep, serves single-run if base dir itself containsevents.jsonl, skips symlinks to avoid duplicates from runner'slatestlink. - Arc<str> migration + NetworkCore method extraction - All internal
Stringfields migrated toArc<str>(clones become refcount bumps). Lock-body logic extracted intoNetworkCoremethods (prepare_link_regions,prepare_add_iface,prepare_replug_iface,remove_device,remove_router,resolve_link_target,remove_device_iface,renew_device_ip,add_dns_entry,router_nat_v6_params, etc.) with purpose-built setup structs. Eliminateslet x; { lock; x = ...; }pattern throughoutlab.rsandhandles.rs. - Mutex/lock architecture overhaul -
LabInnerstruct withnetns+canceloutside the mutex;with()/with_mut()helpers on handles; cachedname/nson Device/Router/Ix; per-nodetokio::sync::Mutex<()>for operation serialization;parking_lot::Mutexfor topology lock; all handle mutation methods made async; pre-await reads combined into single lock;set_nat_v6_modewrite order fixed - No-panics refactor - Device/Router handles return
ResultorOptioninstead of panicking on removed nodes;spawn()returnsResult<JoinHandle>;with_device/with_routerreturnOption<R>; all test call sites updated - Region index overflow -
region_base(idx)useschecked_mul(16).expect()instead of unchecked arithmetic - Fix doc typo - removed
(aka LinkCondition)redundancy from lab.rs module doc - Consolidate test helpers - removed
probe_udp_from,spawn_tcp_echo_in, syncudp_send_recv_count; all callers migrated totest_utilsequivalents - Remove dead region code - deleted unused allocators and fields
- Combine consecutive
nl_runblocks - merged v4 + v6 return-route calls insetup_router_async - Replace
parse().unwrap()with direct construction -net4(),net6(),region_base()helpers RouterBuilder::errorhelper - deduplicated 15-field struct literal in error paths- Real PMTU blackhole test - verifies MTU +
block_icmp_frag_neededdrops oversized packets spawn_command_async- added on Device, Router, and Ix- Unify NAT API - removed
RouterBuilder::nat_config(); users passNat::Custom(cfg) - Remove deprecated aliases - removed
NatMode,switch_route,set_impair, etc. - API rename
Impair->LinkCondition- enum, fields, methods, and presets all renamed - Suppress stderr on
tccommands - stderr captured viaStdio::piped()+.output() ensure_root_nsrace condition - eliminated by makingLab::new()asyncDeviceIface::ip()returnsOption- v6-only devices returnNoneObservedAddrwrapper - converted topub type ObservedAddr = SocketAddr- DNS overlay at creation time -
create_netns(name, dns_overlay)instead of set-after-create - Merge ns creation + async worker - single
Worker::spawn()saves 1 thread per namespace - Extract
apply_mount_overlay()- shared by async/sync workers, user threads, blocking pool - Remove redundant
nft flush ruleset- fresh namespaces have no rules - Dead
replace_default_route_v6- removed unused method - Duplicate docstring in
apply_nat_for_router- removed DnsEntries::new()panics -NetworkCore::new()now returnsResultImpair::Wifi/Mobiledoc inaccuracy - corrected to match actual valueseprintln!inapply_impair_in- replaced withtracing::warn!saturating_addon allocators - all 7 allocators usechecked_add+bail!- Remove unnecessary cleanup, simplify Netlink - fd-only namespaces, removed ResourceList and hooks
- Drop
NETSIM_NS_*from env_vars - callers use handle methods instead resources()->ResourceList::global()- associated function instead of free fn- Core internalization -
NetworkCoreand helpers madepub(crate) nl_runclosure noise -RouterSetupData/IfaceBuildderiveClone- Migrate tests to handle API - ~200+ call sites migrated, dead Lab methods removed
- Remove repetitive/legacy patterns - debug test removed, helpers consolidated
- Variable clones before
nl_run- structurally required, accepted as-is RouterData::wan_ifname()helper - deduplicates wan interface name logicRouterBuilder- builder pattern matchingDeviceBuilder- Core fns simplified to
async fn-set_link_state/replace_default_routeuse netlink directly - Persistent
Netlinkper namespace - created once per AsyncWorker - Test suite debugging - fixed 5+ failing tests across nat/link/region tests
- Async namespace worker redesign - two workers per namespace with
TaskHandle<T> Lab::init_tracing()cleanup - replaced withpatchbay_utils::init_tracing()- Dead iperf UI table - removed from UI
- Duplicate
spawn_reflector_in- consolidated intotest_utils.rs - Remove
RouterIdtype aliases - all code usesNodeId - Bridge/namespace naming - moved into
NetworkCore - Split
lib.rsmonolith - intolab.rs+config.rs shared_cache_rootremoval - callers passcache_direxplicitlyurl_cache_keyallocations - replaced withwrite!bufferStepTemplateDefexpansion - not applied, already correctSimFile/LabConfigduplication -#[serde(flatten)]appliedstage_build_binarydedup - not applied, paths divergewrite_progress/write_run_manifesttwins - extractedwrite_jsonhelperCaptureStoreaccessor pattern - privatefn lock()helper addedartifact_name_kindallocations - returns(&str, bool)now- Multi-pass router resolution - accepted as-is for current topology sizes
VmBinarySpecduplication - unified via shared crate dependency