Merge pull request #1005 from robintown/parallel-calls
Detect split-brains caused by parallel calls
This commit is contained in:
commit
8eafb1ae4a
3 changed files with 246 additions and 4 deletions
65
src/room/checkForParallelCalls.ts
Normal file
65
src/room/checkForParallelCalls.ts
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
Copyright 2023 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { EventType } from "matrix-js-sdk/src/@types/event";
|
||||
import { RoomState } from "matrix-js-sdk/src/models/room-state";
|
||||
|
||||
import { PosthogAnalytics } from "../analytics/PosthogAnalytics";
|
||||
|
||||
function isObject(x: unknown): x is Record<string, unknown> {
|
||||
return typeof x === "object" && x !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the state of a room for multiple calls happening in parallel, sending
|
||||
* the details to PostHog if that is indeed what's happening. (This is unwanted
|
||||
* as it indicates a split-brain scenario.)
|
||||
*/
|
||||
export function checkForParallelCalls(state: RoomState): void {
|
||||
const now = Date.now();
|
||||
const participantsPerCall = new Map<string, number>();
|
||||
|
||||
// For each participant in each call, increment the participant count
|
||||
for (const e of state.getStateEvents(EventType.GroupCallMemberPrefix)) {
|
||||
const content = e.getContent<Record<string, unknown>>();
|
||||
const calls: unknown[] = Array.isArray(content["m.calls"])
|
||||
? content["m.calls"]
|
||||
: [];
|
||||
|
||||
for (const call of calls) {
|
||||
if (isObject(call) && typeof call["m.call_id"] === "string") {
|
||||
const devices: unknown[] = Array.isArray(call["m.devices"])
|
||||
? call["m.devices"]
|
||||
: [];
|
||||
|
||||
for (const device of devices) {
|
||||
if (isObject(device) && (device["expires_ts"] as number) > now) {
|
||||
const participantCount =
|
||||
participantsPerCall.get(call["m.call_id"]) ?? 0;
|
||||
participantsPerCall.set(call["m.call_id"], participantCount + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (participantsPerCall.size > 1) {
|
||||
PosthogAnalytics.instance.trackEvent({
|
||||
eventName: "ParallelCalls",
|
||||
participantsPerCall: Object.fromEntries(participantsPerCall),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -29,7 +29,7 @@ import { CallFeed, CallFeedEvent } from "matrix-js-sdk/src/webrtc/callFeed";
|
|||
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { IWidgetApiRequest } from "matrix-widget-api";
|
||||
import { MatrixClient } from "matrix-js-sdk";
|
||||
import { MatrixClient, RoomStateEvent } from "matrix-js-sdk";
|
||||
import {
|
||||
ByteSentStatsReport,
|
||||
ConnectionStatsReport,
|
||||
|
|
@ -42,6 +42,7 @@ import { TranslatedError, translatedError } from "../TranslatedError";
|
|||
import { ElementWidgetActions, ScreenshareStartData, widget } from "../widget";
|
||||
import { OTelGroupCallMembership } from "../otel/OTelGroupCallMembership";
|
||||
import { ElementCallOpenTelemetry } from "../otel/otel";
|
||||
import { checkForParallelCalls } from "./checkForParallelCalls";
|
||||
|
||||
export enum ConnectionState {
|
||||
EstablishingCall = "establishing call", // call hasn't been established yet
|
||||
|
|
@ -377,18 +378,19 @@ export function useGroupCall(
|
|||
groupCall.on(GroupCallEvent.CallsChanged, onCallsChanged);
|
||||
groupCall.on(GroupCallEvent.ParticipantsChanged, onParticipantsChanged);
|
||||
groupCall.on(GroupCallEvent.Error, onError);
|
||||
|
||||
groupCall.on(
|
||||
GroupCallStatsReportEvent.ConnectionStats,
|
||||
onConnectionStatsReport
|
||||
);
|
||||
|
||||
groupCall.on(
|
||||
GroupCallStatsReportEvent.ByteSentStats,
|
||||
onByteSentStatsReport
|
||||
);
|
||||
|
||||
groupCall.on(GroupCallStatsReportEvent.SummaryStats, onSummaryStatsReport);
|
||||
groupCall.room.currentState.on(
|
||||
RoomStateEvent.Update,
|
||||
checkForParallelCalls
|
||||
);
|
||||
|
||||
updateState({
|
||||
error: null,
|
||||
|
|
@ -448,6 +450,10 @@ export function useGroupCall(
|
|||
GroupCallStatsReportEvent.SummaryStats,
|
||||
onSummaryStatsReport
|
||||
);
|
||||
groupCall.room.currentState.off(
|
||||
RoomStateEvent.Update,
|
||||
checkForParallelCalls
|
||||
);
|
||||
leaveCall();
|
||||
};
|
||||
}, [groupCall, updateState, leaveCall]);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue