Skip to content

feat: add multi-address load balancing and failover support#106

Draft
qevolg wants to merge 2 commits intomainfrom
feat/main/failover
Draft

feat: add multi-address load balancing and failover support#106
qevolg wants to merge 2 commits intomainfrom
feat/main/failover

Conversation

@qevolg
Copy link
Contributor

@qevolg qevolg commented Mar 10, 2026

  • Add multi-address URL parser (common/urlParser.ts) supporting IPv4/IPv6 Format: ws://user:pass@host1:port1,host2:port2,[::1]:port3?retries=5
  • Add ConnectionManager (client/wsConnectionManager.ts) with:
    • Random host selection for initial connection
    • Failover with round-robin across hosts
    • Exponential backoff retry (retries, retry_backoff_ms, retry_backoff_max_ms)
    • Inflight request tracking and resend on reconnect
  • Enhance WebSocketConnector with onClose callback for fault detection
  • Refactor WsClient to use ConnectionManager for multi-address connections
  • Update WSConfig to carry ParsedMultiAddress
  • Update getUrl() to parse multi-address URLs
  • Update TmqConfig to support multi-address URLs
  • Add unit tests for URL parser and ConnectionManager

Description

Issue(s)

  • Close/close/Fix/fix/Resolve/resolve: Issue Link

Checklist

Please check the items in the checklist if applicable.

  • Is the user manual updated?
  • Are the test cases passed and automated?
  • Is there no significant decrease in test coverage?

- Add multi-address URL parser (common/urlParser.ts) supporting IPv4/IPv6
  Format: ws://user:pass@host1:port1,host2:port2,[::1]:port3?retries=5
- Add ConnectionManager (client/wsConnectionManager.ts) with:
  - Random host selection for initial connection
  - Failover with round-robin across hosts
  - Exponential backoff retry (retries, retry_backoff_ms, retry_backoff_max_ms)
  - Inflight request tracking and resend on reconnect
- Enhance WebSocketConnector with onClose callback for fault detection
- Refactor WsClient to use ConnectionManager for multi-address connections
- Update WSConfig to carry ParsedMultiAddress
- Update getUrl() to parse multi-address URLs
- Update TmqConfig to support multi-address URLs
- Add unit tests for URL parser and ConnectionManager

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@gemini-code-assist
Copy link

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the WebSocket client's robustness and availability by introducing comprehensive multi-address support with automatic load balancing and failover capabilities. It allows the client to connect to multiple server addresses, intelligently select an initial connection, and seamlessly switch to an alternative host with retry logic if the current connection fails. This change improves the client's resilience against network issues or server outages, ensuring a more stable and continuous operation.

