Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions packages/@webex/plugin-meetings/src/media/properties.ts
Original file line number Diff line number Diff line change
Expand Up @@ -455,18 +455,22 @@ export default class MediaProperties {
*
* @param {string} issueType
* @param {string} issueSubType
* @param {string} correlationId
* @param {Object} metadata - static-per-meeting context included in the metric payload
* @returns {void}
*/
public sendMediaIssueMetric(issueType: string, issueSubType: string, correlationId) {
public sendMediaIssueMetric(
issueType: string,
issueSubType: string,
metadata: {correlationId: string; isMultistream: boolean; isInLobby: boolean}
) {
const key = `${issueType}_${issueSubType}`;

const count = (this.mediaIssueCounters[key] || 0) + 1;

this.mediaIssueCounters[key] = count;

this.throttledSendMediaIssueMetric({
correlationId,
...metadata,
...this.mediaIssueCounters,
});
}
Expand Down
10 changes: 5 additions & 5 deletions packages/@webex/plugin-meetings/src/meeting/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8020,11 +8020,11 @@ export default class Meeting extends StatelessWebexPlugin {
});

if (atLeastOneUnmutedOtherMember) {
this.mediaProperties.sendMediaIssueMetric(
'inbound_audio',
data.issueSubType,
this.correlationId
);
this.mediaProperties.sendMediaIssueMetric('inbound_audio', data.issueSubType, {
correlationId: this.correlationId,
isMultistream: this.isMultistream,
isInLobby: !!this.isUserUnadmitted,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Separate counters by lobby state before throttled send

sendMediaIssueMetric is throttled for 5 minutes, but this call now includes isInLobby from live meeting state; if a user is admitted during that window, multiple inbound-audio issues are accumulated into one counter while the emitted payload carries only the latest isInLobby value. That mislabels part of the aggregated count (lobby vs admitted) and can skew triage dashboards. This is reachable because lobby state transitions are expected (SelfUtils.hasUserBeenAdmitted / isLocusUserUnadmitted).

Useful? React with 👍 / 👎.

});

Trigger.trigger(
this,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -419,33 +419,39 @@ describe('MediaProperties', () => {
it('should send a behavioral metric with correct parameters', () => {
const issueType = 'audio';
const issueSubType = 'packet-loss';
const correlationId = 'test-correlation-id-123';
const metadata = {correlationId: 'test-correlation-id-123', isMultistream: true, isInLobby: false};

mediaProperties.sendMediaIssueMetric(issueType, issueSubType, correlationId);
mediaProperties.sendMediaIssueMetric(issueType, issueSubType, metadata);

assert.calledOnce(sendBehavioralMetricStub);
assert.calledWith(sendBehavioralMetricStub, BEHAVIORAL_METRICS.MEDIA_ISSUE_DETECTED, {
correlationId,
'audio_packet-loss': 1,
});
assert.calledOnceWithExactly(
sendBehavioralMetricStub,
BEHAVIORAL_METRICS.MEDIA_ISSUE_DETECTED,
{
...metadata,
'audio_packet-loss': 1,
}
);
});

it('should increment count while being throttled and reset it once metric goes out', () => {
const issueType = 'video';
const issueSubType = 'freeze';
const correlationId = 'test-correlation-id';
const metadata = {correlationId: 'test-correlation-id', isMultistream: false, isInLobby: true};

// Call multiple times with same issue type/subtype
mediaProperties.sendMediaIssueMetric(issueType, issueSubType, correlationId);
mediaProperties.sendMediaIssueMetric(issueType, issueSubType, correlationId);
mediaProperties.sendMediaIssueMetric(issueType, issueSubType, correlationId);
mediaProperties.sendMediaIssueMetric(issueType, issueSubType, metadata);
mediaProperties.sendMediaIssueMetric(issueType, issueSubType, metadata);
mediaProperties.sendMediaIssueMetric(issueType, issueSubType, metadata);

// First call should go through immediately, subsequent calls are throttled
assert.calledOnce(sendBehavioralMetricStub);
assert.calledWith(sendBehavioralMetricStub, BEHAVIORAL_METRICS.MEDIA_ISSUE_DETECTED, {
correlationId,
video_freeze: 1, // Only the first call goes through due to throttling
});
assert.calledOnceWithExactly(
sendBehavioralMetricStub,
BEHAVIORAL_METRICS.MEDIA_ISSUE_DETECTED,
{
...metadata,
video_freeze: 1, // Only the first call goes through due to throttling
}
);
sendBehavioralMetricStub.resetHistory();

assert.equal(mediaProperties.mediaIssueCounters['video_freeze'], 2); // counter should be reset after the first metric goes out, hence only 2 not 3 here
Expand All @@ -456,29 +462,29 @@ describe('MediaProperties', () => {
sendBehavioralMetricStub,
BEHAVIORAL_METRICS.MEDIA_ISSUE_DETECTED,
{
correlationId,
...metadata,
video_freeze: 2,
}
);
});

it('should track different issue types separately in counters', () => {
const correlationId = 'test-correlation-id';
const metadata = {correlationId: 'test-correlation-id', isMultistream: true, isInLobby: false};

// Send different issue types
mediaProperties.sendMediaIssueMetric('audio', 'packet-loss', correlationId);
mediaProperties.sendMediaIssueMetric('video', 'freeze', correlationId);
mediaProperties.sendMediaIssueMetric('audio', 'packet-loss', correlationId);
mediaProperties.sendMediaIssueMetric('audio', 'packet-loss', correlationId);
mediaProperties.sendMediaIssueMetric('audio', 'packet-loss', correlationId);
mediaProperties.sendMediaIssueMetric('video', 'freeze', correlationId);
mediaProperties.sendMediaIssueMetric('audio', 'packet-loss', metadata);
mediaProperties.sendMediaIssueMetric('video', 'freeze', metadata);
mediaProperties.sendMediaIssueMetric('audio', 'packet-loss', metadata);
mediaProperties.sendMediaIssueMetric('audio', 'packet-loss', metadata);
mediaProperties.sendMediaIssueMetric('audio', 'packet-loss', metadata);
mediaProperties.sendMediaIssueMetric('video', 'freeze', metadata);

// First call should go through immediately, subsequent calls are throttled
assert.calledOnceWithExactly(
sendBehavioralMetricStub,
BEHAVIORAL_METRICS.MEDIA_ISSUE_DETECTED,
{
correlationId,
...metadata,
'audio_packet-loss': 1,
}
);
Expand All @@ -495,7 +501,7 @@ describe('MediaProperties', () => {
sendBehavioralMetricStub,
BEHAVIORAL_METRICS.MEDIA_ISSUE_DETECTED,
{
correlationId,
...metadata,
video_freeze: 2,
'audio_packet-loss': 3,
}
Expand All @@ -505,18 +511,18 @@ describe('MediaProperties', () => {
it('should flush throttled metrics when unsetPeerConnection is called', () => {
const issueType = 'share';
const issueSubType = 'connection-lost';
const correlationId = 'test-correlation-id';
const metadata = {correlationId: 'test-correlation-id', isMultistream: false, isInLobby: true};

// Send metrics multiple times
mediaProperties.sendMediaIssueMetric(issueType, issueSubType, correlationId);
mediaProperties.sendMediaIssueMetric(issueType, issueSubType, correlationId);
mediaProperties.sendMediaIssueMetric(issueType, issueSubType, metadata);
mediaProperties.sendMediaIssueMetric(issueType, issueSubType, metadata);

// First call should go through immediately
assert.calledOnceWithExactly(
sendBehavioralMetricStub,
BEHAVIORAL_METRICS.MEDIA_ISSUE_DETECTED,
{
correlationId,
...metadata,
'share_connection-lost': 1,
}
);
Expand All @@ -529,7 +535,7 @@ describe('MediaProperties', () => {
sendBehavioralMetricStub,
BEHAVIORAL_METRICS.MEDIA_ISSUE_DETECTED,
{
correlationId,
...metadata,
'share_connection-lost': 1,
}
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5219,7 +5219,11 @@ describe('plugin-meetings', () => {
meeting.mediaProperties.sendMediaIssueMetric,
'inbound_audio',
fakeEventData.issueSubType,
meeting.correlationId
{
correlationId: meeting.correlationId,
isMultistream: meeting.isMultistream,
isInLobby: !!meeting.isUserUnadmitted,
}
);
});
});
Expand Down
Loading