Skip to content

Commit 137c67e

Browse files
authored
fix: Don't treat Jibri busy response as transient error (#659)
* ref: change StartExceptions to be individual class types * don't treat a busy response as a transient error
1 parent c7c2614 commit 137c67e

File tree

4 files changed

+106
-32
lines changed

4 files changed

+106
-32
lines changed

src/main/java/org/jitsi/jicofo/recording/jibri/JibriRecorder.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -178,9 +178,9 @@ protected IQ handleStartRequest(JibriIq iq)
178178
catch (JibriSession.StartException exc)
179179
{
180180
ErrorIQ errorIq;
181-
String reason = exc.getReason();
181+
String reason = exc.getMessage();
182182

183-
if (StartException.ALL_BUSY.equals(reason))
183+
if (exc instanceof StartException.AllBusy)
184184
{
185185
logger.info("Failed to start a Jibri session, " +
186186
"all Jibris were busy");
@@ -189,7 +189,7 @@ protected IQ handleStartRequest(JibriIq iq)
189189
XMPPError.Condition.resource_constraint,
190190
"all Jibris are busy");
191191
}
192-
else if (StartException.NOT_AVAILABLE.equals(reason))
192+
else if (exc instanceof StartException.NotAvailable)
193193
{
194194
logger.info("Failed to start a Jibri session, " +
195195
"no Jibris available");

src/main/java/org/jitsi/jicofo/recording/jibri/JibriSession.java

Lines changed: 68 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -336,10 +336,10 @@ private void startInternal()
336336
logger.error("Unable to find an available Jibri, can't start");
337337

338338
if (jibriDetector.isAnyInstanceConnected()) {
339-
throw new StartException(StartException.ALL_BUSY);
339+
throw new StartException.AllBusy();
340340
}
341341

342-
throw new StartException(StartException.NOT_AVAILABLE);
342+
throw new StartException.NotAvailable();
343343
}
344344

345345
try
@@ -351,14 +351,17 @@ private void startInternal()
351351
catch (Exception e)
352352
{
353353
logger.error("Failed to send start Jibri IQ: " + e, e);
354-
jibriDetector.memberHadTransientError(jibriJid);
354+
if (!(e instanceof StartException.OneBusy))
355+
{
356+
jibriDetector.memberHadTransientError(jibriJid);
357+
}
355358
if (!maxRetriesExceeded())
356359
{
357360
retryRequestWithAnotherJibri();
358361
}
359362
else
360363
{
361-
throw new StartException(StartException.INTERNAL_SERVER_ERROR);
364+
throw new StartException.InternalServerError();
362365
}
363366
}
364367
}
@@ -558,20 +561,22 @@ private void sendJibriStartIq(final Jid jibriJid)
558561
"Unexpected response to start request: "
559562
+ (reply != null ? reply.toXML() : "null"));
560563

561-
throw new StartException(StartException.UNEXPECTED_RESPONSE);
564+
throw new StartException.UnexpectedResponse();
562565
}
563566

564567
JibriIq jibriIq = (JibriIq) reply;
565-
566-
// According to the "protocol" only PENDING status is allowed in
567-
// response to the start request.
568-
if (!Status.PENDING.equals(jibriIq.getStatus()))
568+
if (isBusyResponse(jibriIq))
569+
{
570+
logger.info("Jibri " + jibriIq.getFrom() + " was busy");
571+
throw new StartException.OneBusy();
572+
}
573+
if (!isPendingResponse(jibriIq))
569574
{
570575
logger.error(
571576
"Unexpected status received in response to the start IQ: "
572577
+ jibriIq.toXML());
573578

574-
throw new StartException(StartException.UNEXPECTED_RESPONSE);
579+
throw new StartException.UnexpectedResponse();
575580
}
576581

577582
processJibriIqFromJibri(jibriIq);
@@ -830,25 +835,47 @@ void onSessionStateChanged(
830835
JibriIq.FailureReason failureReason);
831836
}
832837

833-
static public class StartException extends Exception
838+
static public abstract class StartException extends Exception
834839
{
835-
final static String ALL_BUSY = "All Jibri instances are busy";
836-
final static String INTERNAL_SERVER_ERROR = "Internal server error";
837-
final static String NOT_AVAILABLE = "No Jibris available";
838-
final static String UNEXPECTED_RESPONSE = "Unexpected response";
839-
840-
private final String reason;
841-
842-
StartException(String reason)
840+
public StartException(String message)
843841
{
844-
super(reason);
845-
846-
this.reason = reason;
842+
super(message);
847843
}
848844

849-
String getReason()
845+
static public class AllBusy extends StartException
846+
{
847+
public AllBusy()
848+
{
849+
super("All jibri instances are busy");
850+
}
851+
}
852+
static public class InternalServerError extends StartException
850853
{
851-
return reason;
854+
public InternalServerError()
855+
{
856+
super("Internal server error");
857+
}
858+
}
859+
static public class NotAvailable extends StartException
860+
{
861+
public NotAvailable()
862+
{
863+
super("No Jibris available");
864+
}
865+
}
866+
static public class UnexpectedResponse extends StartException
867+
{
868+
public UnexpectedResponse()
869+
{
870+
super("Unexpected response");
871+
}
872+
}
873+
static public class OneBusy extends StartException
874+
{
875+
public OneBusy()
876+
{
877+
super("This Jibri instance was busy");
878+
}
852879
}
853880
}
854881

