• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

grpc / grpc-java / #20197

16 Mar 2026 05:51PM UTC coverage: 88.694% (+0.005%) from 88.689%
#20197

push

github

web-flow
xds: reuse GrpcXdsTransport and underlying gRPC channel to the same xDS server by ref-counting

This PR implements reusing the gRPC xDS transport (and underlying gRPC
channel) to the same xDS server by ref-counting, which is already
implemented in gRPC C++
([link](https://github.com/grpc/grpc/blob/5a3a5d531/src/core/xds/grpc/xds_transport_grpc.cc#L399-L414))
and gRPC Go
([link](https://github.com/grpc/grpc-go/blob/81c7924ec/internal/xds/clients/grpctransport/grpc_transport.go#L78-L120)).
This optimization is expected to reduce memory footprint of the xDS
management server and xDS enabled clients as channel establishment and
lifecycle management of the connection is expensive.

* Implemented a map to store `GrpcXdsTransport` instances keyed by the
`Bootstrapper.ServerInfo` and each `GrpcXdsTransport` has a ref count.
Note, the map cannot be simply keyed by the xDS server address as the
client could have different channel credentials to the same xDS server,
which should be counted as different transport instances.
* When `GrpcXdsTransportFactory.create()` is called, the existing
transport is reused if it already exists in the map and increment its
ref count, otherwise create a new transport, store it in the map, and
increment its ref count.
* When `GrpcXdsTransport.shutdown()` is called, its ref count is
decremented and the underlying gRPC channel is shut down when its ref
count reaches zero.
* Note this ref-counting of the `GrpcXdsTransport` is different and
orthogonal to the ref-counting of the xDS client keyed by the xDS server
target name to allow for xDS-based fallback per [gRFC
A71](https://github.com/grpc/proposal/blob/master/A71-xds-fallback.md).

Prod risk level: Low
* Reusing the underlying gRPC channel to the xDS server would not affect
the gRPC xDS (ADS/LRS) streams which would be multiplexed on the same
channel, however, this means new xDS (ADS/LRS) streams and RPCs may fail
due ... (continued)

35473 of 39995 relevant lines covered (88.69%)

0.89 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.88
/../netty/src/main/java/io/grpc/netty/ClientTransportLifecycleManager.java
1
/*
2
 * Copyright 2016 The gRPC Authors
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16

17
package io.grpc.netty;
18

19
import com.google.errorprone.annotations.CanIgnoreReturnValue;
20
import io.grpc.Attributes;
21
import io.grpc.Status;
22
import io.grpc.internal.DisconnectError;
23
import io.grpc.internal.ManagedClientTransport;
24

25
/** Maintainer of transport lifecycle status. */
26
final class ClientTransportLifecycleManager {
27
  private final ManagedClientTransport.Listener listener;
28
  private boolean transportReady;
29
  private boolean transportShutdown;
30
  private boolean transportInUse;
31
  /** null iff !transportShutdown. */
32
  private Status shutdownStatus;
33
  /** null iff !transportShutdown. */
34
  private boolean transportTerminated;
35

36
  public ClientTransportLifecycleManager(ManagedClientTransport.Listener listener) {
1✔
37
    this.listener = listener;
1✔
38
  }
1✔
39

40
  public Attributes filterAttributes(Attributes attributes) {
41
    if (transportReady || transportShutdown) {
1✔
42
      return attributes;
×
43
    }
44
    return listener.filterTransport(attributes);
1✔
45
  }
46

47
  public void notifyReady() {
48
    if (transportReady || transportShutdown) {
1✔
49
      return;
×
50
    }
51
    transportReady = true;
1✔
52
    listener.transportReady();
1✔
53
  }
1✔
54

55
  /**
56
   * Marks transport as shutdown, but does not set the error status. This must eventually be
57
   * followed by a call to notifyShutdown.
58
   */
59
  public void notifyGracefulShutdown(Status s, DisconnectError disconnectError) {
60
    if (transportShutdown) {
1✔
61
      return;
1✔
62
    }
63
    transportShutdown = true;
1✔
64
    listener.transportShutdown(s, disconnectError);
1✔
65
  }
1✔
66

67
  /** Returns {@code true} if was the first shutdown. */
68
  @CanIgnoreReturnValue
69
  public boolean notifyShutdown(Status s, DisconnectError disconnectError) {
70
    notifyGracefulShutdown(s, disconnectError);
1✔
71
    if (shutdownStatus != null) {
1✔
72
      return false;
1✔
73
    }
74
    shutdownStatus = s;
1✔
75
    return true;
1✔
76
  }
77

78
  public void notifyInUse(boolean inUse) {
79
    if (inUse == transportInUse) {
1✔
80
      return;
×
81
    }
82
    transportInUse = inUse;
1✔
83
    listener.transportInUse(inUse);
1✔
84
  }
1✔
85

86
  public void notifyTerminated(Status s, DisconnectError disconnectError) {
87
    if (transportTerminated) {
1✔
88
      return;
×
89
    }
90
    transportTerminated = true;
1✔
91
    notifyShutdown(s, disconnectError);
1✔
92
    listener.transportTerminated();
1✔
93
  }
1✔
94

95
  public Status getShutdownStatus() {
96
    return shutdownStatus;
1✔
97
  }
98

99
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc