Fix runtime child route forwarding

This commit is contained in:
Michael Mikovsky
2026-05-09 12:47:51 -06:00
parent a61c0ce72d
commit 0f54b53a79
2 changed files with 70 additions and 3 deletions
-2
View File
@@ -285,8 +285,6 @@ connection closes or unregisters
- Local outbound calls through the runtime are not implemented. - Local outbound calls through the runtime are not implemented.
- Connection registration does not yet atomically update endpoint routes. - Connection registration does not yet atomically update endpoint routes.
- Disconnect does not yet clean hooks, sessions, route state, and queued effects. - Disconnect does not yet clean hooks, sessions, route state, and queued effects.
- `RouteDecision::Child(index)` still depends on index compatibility with the
existing `ProtocolEndpoint` route table.
- Child ingress still allocates because the existing `Ingress::Child` owns a - Child ingress still allocates because the existing `Ingress::Child` owns a
`Vec<String>`. `Vec<String>`.
+70 -1
View File
@@ -236,7 +236,10 @@ where
.endpoint .endpoint
.endpoint() .endpoint()
.child_routes() .child_routes()
.get(index) .iter()
// RouteDecision indexes are compiled from registered children only.
.filter(|child| child.registered)
.nth(index)
.and_then(|child| { .and_then(|child| {
self.connections self.connections
.registered_by_path(ConnectionDirection::Child, &child.path) .registered_by_path(ConnectionDirection::Child, &child.path)
@@ -393,6 +396,72 @@ mod tests {
assert_eq!(runtime.transport().sent[0].0, child); assert_eq!(runtime.transport().sent[0].0, child);
} }
#[test]
fn child_route_decision_uses_registered_child_order() {
let parent = ConnectionId::new(1);
let unregistered_child = ConnectionId::new(2);
let registered_child = ConnectionId::new(3);
let mut connections = Connections::new();
connections.push(Connection::registered(
parent,
ConnectionDirection::Parent,
vec![],
ConnectionGeneration::INITIAL,
));
connections.push(Connection::registered(
unregistered_child,
ConnectionDirection::Child,
vec![String::from("agent"), String::from("spare")],
ConnectionGeneration::INITIAL,
));
connections.push(Connection::registered(
registered_child,
ConnectionDirection::Child,
vec![String::from("agent"), String::from("grand")],
ConnectionGeneration::INITIAL,
));
let endpoint = ProtocolEndpoint::new(
vec![String::from("agent")],
Some(vec![]),
vec![
ChildRoute {
path: vec![String::from("agent"), String::from("spare")],
registered: false,
},
ChildRoute::registered(vec![String::from("agent"), String::from("grand")]),
],
vec![],
);
let frame = encode_packet(
&PacketHeader {
packet_type: PacketType::Call,
src_path: vec![],
dst_path: vec![String::from("agent"), String::from("grand")],
dst_leaf: None,
hook_id: None,
},
&CallMessage {
procedure_id: String::from("org.example.v1.echo.invoke"),
data: vec![],
response_hook: None,
},
)
.expect("frame encodes");
let transport = RecordingTransport {
inbound: Some((parent, frame)),
sent: Vec::new(),
};
let mut runtime = NodeRuntime::new(EndpointState::new(endpoint), connections, transport);
let outcome = runtime.tick(TickBudget::default()).expect("tick succeeds");
assert_eq!(outcome.outbound_frames, 1);
assert_eq!(runtime.transport().sent[0].0, registered_child);
}
#[test] #[test]
fn receive_keeps_local_events_queued_for_leaf_dispatch() { fn receive_keeps_local_events_queued_for_leaf_dispatch() {
let parent = ConnectionId::new(1); let parent = ConnectionId::new(1);