Skip to content

[client] Revert migrate deprecated grpc client code#4805

Merged
lixmal merged 1 commit intomainfrom
revert-grpc
Nov 18, 2025
Merged

[client] Revert migrate deprecated grpc client code#4805
lixmal merged 1 commit intomainfrom
revert-grpc

Conversation

@lixmal
Copy link
Copy Markdown
Collaborator

@lixmal lixmal commented Nov 18, 2025

This reverts commit 43c9a51.

Describe your changes

Issue ticket number and link

Stack

Checklist

  • Is it a bug fix
  • Is a typo/documentation fix
  • Is a feature enhancement
  • It is a refactor
  • Created tests that fail without the change (if possible)

By submitting this pull request, you confirm that you have read and agree to the terms of the Contributor License Agreement.

Documentation

Select exactly one:

  • I added/updated documentation for this change
  • Documentation is not needed for this change (explain why)

Docs PR URL (required if "docs added" is checked)

Paste the PR link from https://github.com/netbirdio/docs here:

https://github.com/netbirdio/docs/pull/__

Summary by CodeRabbit

  • Refactor
    • Streamlined gRPC connection initialization with an optimized dial timeout mechanism for improved connection reliability and handling.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Nov 18, 2025

Walkthrough

The gRPC client dialer is refactored to simplify connection initialization by replacing grpc.NewClient with grpc.DialContext using a 30-second timeout and blocking option, while removing a separate connection readiness check function and associated exported error variable.

Changes

Cohort / File(s) Summary
gRPC Dialer Refactoring
client/grpc/dialer.go
Removed imports (errors, connectivity); removed exported variable ErrConnectionShutdown and function waitForConnectionReady; replaced connection creation from grpc.NewClient to grpc.DialContext with 30s timeout and grpc.WithBlock() option; simplified error handling; removed post-dial readiness validation and cleanup logic.

Sequence Diagram

sequenceDiagram
    participant Caller
    participant Dialer
    participant gRPC as gRPC Lib

    rect rgb(200, 220, 255)
    Note over Caller,gRPC: Old Flow (Removed)
    Caller->>Dialer: Create connection
    Dialer->>gRPC: NewClient()
    gRPC-->>Dialer: conn (possibly not ready)
    Dialer->>Dialer: waitForConnectionReady()
    Dialer->>gRPC: Poll connection state
    gRPC-->>Dialer: Ready
    Dialer-->>Caller: conn
    end

    rect rgb(220, 240, 220)
    Note over Caller,gRPC: New Flow (Simplified)
    Caller->>Dialer: Create connection
    Dialer->>Dialer: Create 30s timeout context
    Dialer->>gRPC: DialContext(ctx, WithBlock())
    gRPC-->>Dialer: conn (blocks until ready)
    Dialer-->>Caller: conn
    end
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

  • Public API removal: Verify that ErrConnectionShutdown and waitForConnectionReady are not used by external callers or other packages.
  • Dial flow change: Confirm that replacing grpc.NewClient with grpc.DialContext(..., grpc.WithBlock()) achieves the same readiness guarantee with equivalent timeout semantics.
  • Error handling simplification: Ensure the new error wrapping format ("dial context: %w") is sufficient for debugging and doesn't lose context.

Poem

🐰 The dial now blocks with grace,
Thirty seconds to find its place,
No more waiting checks between,
Cleanest connection ever seen!
Thump thump — one path, supreme! 🎯

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description check ⚠️ Warning The description is largely incomplete with empty 'Describe your changes', 'Issue ticket number and link', and 'Stack' sections. Only the revert commit hash is provided without substantive explanation of why the revert is needed. Fill in the 'Describe your changes' section with details about why the previous migration was problematic, and provide the related issue ticket number and link for context.
Docstring Coverage ⚠️ Warning Docstring coverage is 62.50% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly indicates this PR reverts deprecated gRPC client code changes, directly matching the changeset which removes public exports and reverts to dialContext approach.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch revert-grpc

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
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: 1

🧹 Nitpick comments (2)
shared/management/client/grpc.go (1)