@@ -886,4 +913,21 @@ public void instanceOffline(Jid jid)
886913
}
887914
}
888915

916+
/**
917+
* Returns true if the given IQ represens a busy response from Jibri
918+
* @param iq
919+
* @return
920+
*/
921+
private boolean isBusyResponse(JibriIq iq)
922+
{
923+
return Status.OFF.equals(iq.getStatus()) &&
924+
iq.isFailure() &&
925+
FailureReason.BUSY.equals(iq.getFailureReason());
926+
}
927+
928+
private boolean isPendingResponse(JibriIq iq)
929+
{
930+
return Status.PENDING.equals(iq.getStatus());
931+
}
932+
889933
}

src/main/java/org/jitsi/jicofo/recording/jibri/JibriSipGateway.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -169,19 +169,19 @@ protected IQ handleStartRequest(JibriIq iq)
169169
}
170170
catch (StartException exc)
171171
{
172-
String reason = exc.getReason();
172+
String reason = exc.getMessage();
173173
logger.info(
174174
"Failed to start a Jibri session: " + reason, exc);
175175
sipSessions.remove(sipAddress);
176176
ErrorIQ errorIq;
177-
if (StartException.ALL_BUSY.equals(reason))
177+
if (exc instanceof StartException.AllBusy)
178178
{
179179
errorIq = ErrorResponse.create(
180180
iq,
181181
XMPPError.Condition.resource_constraint,
182182
"all Jibris are busy");
183183
}
184-
else if(StartException.NOT_AVAILABLE.equals(reason))
184+
else if(exc instanceof StartException.NotAvailable)
185185
{
186186
errorIq = ErrorResponse.create(
187187
iq,

src/test/kotlin/org/jitsi/jicofo/recording/jibri/JibriSessionTest.kt

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import io.kotest.matchers.shouldNotBe
2525
import io.mockk.every
2626
import io.mockk.mockk
2727
import io.mockk.mockkStatic
28+
import io.mockk.slot
2829
import io.mockk.spyk
2930
import io.mockk.verify
3031
import org.jitsi.jicofo.FocusBundleActivator
@@ -54,7 +55,7 @@ class JibriSessionTest : ShouldSpec({
5455
JidCreate.bareFrom("[email protected]")
5556
)
5657
val detector: JibriDetector = mockk {
57-
every { selectJibri() } returnsMany(jibriList)
58+
every { selectJibri() } returnsMany (jibriList)
5859
every { isAnyInstanceConnected } returns true
5960
every { memberHadTransientError(any()) } answers {
6061
// Simulate the real JibriDetector logic and put the Jibri at the back of the list
@@ -118,9 +119,38 @@ class JibriSessionTest : ShouldSpec({
118119
shouldThrow<JibriSession.StartException> {
119120
jibriSession.start()
120121
}
121-
verify(exactly = maxNumRetries + 1) { xmppConnection.sendPacketAndGetReply(any())}
122+
verify(exactly = maxNumRetries + 1) { xmppConnection.sendPacketAndGetReply(any()) }
122123
}
123124
}
124125
}
125126
}
127+
context("Trying to start a session with a Jibri that is busy") {
128+
val iq = slot<IQ>()
129+
// First return busy, then pending
130+
every { xmppConnection.sendPacketAndGetReply(capture(iq)) } answers {
131+
JibriIq().apply {
132+
type = IQ.Type.result
133+
from = iq.captured.to
134+
to = iq.captured.from
135+
shouldRetry = true
136+
status = JibriIq.Status.OFF
137+
failureReason = JibriIq.FailureReason.BUSY
138+
}
139+
} andThen {
140+
JibriIq().apply {
141+
type = IQ.Type.result
142+
from = iq.captured.to
143+
to = iq.captured.from
144+
shouldRetry = true
145+
status = JibriIq.Status.PENDING
146+
}
147+
}
148+
jibriSession.start()
149+
should("not count as a transient error") {
150+
verify(exactly = 0) { detector.memberHadTransientError(any()) }
151+
}
152+
should("retry with another jibri") {
153+
verify(exactly = 2) { xmppConnection.sendPacketAndGetReply(any()) }
154+
}
155+
}
126156
})

0 commit comments

Comments
 (0)