Highlights

  • Multi-Address URL Parsing: Introduced a new utility (common/urlParser.ts) to parse multi-address WebSocket URLs, supporting IPv4, IPv6, hostnames, user credentials, and query parameters for failover configuration (retries, backoff).
  • Connection Management with Failover: Added a ConnectionManager (client/wsConnectionManager.ts) to handle multi-address connections, including random host selection for initial connection, round-robin failover, exponential backoff retries, and tracking/resending of inflight requests upon reconnection.
  • Enhanced WebSocketClient: Refactored WsClient (client/wsClient.ts) to integrate with the new ConnectionManager, enabling it to leverage multi-address capabilities, automatic failover, and inflight request management for all communication methods (exec, sendMsg, etc.).
  • Configurability for Multi-Address: Updated WSConfig and TmqConfig to store the parsed multi-address information, and modified the getUrl utility to perform the multi-address parsing and configuration.
  • WebSocketConnector Enhancements: Added an onClose callback mechanism to WebSocketConnector to allow external components (like ConnectionManager) to react to connection closures, facilitating failover detection.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • nodejs/src/client/wsClient.ts
    • Imported ConnectionManager and urlParser utilities.
    • Added private members _connMgr, _parsedMultiAddress, _database, and _inflightIdCounter to manage multi-address connections and inflight requests.
    • Modified the constructor to accept parsedMultiAddress and initialize ConnectionManager if provided.
    • Implemented _nextInflightId to generate unique IDs for inflight requests.
    • Added _ensureConnector to provide an active WebSocket connector, handling failover via ConnectionManager if necessary.
    • Introduced _sendConnMsg to handle connection message sending and re-authentication after reconnects.
    • Updated connect method to use ConnectionManager for multi-address connections, falling back to the original single-URL logic.
    • Refactored execNoResp, exec, sendBinaryMsg, sendMsg, freeResult, and version methods to utilize _ensureConnector and track/complete inflight requests via ConnectionManager.
    • Modified getState to report connection status from ConnectionManager when active.
    • Updated ready method to trigger ConnectionManager reconnect if not connected.
    • Modified close method to delegate connection closing to ConnectionManager if present.
  • nodejs/src/client/wsConnectionManager.ts
    • Added a new file defining the ConnectionManager class.
    • Implemented InflightRequest interface to track pending requests.
    • The ConnectionManager constructor initializes with parsed multi-address data and a random starting host index.
    • Provided connect method for initial connection using a random host.
    • Included getConnector and isConnected methods for current connection status.
    • Implemented reconnect for failover logic, attempting to connect to hosts with exponential backoff and round-robin strategy.
    • Added trackRequest and completeRequest to manage inflight requests.
    • Provided getInflightCount for diagnostic purposes.
    • Implemented close to shut down the manager and clear inflight requests.
    • Included getCurrentUrl and getParsed to access connection details.
    • Developed private methods _tryConnectWithRetries, _createConnector, _setupCloseHandler, _resendInflightRequests, and _sleep to support failover and request resending.
  • nodejs/src/client/wsConnector.ts
    • Added a private _onCloseCallbacks array to store functions to be called on connection close.
    • Modified the onclose event handler to invoke registered _onCloseCallbacks.
    • Added a public onClose method to register new close callbacks.
    • Introduced sendBinaryMsgRaw to send binary data without callback registration, used for resending inflight requests.
  • nodejs/src/common/config.ts
    • Imported ParsedMultiAddress interface.
    • Added a private _parsedMultiAddress member to WSConfig.
    • Included getParsedMultiAddress and setParsedMultiAddress methods to manage the parsed multi-address information.
  • nodejs/src/common/urlParser.ts
    • Added a new file defining HostPort and ParsedMultiAddress interfaces.
    • Implemented parseMultiAddressUrl to parse complex URL strings with multiple hosts, user credentials, and failover parameters (retries, backoff).
    • Provided buildUrlForHost to construct a standard URL object for a specific host from the parsed multi-address data.
    • Developed helper functions findUserInfoEnd, findHostEnd, parseHosts, splitHostSegments, and extractIntParam for robust URL parsing.
  • nodejs/src/common/utils.ts
    • Imported parseMultiAddressUrl and buildUrlForHost from the new urlParser module.
    • Refactored getUrl to first parse the raw URL using parseMultiAddressUrl and store the result in WSConfig.
    • Updated getUrl to apply user, password, token, timezone, and bearer token overrides to the parsed multi-address data.
    • Ensured the pathname is set to '/ws' and returned a URL built from the first host for backward compatibility.
  • nodejs/src/sql/wsSql.ts
    • Modified the WsSql constructor to pass the ParsedMultiAddress obtained from wsConfig.getParsedMultiAddress() to the WsClient constructor.
  • nodejs/src/tmq/config.ts
    • Imported parseMultiAddressUrl, buildUrlForHost, and ParsedMultiAddress.
    • Added a parsedMultiAddress member to TmqConfig.
    • Modified the constructor to parse the raw URL using parseMultiAddressUrl and store the result in this.parsedMultiAddress.
    • Updated URL construction to use buildUrlForHost for compatibility and apply user/password/token overrides to the parsed multi-address.
  • nodejs/src/tmq/wsTmq.ts
    • Modified the WsConsumer constructor to pass this._wsConfig.parsedMultiAddress to the WsClient constructor.
    • Updated the connect method to pass this._wsConfig.parsedMultiAddress to the WsClient constructor when creating the wsSql client.
  • nodejs/test/connectionManager.test.ts
    • Added a new test file for ConnectionManager.
    • Included tests for constructor initialization, getCurrentUrl, trackRequest, completeRequest, and close functionality.
  • nodejs/test/urlParser.test.ts
    • Added a new test file for urlParser.
    • Included comprehensive tests for parseMultiAddressUrl covering single/multiple hosts, IPv6, mixed addresses, WSS scheme, default ports, failover parameters, pathnames, query parameters, and error handling.
    • Added tests for buildUrlForHost to verify correct URL construction for IPv4 and IPv6 hosts, and pathname preservation.
