Bridge Java-to-Go runtime behavior by enforcing forward ownership checks, wiring node command dispatch/diagnostics, and adding contract coverage so migrated APIs can run with production semantics.
- Add batch delete, redeploy, and change-tunnel for forwards
- Add batch delete and redeploy for tunnels
- Add batch delete for nodes
- Add multi-select UI with floating toolbar on all three pages
- Create DTOs: BatchDeleteDto, BatchRedeployDto, BatchChangeTunnelDto, BatchOperationResultDto
When multiple TcpPing requests are sent in parallel for diagnosing
multiple remote addresses, the Go agent was processing them serially.
This caused later requests to timeout (10s) while waiting for earlier
requests to complete.
Changes:
- Only TcpPing commands run in goroutines for parallel execution
- TcpPing (read-only diagnostic) no longer triggers saveConfig()
- Other state-mutating commands remain synchronous with config save
- Add mutex to saveConfig() to protect concurrent file writes
Previously, forward/tunnel diagnosis executed TCP pings sequentially,
causing total time to accumulate. If the first remote address timed out
(5s), subsequent checks could push total time beyond the frontend's 30s
timeout, resulting in diagnosis failure even for healthy endpoints.
Now all diagnostic tasks run in parallel using CompletableFuture, so
total time equals max(individual ping time) instead of sum.
When using relay connector with noDelay=false (default), connection
errors to the final target are deferred until first read/write during
Transport(). Previously the Transport() return value was ignored,
causing the marker to never be called for unreachable targets.
Now we capture the Transport() error and mark the node as failed,
enabling failover for subsequent connections.
When using relay connector with noDelay=false (default), connection
errors are deferred until first read/write. This prevents the forwarder
marker from being called, causing failover to never trigger.
Setting nodelay=true ensures connection errors propagate immediately,
allowing proper failover behavior when chain targets are unreachable.
The single-node bypass in hop.Select() was preventing FailFilter from
being applied when retry excludes reduced available nodes to one.
This caused failed forwarder nodes to keep being selected instead of
failing over to healthy alternatives.
FailFilter's built-in safety guard (len <= 1 returns as-is) ensures
the last remaining node is never permanently blocked.