58-60: Consider preserving error context through wrapping.

The change removes error wrapping in favor of logging, which loses contextual information about where the error occurred. While this simplifies the error handling, it makes debugging connection issues more difficult as the error no longer indicates it came from CreateConnection.

Consider maintaining error context while still logging:

 		var err error
 		conn, err = nbgrpc.CreateConnection(ctx, addr, tlsEnabled, wsproxy.ManagementComponent)
 		if err != nil {
-			log.Printf("createConnection error: %v", err)
-			return err
+			log.Printf("createConnection error: %v", err)
+			return fmt.Errorf("create connection: %w", err)
 		}
 		return nil
shared/signal/client/grpc.go (1)

63-65: Consider preserving error context through wrapping.

Similar to the management client, this change removes error wrapping which loses contextual information about where the error occurred. This makes debugging signal connection issues more difficult.

Consider maintaining error context while still logging:

 		var err error
 		conn, err = nbgrpc.CreateConnection(ctx, addr, tlsEnabled, wsproxy.SignalComponent)
 		if err != nil {
-			log.Printf("createConnection error: %v", err)
-			return err
+			log.Printf("createConnection error: %v", err)
+			return fmt.Errorf("create connection: %w", err)
 		}
 		return nil
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4eeb2d8 and 6ae17c8.

📒 Files selected for processing (8)
  • client/grpc/dialer.go (1 hunks)
  • client/grpc/dialer_generic.go (2 hunks)
  • client/net/conn.go (2 hunks)
  • client/net/dial.go (1 hunks)
  • client/net/dialer_dial.go (1 hunks)
  • client/net/listener_listen.go (2 hunks)
  • shared/management/client/grpc.go (1 hunks)
  • shared/signal/client/grpc.go (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
client/grpc/dialer_generic.go (1)
client/grpc/dialer_js.go (1)
  • WithCustomDialer (11-13)
client/grpc/dialer.go (2)
client/grpc/dialer_generic.go (1)
  • WithCustomDialer (21-44)
client/grpc/dialer_js.go (1)
  • WithCustomDialer (11-13)
client/net/conn.go (1)
client/net/hooks/hooks.go (2)
  • ConnectionID (13-13)
  • GetCloseHooks (82-86)
⏰ 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). (22)
  • GitHub Check: Relay / Unit (386)
  • GitHub Check: Management / Unit (amd64, mysql)
  • GitHub Check: Management / Unit (amd64, sqlite)
  • GitHub Check: Relay / Unit (amd64, -race)
  • GitHub Check: Management / Unit (amd64, postgres)
  • GitHub Check: Signal / Unit (386)
  • GitHub Check: Client (Docker) / Unit
  • GitHub Check: Signal / Unit (amd64)
  • GitHub Check: Client / Unit (386)
  • GitHub Check: Client / Unit (amd64)
  • GitHub Check: Client / Unit
  • GitHub Check: Client / Unit
  • GitHub Check: Linux
  • GitHub Check: Windows
  • GitHub Check: Darwin
  • GitHub Check: release_ui_darwin
  • GitHub Check: release_ui
  • GitHub Check: release
  • GitHub Check: iOS / Build
  • GitHub Check: Android / Build
  • GitHub Check: Client / Unit
  • GitHub Check: JS / Lint
🔇 Additional comments (4)
client/net/dial.go (1)

77-77: LGTM: Formatting change.

This blank line improves code readability with no functional impact.

client/net/dialer_dial.go (1)

67-67: LGTM: Error message clarification.

The addition of "failed to" improves error message clarity.

client/grpc/dialer.go (1)

45-62: Confirm the justification for reverting the grpc.NewClient migration.

The review comment's concerns are factually valid: the code deliberately uses the deprecated grpc.DialContext API with blocking behavior via grpc.WithBlock(). Git history confirms this is commit 6ae17c8 which reverts commit 43c9a51 ("[client] Migrate deprecated grpc client code")—this is an intentional reversion.

The deprecated API concerns remain valid:

  • DialContext is the older, deprecated API; NewClient is recommended
  • WithBlock() enforces synchronous connection establishment, changing from lazy to blocking semantics
  • DialContext uses legacy "passthrough" resolver while NewClient uses "dns" resolver

However, since this reversion is recent and deliberate, the key missing information is:

  • Why was the migration to grpc.NewClient (PR #4687) reverted instead of fixing any issues it introduced?
  • What problems did the migration cause that required reverting?

Verify and document the rationale for this intentional reversion before merging.

client/grpc/dialer_generic.go (1)

21-43: No action required—parameters are intentionally unused due to platform-specific implementation.

The function uses Go's build tags (//go:build !js) for conditional compilation. The dialer_generic.go implementation is compiled for non-JS platforms where the parameters are unused, while dialer_js.go uses both parameters for WebSocket proxy configuration. The identical function signature across both implementations ensures caller compatibility. This is intentional and correct.

Comment thread client/net/conn.go Outdated
Comment on lines +20 to +21
// Close overrides the net.Conn Close method to execute all registered hooks after closing the connection
// Close overrides the net.Conn Close method to execute all registered hooks before closing the connection.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical: Documentation contradicts implementation.

The comments on lines 21 and 32 state that hooks execute "before closing the connection," but the closeConn implementation on lines 38-49 clearly shows that conn.Close() is called first (line 39), and then the hooks are executed (lines 41-46). The documentation is incorrect and misleading.

Apply this diff to correct the documentation:

-// Close overrides the net.Conn Close method to execute all registered hooks before closing the connection.
+// Close overrides the net.Conn Close method to execute all registered hooks after closing the connection.
 func (c *Conn) Close() error {
 	return closeConn(c.ID, c.Conn)
 }
 
 // TCPConn wraps net.TCPConn to override its Close method to include hook functionality.
 type TCPConn struct {
 	*net.TCPConn
 	ID hooks.ConnectionID
 }
 
-// Close overrides the net.TCPConn Close method to execute all registered hooks before closing the connection.
+// Close overrides the net.TCPConn Close method to execute all registered hooks after closing the connection.
 func (c *TCPConn) Close() error {
 	return closeConn(c.ID, c.TCPConn)
 }

Also applies to: 32-32, 38-49

🤖 Prompt for AI Agents
In client/net/conn.go around lines 20-21, line 32 and lines 38-49, the comments
incorrectly state that registered hooks run "before closing the connection"
while the implementation calls conn.Close() first and then runs the hooks;
update the doc comments at those locations to say that hooks are executed after
the connection is closed (and make the wording consistent across all affected
comment lines), e.g., change "before closing" to "after closing" and adjust the
sentence to match the implementation behavior.

@sonarqubecloud
Copy link
Copy Markdown

Copy link
Copy Markdown
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: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a11ab18 and 16cf98f.

📒 Files selected for processing (1)
  • client/grpc/dialer.go (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
client/grpc/dialer.go (2)
client/grpc/dialer_generic.go (1)
  • WithCustomDialer (21-43)
client/grpc/dialer_js.go (1)
  • WithCustomDialer (11-13)
⏰ 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). (20)
  • GitHub Check: Management / Unit (amd64, mysql)
  • GitHub Check: Relay / Unit (386)
  • GitHub Check: Client / Unit (amd64)
  • GitHub Check: Management / Unit (amd64, postgres)
  • GitHub Check: Relay / Unit (amd64, -race)
  • GitHub Check: Management / Unit (amd64, sqlite)
  • GitHub Check: Signal / Unit (amd64)
  • GitHub Check: Client / Unit (386)
  • GitHub Check: Client (Docker) / Unit
  • GitHub Check: Windows
  • GitHub Check: Darwin
  • GitHub Check: Linux
  • GitHub Check: iOS / Build
  • GitHub Check: Android / Build
  • GitHub Check: Client / Unit
  • GitHub Check: release_ui_darwin
  • GitHub Check: release
  • GitHub Check: Client / Unit
  • GitHub Check: Client / Unit
  • GitHub Check: JS / Lint
🔇 Additional comments (1)
client/grpc/dialer.go (1)

29-65: The review comment is incorrect.

The exported symbols ErrConnectionShutdown and waitForConnectionReady do not exist in the grpc package and are not referenced anywhere in the codebase. The grpc package only exports Backoff, CreateConnection, and WithCustomDialer. No breaking API changes were introduced in this PR.

Likely an incorrect or invalid review comment.

Comment thread client/grpc/dialer.go
Comment on lines +46 to 62
connCtx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()

conn, err := grpc.DialContext(
connCtx,
addr,
transportOption,
WithCustomDialer(tlsEnabled, component),
grpc.WithBlock(),
grpc.WithKeepaliveParams(keepalive.ClientParameters{
Time: 30 * time.Second,
Timeout: 10 * time.Second,
}),
)
if err != nil {
return nil, fmt.Errorf("new client: %w", err)
}

ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()

if err := waitForConnectionReady(ctx, conn); err != nil {
_ = conn.Close()
return nil, err
return nil, fmt.Errorf("dial context: %w", err)
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify: This reverts to deprecated gRPC APIs that should not be used with gRPC v1.73.0.

The code now uses grpc.DialContext and grpc.WithBlock(), both of which were deprecated in gRPC v1.70.0. Since the project uses gRPC v1.73.0, this revert introduces deprecated API usage. The modern approach is grpc.NewClient with explicit connection state management if needed.

Please verify the deprecation status and recommended migration path for these gRPC APIs in version 1.73.0:

Additionally, check if there was a specific bug or issue with the original grpc.NewClient implementation that necessitated this revert:


🏁 Script executed:

#!/bin/bash
# Search for related issues, bug reports, or comments about grpc.NewClient problems
rg -n -C5 'grpc\.NewClient|waitForConnectionReady|ErrConnectionShutdown' --type go

# Check git history for the original migration commit and this revert
git log --oneline --grep="grpc" --all | head -20

# Look for any TODO/FIXME/BUG comments related to gRPC connection handling
rg -n 'TODO|FIXME|BUG|HACK' --type go -g '*grpc*' -C3

Length of output: 3387


🌐 Web query:

grpc.DialContext grpc.WithBlock deprecated gRPC 1.73.0 alternative

💡 Result:

Short answer: stop using grpc.DialContext(..., grpc.WithBlock()) — use grpc.NewClient (introduced v1.63) and, if you must block until the channel is ready, call the ClientConn's Connect + WaitForStateChange / check connectivity state yourself. grpc.NewClient does no I/O by default; Dial/DialContext and the WithBlock/WithTimeout DialOptions are deprecated. [1][2][3]

Example sketch:

  • cc := grpc.NewClient(target, opts...) // no I/O
  • if err := cc.Connect(ctx); err != nil { ... } // triggers connection attempt
  • // optionally wait for Ready via cc.WaitForStateChange / cc.GetState loop

References:

  1. grpc-go package docs (NewClient, DialContext deprecation). [1]
  2. grpc-go anti-patterns / guidance recommending NewClient. [2]
  3. clientconn implementation notes describing DialContext behavior (WithBlock behaviour). [3]

This revert to deprecated gRPC APIs contradicts gRPC v1.73.0 best practices and creates codebase inconsistency.

The grpc.DialContext and grpc.WithBlock() APIs are deprecated in gRPC v1.70.0 and later in favor of grpc.NewClient. Since the project uses gRPC v1.73.0, this change reintroduces deprecated APIs that should not be used.

Additionally, other files in the codebase (flow/client/client.go:68, client/ui/event/event.go:167) continue using the modern grpc.NewClient approach, creating inconsistency.

Required changes:

  • Revert to grpc.NewClient (modern API)
  • If blocking connection state is required, use ClientConn.Connect(ctx) instead of grpc.WithBlock()

@lixmal lixmal merged commit 60f4d5f into main Nov 18, 2025
36 of 37 checks passed
@lixmal lixmal deleted the revert-grpc branch November 18, 2025 11:41
@coderabbitai coderabbitai Bot mentioned this pull request Mar 31, 2026
7 tasks
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.

2 participants