Activity
  • The pull request introduces new features for multi-address load balancing and failover support.
  • New files wsConnectionManager.ts, urlParser.ts, connectionManager.test.ts, and urlParser.test.ts were added.
  • Existing core client and configuration files (wsClient.ts, wsConnector.ts, config.ts, utils.ts, wsSql.ts, tmq/config.ts, tmq/wsTmq.ts) were modified to integrate the new functionality.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces significant and well-structured functionality for multi-address load balancing and failover, effectively encapsulating complex logic with a robust ConnectionManager and custom URL parser. However, a high-severity SQL injection vulnerability was identified where the database name extracted from the connection URL is used unsafely in internal SQL queries. It is recommended to validate the database name and use proper escaping or parameterized queries. Additionally, there are a few suggestions for code cleanup and stricter error handling to further improve the implementation.

Comment on lines +34 to 36
if (parsed.pathname && parsed.pathname !== "/") {
wsConfig.setDb(parsed.pathname.slice(1));
}

Choose a reason for hiding this comment

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

security-high high

The database name is extracted from the URL pathname and stored in the WSConfig object without any validation or sanitization. This database name is later used in nodejs/src/sql/wsSql.ts (e.g., in stmtInit and open methods) to construct SQL queries by direct string concatenation. An attacker who can control the connection URL can inject malicious SQL commands, potentially leading to unauthorized data access or manipulation on the TDengine server. For example, a database name like mydb' OR '1'='1 could bypass intended filters in metadata queries.

Comment on lines +211 to +216
if (isNaN(port)) {
// Might be hostname without port that contains something odd, treat whole as host
results.push({ host: trimmed, port: DEFAULT_PORT });
} else {
results.push({ host, port });
}

Choose a reason for hiding this comment

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

high

The current logic for handling an invalid port for a hostname or IPv4 address is to treat the entire segment (e.g., 1.2.3.4:abc) as the hostname and use a default port. This can hide typos in the URL and lead to confusing connection errors. For consistency with the IPv6 parsing logic, which throws an error for an invalid port, and to provide clearer feedback to the user, it would be better to throw an error here if the port is not a valid number.

                if (isNaN(port)) {
                    throw new TDWebSocketClientError(
                        ErrorCode.ERR_INVALID_URL,
                        `Invalid port in: ${trimmed}`
                    );
                } else {
                    results.push({ host, port });
                }

Comment on lines +39 to +40
private _isReconnecting: boolean = false;
private _reconnectPromise: Promise<WebSocketConnector> | null = null;

Choose a reason for hiding this comment

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

medium

These private properties, _isReconnecting and _reconnectPromise, are declared but never used. They appear to be remnants of a previous implementation and can be safely removed to improve code clarity. The _reconnectMutex already handles the concurrency control for reconnections.

Comment on lines +65 to +66
// ???
// 连接建立成功后,发送连接成功的消息,触发后续的事件回调

Choose a reason for hiding this comment

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

medium

These comments, including // ???, seem to be temporary developer notes. They should be removed from the final code to improve readability and maintainability. Similar notes are present on lines 174-180 and 199-201 and should also be cleaned up.

…etConnector

1. Simplify urlParser: replace bracket-aware scanning functions with
   straightforward @/slash/query position-based host:port extraction.
   All existing test cases still pass.

2. Refactor WebSocketConnector: change constructor from URL to string,
   parse multi-address internally via parseMultiAddressUrl(). Merge
   ConnectionManager failover (round-robin + exponential backoff),
   reconnect, and inflight request tracking directly into WebSocketConnector.

3. Simplify WsClient: remove ConnectionManager dependency and dual code
   paths (multi-address vs single-address). WsClient now passes raw URL
   string to WebSocketConnector, which handles everything uniformly.

4. Update consumers: wsSql, wsTmq/TmqConfig, wsConnectorPool, config,
   utils all updated to use string URLs instead of URL objects.

5. Delete wsConnectionManager.ts — all its behavior now lives in
   WebSocketConnector.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant