Skip to content

Conversation

@venky-mediboina
Copy link
Contributor

@venky-mediboina venky-mediboina commented Jun 11, 2025

COMPLETES #SPARK-610518

This pull request addresses

Saving the subnet ip details and sending to web-client

by making the following changes

Saved the subnet ip details in reachability results and sending to web-client

Change Type

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update
  • Tooling change
  • Internal code refactor

The following scenarios were tested

< ENUMERATE TESTS PERFORMED, WHETHER MANUAL OR AUTOMATED >

The GAI Coding Policy And Copyright Annotation Best Practices

  • GAI was not used (or, no additional notation is required)
  • Code was generated entirely by GAI
  • GAI was used to create a draft that was subsequently customized or modified
  • Coder created a draft manually that was non-substantively modified by GAI (e.g., refactoring was performed by GAI on manually written code)
  • Tool used for AI assistance (GitHub Copilot / Other - specify)
    • Github Copilot
    • Other - Please Specify
  • This PR is related to
    • Feature
    • Defect fix
    • Tech Debt
    • Automation

I certified that

  • I have read and followed contributing guidelines
  • I discussed changes with code owners prior to submitting this pull request
  • I have not skipped any automated checks
  • All existing and new tests passed
  • I have updated the documentation accordingly

Make sure to have followed the contributing guidelines before submitting.

@venky-mediboina venky-mediboina requested review from a team as code owners June 11, 2025 06:21
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jun 11, 2025

📝 Walkthrough

Walkthrough

Introduces per-protocol, per-subnet reachability tracking by adding SubnetDetails arrays for udp/tcp/xtls and seeding them from cluster URLs. ClusterReachability now exposes a public clusterInfo property, a new resultDetailsUpdated event, and uses a dynamic checkIP helper. ICE candidate parsing was changed to extract server IP and port (from candidate url or fields) and pass port into saveResult; saveResult now updates/creates per-subnet detail entries. Types and persisted result shapes were extended (SubnetDetails, ClusterNode, TransportResult.details and TransportResultForBackend.details/minLatency). Adds STUN_SERVER_URL_REGEX, PROTOCOLS_LIST, and an is-ip dependency.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.


📜 Recent review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f01b21a and 543d74a.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (1)
  • packages/@webex/plugin-meetings/package.json (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/@webex/plugin-meetings/package.json
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Initialize Project
  • GitHub Check: AWS Amplify Console Web Preview
✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@venky-mediboina venky-mediboina changed the title Subnet details fix(plugin-meetings): added saving subnet ip details in reachability Jun 11, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🔭 Outside diff range comments (1)
packages/@webex/plugin-meetings/src/reachability/index.ts (1)

168-178: ⚠️ Potential issue

Fix subnet logging to display meaningful information

The subnet is now an object with properties, but line 174 logs it directly which will output [object Object]. Consider logging specific subnet details for better debugging.

-          logMessage += `${subnet}`;
+          logMessage += `${subnet.serverIps}:${subnet.port}/${subnet.protocol}`;
🧹 Nitpick comments (2)
packages/@webex/plugin-meetings/src/reachability/reachability.types.ts (1)

13-21: Consider refining the SubnetDetails type definition

A few suggestions for the type definition:

  1. The protocol property appears to always be set based on usage - consider making it required
  2. Use boolean instead of true | false for consistency
  3. Consider renaming serverIps to serverIp since it contains a single IP address
 export type SubnetDetails = {
-  protocol?: 'udp' | 'tcp' | 'xtls'; // Protocol used for the test
-  serverIps: string; // IP address of the subnet being tested
-  reachable: true | false; // Result for this specific address
+  protocol: 'udp' | 'tcp' | 'xtls'; // Protocol used for the test
+  serverIp: string; // IP address of the subnet being tested
+  reachable: boolean; // Result for this specific address
   'answered-tx'?: number; // 1 if reachable, 0 otherwise
   'lost-tx'?: number; // 1 if unreachable, 0 otherwise
   port: number; // Port used for the test
   latencies?: number[];
 };
packages/@webex/plugin-meetings/src/reachability/clusterReachability.ts (1)

183-183: Remove commented code

The commented line should be removed to maintain code cleanliness.

-    // this.pc = null;
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4f2ba1a and fdbba5a.

📒 Files selected for processing (3)
  • packages/@webex/plugin-meetings/src/reachability/clusterReachability.ts (9 hunks)
  • packages/@webex/plugin-meetings/src/reachability/index.ts (2 hunks)
  • packages/@webex/plugin-meetings/src/reachability/reachability.types.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: AWS Amplify Console Web Preview

@aws-amplify-us-east-2
Copy link

This pull request is automatically being deployed by Amplify Hosting (learn more).

Access this pull request here: https://pr-4334.d3m3l2kee0btzx.amplifyapp.com

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (3)
packages/@webex/plugin-meetings/src/reachability/index.ts (1)

541-602: Missing null-safety & quadratic uniqueness loop

cleanUpDomainNames repeatedly does
protocolResult.details.filter(...)
inside nested loops without optional chaining; if any cluster lacks details, an exception will bubble.

Additionally, uniqueness is enforced with findIndex per element ‑ an O(n²) op. A Set keyed by serverIps:port would be linear and simpler.

Not blocking, but worth tightening.

🧰 Tools
🪛 Biome (1.9.4)

[error] 558-558: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

packages/@webex/plugin-meetings/src/reachability/clusterReachability.ts (2)

53-66: Inline type deserves a dedicated interface

The anonymous {serverIps, port, …} shape is used throughout the file and in index.ts. Declare a SubnetDetails interface once and reuse to avoid drift and improve IntelliSense.


466-498: Pre-population inflates lost-tx and hides duplicates

Every subnet is seeded with lost-tx: 1. Before any traffic, loss is already 100 %. Initialise to 0.

Additionally, using ${serverIps}:${port}:${protocol} as key means dual-stack (v4/v6) or multiple DNS A-records at the same host/port will overwrite each other. Consider including the resolved IP in the key or keeping a per-protocol Map<serverIps, Map<port, SubnetDetails>>.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fdbba5a and 84866df.

📒 Files selected for processing (4)
  • packages/@webex/plugin-meetings/src/reachability/clusterReachability.ts (10 hunks)
  • packages/@webex/plugin-meetings/src/reachability/index.ts (5 hunks)
  • packages/@webex/plugin-meetings/src/reachability/reachability.types.ts (2 hunks)
  • packages/@webex/plugin-meetings/src/reachability/util.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/@webex/plugin-meetings/src/reachability/reachability.types.ts
🧰 Additional context used
🪛 Biome (1.9.4)
packages/@webex/plugin-meetings/src/reachability/index.ts

[error] 558-558: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: AWS Amplify Console Web Preview

Copy link
Collaborator

@marcin-bazyl marcin-bazyl left a comment

Choose a reason for hiding this comment

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

I haven't tested this PR, but based on the comments I have serious doubts if this code works at all. Has this been tested and proven to work?
Also there are multiple AI comments, please go through all of them and either address them or mark as resolved if you think they're wrong.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (2)
packages/@webex/plugin-meetings/src/reachability/index.ts (1)

941-943: Remove hardcoded domain name assumptions.

The hardcoded .public. check was previously flagged as problematic since:

  1. Domain names can appear in any protocol, not just xtls
  2. We shouldn't make assumptions about what FQDNs contain

Consider using the isDomainName utility function consistently instead of substring matching.

-            .filter((subnet) => subnet.protocol === 'xtls' && subnet.serverIps.includes('.public.'))
+            .filter((subnet) => subnet.protocol === 'xtls' && isDomainName(subnet.serverIps))
packages/@webex/plugin-meetings/src/reachability/clusterReachability.ts (1)

345-361: Fix critical logic bug in new subnet creation.

When adding a new subnet that was just successfully reached, the code incorrectly marks it as unreachable. This creates contradictory data since we just proved the subnet is reachable.

         this.subnets.set(subnetKey, {
           serverIps: serverIp,
           port,
           protocol,
-          reachable: false, // Retain domain name for failures
-          'answered-tx': 0,
-          'lost-tx': 1,
-          latencies: [],
+          reachable: true,
+          'answered-tx': 1,
+          'lost-tx': 0,
+          latencies: [latency],
         });
🧹 Nitpick comments (2)
packages/@webex/plugin-meetings/src/reachability/util.ts (1)

47-58: Remove duplicate JSDoc comments.

There are duplicate JSDoc comment blocks for the isDomainName function. Keep only one set of comments to improve code cleanliness.

-/**
- * Checks if the given server IP is a domain name.
- *
- * @param {string} serverIps - The server IP or domain name to check.
- * @returns {boolean} true if the server IP is a domain name, false otherwise.
- */
 /**
  * Checks if the given server IP is a domain name.
  *
  * @param {string} serverIps - The server IP or domain name to check.
  * @returns {boolean} true if the server IP is a domain name, false otherwise.
  */
packages/@webex/plugin-meetings/src/reachability/index.ts (1)

530-530: Apply optional chaining suggestion from static analysis.

-        if (protocolResult && protocolResult.details) {
+        if (protocolResult?.details) {
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 84866df and 86150f5.

📒 Files selected for processing (4)
  • packages/@webex/plugin-meetings/src/reachability/clusterReachability.ts (9 hunks)
  • packages/@webex/plugin-meetings/src/reachability/index.ts (6 hunks)
  • packages/@webex/plugin-meetings/src/reachability/reachability.types.ts (2 hunks)
  • packages/@webex/plugin-meetings/src/reachability/util.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/@webex/plugin-meetings/src/reachability/reachability.types.ts
🧰 Additional context used
🪛 Biome (1.9.4)
packages/@webex/plugin-meetings/src/reachability/index.ts

[error] 530-530: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🔇 Additional comments (9)
packages/@webex/plugin-meetings/src/reachability/util.ts (1)

75-85: LGTM - Clean utility function.

The constructSubnetKey function provides a consistent way to generate unique keys for subnet identification.

packages/@webex/plugin-meetings/src/reachability/index.ts (3)

164-194: LGTM - Proper Map usage in subnet reachability check.

The method correctly uses the new subnets Map structure and properly iterates over values.


464-501: Enhanced result processing with proper domain name segregation.

The method now correctly separates domain names from IP subnet details and processes them appropriately for each protocol.


922-961: Enhanced storage with detailed subnet information.

The method now properly stores subnet details and domain names per protocol, enabling richer reachability reporting.

packages/@webex/plugin-meetings/src/reachability/clusterReachability.ts (5)

58-80: Excellent refactoring to use Map for subnet tracking.

The change from Set to Map with composite keys provides efficient lookups and proper data structure for subnet management.


92-113: Comprehensive result object initialization.

All new fields are properly initialized with appropriate default values.


421-447: Improved ICE candidate processing with port extraction.

The enhanced regex properly handles IPv6 and domain names, and port information is correctly extracted and passed to saveResult.


473-507: Valuable subnet prepopulation for comprehensive tracking.

The prepopulation ensures all subnets are tracked from the start, providing complete visibility into reachability status across all protocols.


197-203: Simplified peer connection cleanup.

The removal of property nullification after closing is a reasonable simplification.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🔭 Outside diff range comments (1)
packages/@webex/plugin-meetings/src/reachability/index.ts (1)

450-511: Method has become overly complex with multiple responsibilities.

The getReachabilityResults method now handles data transformation, domain name segregation, storage operations, and result formatting. This violates the single responsibility principle and makes the code harder to maintain and test.

Consider breaking this method into smaller, focused functions:

  • transformReachabilityResults() - handles data transformation
  • segregateDomainNames() - separates domains from IPs
  • saveProcessedResults() - handles storage operations
♻️ Duplicate comments (1)
packages/@webex/plugin-meetings/src/reachability/index.ts (1)

464-491: Backend schema violation remains unresolved.

The details arrays still contain subnet objects with boolean fields that are not converted to the string format expected by the backend schema. This is the same issue flagged in previous reviews.

The mapTransportResultToBackendDataFormat function is only applied to the top-level protocol result, but the details array objects bypass this conversion, leaving boolean values like reachable: true instead of reachable: "true".

Apply the backend format conversion to each subnet object in the details arrays:

             details: details.filter((subnet) => !isDomainName(subnet.serverIps)),
+            details: details
+              .filter((subnet) => !isDomainName(subnet.serverIps))
+              .map((subnet) => this.mapTransportResultToBackendDataFormat(subnet)),
🧹 Nitpick comments (2)
packages/@webex/plugin-meetings/src/reachability/index.ts (2)

530-530: Use optional chaining for safer property access.

The condition protocolResult && protocolResult.details can be simplified using optional chaining for better readability and safety.

-        if (protocolResult && protocolResult.details) {
+        if (protocolResult?.details) {

557-565: Improve minLatency calculation logic.

The nested array operations and filtering could be simplified for better readability and performance.

-            const latencies = filteredDetails
-              .map((subnet) => Math.min(...(subnet.latencies || [])))
-              .filter((latency) => !Number.isNaN(latency)); // Use Number.isNaN instead of isNaN
-
-            if (latencies.length > 0) {
-              protocolResult.minLatency = Math.min(...latencies);
-            } else {
-              protocolResult.minLatency = undefined; // No valid latencies
-            }
+            const validLatencies = filteredDetails
+              .flatMap((subnet) => subnet.latencies || [])
+              .filter((latency) => !Number.isNaN(latency));
+            
+            protocolResult.minLatency = validLatencies.length > 0 
+              ? Math.min(...validLatencies) 
+              : undefined;
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 86150f5 and 9c5700c.

📒 Files selected for processing (2)
  • packages/@webex/plugin-meetings/src/reachability/index.ts (6 hunks)
  • packages/@webex/plugin-meetings/src/reachability/util.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/@webex/plugin-meetings/src/reachability/util.ts
🧰 Additional context used
🪛 Biome (1.9.4)
packages/@webex/plugin-meetings/src/reachability/index.ts

[error] 530-530: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

🔇 Additional comments (3)
packages/@webex/plugin-meetings/src/reachability/index.ts (3)

15-15: LGTM!

The import of utility functions is correctly implemented and aligns with their usage throughout the file.


164-193: LGTM!

The method correctly adapts to the new subnet data structure using cluster.subnets.values() and properly extracts the server IP from subnet.serverIps. The JSON logging provides better debugging information.


922-948: ```shell
#!/bin/bash

Locate definition and usage of clusterReachability in reachability/index.ts

rg "clusterReachability" -n packages/@webex/plugin-meetings/src/reachability/index.ts


</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

this.pc = this.createPeerConnection(clusterInfo);
this.clusterInfo = clusterInfo;
this.isVideoMesh = !!clusterInfo.isVideoMesh;
this.numUdpUrls = Array.isArray(clusterInfo.udp) ? clusterInfo.udp.length : 0;
Copy link
Collaborator

Choose a reason for hiding this comment

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

why do we need the isArray() checks? the type of clusterInfo is defined saying that these will always be arrays

}

// Ensure upsert uses raw host string even if not a literal IP.
private upsertDetailByHost(protocol: 'udp' | 'tcp' | 'xtls', host: string, port: number) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

the name is a bit misleading - "upsert" means insert or update, but this function is not doing any update, I can only see it doing an insert if the entry doesn't exist.

const {details} = this.result[protocol];
let existing = details.find((subnet) => subnet.serverIp === host && subnet.port === port);
if (!existing) {
existing = {
Copy link
Collaborator

Choose a reason for hiding this comment

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

would be better for existing to be a const and not re-use it here as we're adding a new entry, so it should be const newEntry here, not existing as it's not an existing entry, or you could just call push({serverIpd: host, ....}) directly without having any variable

if (match) {
return {host: match[1], port: Number(match[2])};
}
const stripped = rawUrl.replace(/^stun:/i, '').split(';')[0];
Copy link
Collaborator

Choose a reason for hiding this comment

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

what's the purpose of the code block lines 151-157?

};

// Regular expression to match STUN and TURN server URLs
export const STUN_GENERIC_URL_REGEX = /^stun:([^:;]+):(\d+)(?:;.*)?$/i;
Copy link
Collaborator

Choose a reason for hiding this comment

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

why do we have semicolons in these regexes?


let subnet = result.details.find((s) => s.serverIp === serverIp && s.port === port);
if (!subnet) {
if (protocol === 'udp' && this.perUrlProtocols.size === 0) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

why do we need any special handling for "per url" mode in saveResult()? Shouldn't it just work fine with same code always? if per url mode creates more results, then we will save more results, each with different serverIp and port, so they shouldn't conflict with anything. We only need to make sure that latencyInMilliseconds stores the first latency (the smallest one) and this was already being done in saveResult() anyway, so why do you need any more changes?


private perUrlProtocols: Set<'udp' | 'tcp' | 'xtls'> = new Set();

public enablePerUrlMode(protocols: ('udp' | 'tcp' | 'xtls')[] = ['udp']) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

there is a lot of hardcoded logic that makes the "per url" mode work in a very specific way only for UDP, so I don't think it makes much sense to try to be generic here with a set of protocols as only "udp" matters here and if someone called enablePerUrlMode() with other protocols, then nothing would happen, so maybe better to have a simple flag or even nothing at all and just use the config.meetings.reachabilityEnablePerUrlForUdp

const perUrlUdpOnly = this.perUrlProtocols.size === 1 && this.perUrlProtocols.has('udp');

// Per‑URL UDP multi‑PC mode
if (perUrlUdpOnly) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

there is awful lot of code duplication introduced in this PR, the whole flow of what we do with an RTCPeerConnection for a single UDP url or for a list of TCP, xTLS urls or for a combination of both (like in "legacy" mode) should be really the same and should be managed by a single piece of code, preferably all contained within a class that would then be instantiated multiple times from ClusterReachability: in legacy mode it would be instantiated and run only once, in the new mode it would be instantiated and run for each UDP url and once for xtls and tcp urls together.

isFirstResult[protocol] = false;
const clusterChecker = new ClusterReachability(key, cluster);
if (perUrlUdpMode) {
clusterChecker.enablePerUrlMode(['udp']);
Copy link
Collaborator

Choose a reason for hiding this comment

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

would be better if this was passed in as a ClusterReachability constructor parameter

file: 'reachability',
function: 'resultReady event handler',
},
'reachability:firstResultAvailable',
Copy link
Collaborator

Choose a reason for hiding this comment

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

what happened to this code? why is it deleted? why are we not sending "reachability:firstResultAvailable" event anymore?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

validated If the pull request is validated for automation.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants