feat: Support in-place ICE restart - #1299
Merged
Merged
Conversation
This was referenced Aug 6, 2026
jitsi-ci Bot
pushed a commit
to jitsi/jitsi-pr-tests-pages
that referenced
this pull request
Aug 6, 2026
Adds the machinery to ask a bridge to restart ICE in place and to handle its answer. Colibri2Session.restartIce sends a conference-modify with <transport ice-restart="true"/> for the endpoint; the bridge creates a new ICE agent with fresh credentials while the existing one keeps carrying media (make-before-break) and answers with the new transport. ColibriV2SessionManager.endpointIceRestarted applies a latest-wins guard keyed on the ice-generation attribute before firing a new ColibriSessionManager.Listener event: colibri2 responses are handled on an IO pool, so if a participant restarts twice in quick succession the two responses can arrive in either order. JingleSession.sendTransportInfo signals the transport on to the client as a plain Jingle transport-info. Also adds the ice_restarts_requested/relayed/failed metrics, named to match jitsi-videobridge's metrics for the other half of the flow.
A participant asks for an in-place ICE restart with a Jingle session-info carrying <bridge-session ice-restart="true"/>. Unlike the existing session-terminate + restart="true" flow, which stays as the fallback, this does not tear the session down: jicofo validates the bridge-session ID, rate-limits the request, asks the bridge to rotate its ICE credentials in place, and relays the resulting transport back to the participant in a transport-info. The participant then signals its own new credentials in a transport-info tagged with the same ice-generation, which the existing onTransportInfo path already forwards to the bridge unmodified. ICE restarts get their own rate-limit budget (with the same configured limits) so that a burst of them can not exhaust the budget for full session restarts, and vice versa. Requests are gated by jicofo.conference.enable-ice-restart, default true; when disabled they are rejected with feature-not-implemented so the client can fall back.
The bridge declines an ICE restart by answering with no transport rather than with an error, so that one endpoint cannot fail a conference-modify carrying updates for others. Nothing was propagated back to the client, which meant the participant waited for a restart that would never arrive and only recovered via its own 15s timeout - slower than if the in-place restart had never been attempted. The bridge declines for reasons that are not exotic: ICE restarts disabled in its configuration, a transport that is not established yet, or a transport that is shutting down. The first of those covers the whole of a rollout, when clients have the feature enabled and only some bridges do. Escalate instead: on a declined restart, re-invite the participant, which is the recovery it would have got had it never asked for an in-place restart, and now happens immediately rather than after a client timeout. As in iceRestart(), Bridge.endpointRequestedRestart() is intentionally not called - a bridge declining a restart is not evidence that it is failing ICE, and counting it would feed the bridge-selection penalty.
Picks up the ice-generation and ice-restart attributes used by the in-place ICE restart.
bgrozev
force-pushed
the
in-place-ice-restart
branch
from
August 10, 2026 20:03
0946409 to
72d937f
Compare
jitsi-ci Bot
pushed a commit
to jitsi/jitsi-pr-tests-pages
that referenced
this pull request
Aug 10, 2026
JonathanLennox
approved these changes
Aug 13, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements the jicofo side of an in-place ICE restart, in which the bridge creates a new ICE agent with fresh credentials while the existing one keeps carrying media until the new one connects (make-before-break). Jicofo receives the client's request, asks the bridge, and relays the bridge's new transport back to the client.
Intake
The client asks for a restart with a Jingle
session-infocarrying<bridge-session ice-restart="true"/>.session-infois the action for in-session informational messages and implies no state change, which is right here: the session is explicitly not being torn down. The existing modes on the neighbouring paths are unchanged and still trigger a full re-invite —<ice-state>failed</ice-state>onsession-info, andrestart="true"onsession-terminate, which remains the client's fallback when an in-place restart does not work out.The request is validated against the bridge-session ID and rate limited. The rate limiter is a separate instance reusing the existing
jicofo.conference.restart-request-rate-limits.*keys, so ICE restarts and full-session restarts do not share a budget.Relay
Colibri2Session.restartIce()sends a conference-modify with<transport ice-restart="true"/>. The bridge answers with its new transport, whichColibriV2SessionManager.endpointIceRestarted()picks up and forwards to the participant as a plain Jingletransport-info(nottransport-replace, so the client stays the answerer).Colibri2 responses are handled on
TaskPools.ioPooland can reorder, so a latest-wins guard on the transport'sice-generation(tracked per participant) drops anything not newer than what was already relayed.The reverse leg needs no new code: the client's own new credentials come back through the existing
onTransportInfotoupdateTransporttoupdateParticipantpath, and theice-generationattribute survives it unchanged.Config and metrics
jicofo.conference.enable-ice-restart(default true). When disabled, the request is answered withfeature-not-implementedand the client falls back on its own.IceRestartMetricscounts requests received, transports relayed, and failures. Failures currently share one counter across several causes (disabled, rate limited, stale bridge-session ID, unknown endpoint, no transport in the response, stale generation, no Jingle session); the log lines distinguish them.Bridge.endpointRequestedRestart()is deliberately not called here, unlike theiceFailedandterminateSessionpaths. An in-place restart is normally a client network change rather than evidence the bridge is failing ICE, so counting it would feed the bridge-selection penalty unfairly.Part of a multi-repo change; all PRs share the
in-place-ice-restartbranch name so CI tests them together: