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

grpc / grpc-java / #20046

03 Nov 2025 09:31PM UTC coverage: 88.53% (+0.008%) from 88.522%
#20046

push

github

ejona86
xds: Avoid default bootstrap when global override in XdsNameResolver

This fixes a regression with an internal API from 27d150890 where
overridding the global bootstrap didn't impact parsing the default
bootstrap. So if no global bootstrap was available XdsNameResolver would
fail to start even though an override was in place in
SharedXdsClientPoolProvider. Instead of dealing with the override in
SharedXdsClientPoolProvider, do it in GrpcBootstrapperImpl so
XdsNameResolver is ignorant of the source of the default bootstrap.

We want all of this to go away in favor of XDS_CLIENT_SUPPLIER
injection, but there needs to be some overlap for migration.

cl/826085025

34963 of 39493 relevant lines covered (88.53%)

0.89 hits per line

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

95.24
/../xds/src/main/java/io/grpc/xds/SharedXdsClientPoolProvider.java
1
/*
2
 * Copyright 2020 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.xds;
18

19
import static com.google.common.base.Preconditions.checkNotNull;
20

21
import com.google.common.annotations.VisibleForTesting;
22
import com.google.common.collect.ImmutableList;
23
import com.google.errorprone.annotations.concurrent.GuardedBy;
24
import io.grpc.CallCredentials;
25
import io.grpc.MetricRecorder;
26
import io.grpc.internal.ExponentialBackoffPolicy;
27
import io.grpc.internal.GrpcUtil;
28
import io.grpc.internal.ObjectPool;
29
import io.grpc.internal.SharedResourceHolder;
30
import io.grpc.internal.TimeProvider;
31
import io.grpc.xds.client.Bootstrapper;
32
import io.grpc.xds.client.Bootstrapper.BootstrapInfo;
33
import io.grpc.xds.client.XdsClient;
34
import io.grpc.xds.client.XdsClientImpl;
35
import io.grpc.xds.client.XdsInitializationException;
36
import io.grpc.xds.internal.security.TlsContextManagerImpl;
37
import java.util.Map;
38
import java.util.concurrent.ConcurrentHashMap;
39
import java.util.concurrent.ScheduledExecutorService;
40
import java.util.logging.Level;
41
import java.util.logging.Logger;
42
import javax.annotation.Nullable;
43
import javax.annotation.concurrent.ThreadSafe;
44

45
/**
46
 * The global factory for creating a singleton {@link XdsClient} instance to be used by all gRPC
47
 * clients in the process.
48
 */
49
@ThreadSafe
50
final class SharedXdsClientPoolProvider implements XdsClientPoolFactory {
51
  private static final boolean LOG_XDS_NODE_ID = Boolean.parseBoolean(
1✔
52
      System.getenv("GRPC_LOG_XDS_NODE_ID"));
1✔
53
  private static final Logger log = Logger.getLogger(XdsClientImpl.class.getName());
1✔
54
  private static final ExponentialBackoffPolicy.Provider BACKOFF_POLICY_PROVIDER =
1✔
55
      new ExponentialBackoffPolicy.Provider();
56

57
  @Nullable
58
  private final Bootstrapper bootstrapper;
59
  private final Object lock = new Object();
1✔
60
  private final Map<String, ObjectPool<XdsClient>> targetToXdsClientMap = new ConcurrentHashMap<>();
1✔
61

62
  SharedXdsClientPoolProvider() {
63
    this(null);
1✔
64
  }
1✔
65

66
  @VisibleForTesting
67
  SharedXdsClientPoolProvider(@Nullable Bootstrapper bootstrapper) {
1✔
68
    this.bootstrapper = bootstrapper;
1✔
69
  }
1✔
70

71
  static SharedXdsClientPoolProvider getDefaultProvider() {
72
    return SharedXdsClientPoolProviderHolder.instance;
1✔
73
  }
74

75
  @Override
76
  @Nullable
77
  public ObjectPool<XdsClient> get(String target) {
78
    return targetToXdsClientMap.get(target);
1✔
79
  }
80

81
  @Deprecated
82
  public ObjectPool<XdsClient> getOrCreate(
83
      String target, MetricRecorder metricRecorder, CallCredentials transportCallCredentials)
84
      throws XdsInitializationException {
85
    BootstrapInfo bootstrapInfo;
86
    if (bootstrapper != null) {
1✔
87
      bootstrapInfo = bootstrapper.bootstrap();
1✔
88
    } else {
89
      bootstrapInfo = GrpcBootstrapperImpl.defaultBootstrap();
×
90
    }
91
    return getOrCreate(target, bootstrapInfo, metricRecorder, transportCallCredentials);
1✔
92
  }
93

94
  @Override
95
  public ObjectPool<XdsClient> getOrCreate(
96
      String target, BootstrapInfo bootstrapInfo, MetricRecorder metricRecorder) {
97
    return getOrCreate(target, bootstrapInfo, metricRecorder, null);
1✔
98
  }
99

100
  public ObjectPool<XdsClient> getOrCreate(
101
      String target,
102
      BootstrapInfo bootstrapInfo,
103
      MetricRecorder metricRecorder,
104
      CallCredentials transportCallCredentials) {
105
    ObjectPool<XdsClient> ref = targetToXdsClientMap.get(target);
1✔
106
    if (ref == null) {
1✔
107
      synchronized (lock) {
1✔
108
        ref = targetToXdsClientMap.get(target);
1✔
109
        if (ref == null) {
1✔
110
          ref =
1✔
111
              new RefCountedXdsClientObjectPool(
112
                  bootstrapInfo, target, metricRecorder, transportCallCredentials);
113
          targetToXdsClientMap.put(target, ref);
1✔
114
        }
115
      }
1✔
116
    }
117
    return ref;
1✔
118
  }
119

120
  @Override
121
  public ImmutableList<String> getTargets() {
122
    return ImmutableList.copyOf(targetToXdsClientMap.keySet());
1✔
123
  }
124

125
  private static class SharedXdsClientPoolProviderHolder {
126
    private static final SharedXdsClientPoolProvider instance = new SharedXdsClientPoolProvider();
1✔
127
  }
128

129
  @ThreadSafe
130
  @VisibleForTesting
131
  class RefCountedXdsClientObjectPool implements ObjectPool<XdsClient> {
132

133
    private final BootstrapInfo bootstrapInfo;
134
    private final String target; // The target associated with the xDS client.
135
    private final MetricRecorder metricRecorder;
136
    private final CallCredentials transportCallCredentials;
137
    private final Object lock = new Object();
1✔
138
    @GuardedBy("lock")
139
    private ScheduledExecutorService scheduler;
140
    @GuardedBy("lock")
141
    private XdsClient xdsClient;
142
    @GuardedBy("lock")
143
    private int refCount;
144
    @GuardedBy("lock")
145
    private XdsClientMetricReporterImpl metricReporter;
146

147
    @VisibleForTesting
148
    RefCountedXdsClientObjectPool(
149
        BootstrapInfo bootstrapInfo, String target, MetricRecorder metricRecorder) {
150
      this(bootstrapInfo, target, metricRecorder, null);
1✔
151
    }
1✔
152

153
    @VisibleForTesting
154
    RefCountedXdsClientObjectPool(
155
        BootstrapInfo bootstrapInfo,
156
        String target,
157
        MetricRecorder metricRecorder,
158
        CallCredentials transportCallCredentials) {
1✔
159
      this.bootstrapInfo = checkNotNull(bootstrapInfo, "bootstrapInfo");
1✔
160
      this.target = target;
1✔
161
      this.metricRecorder = checkNotNull(metricRecorder, "metricRecorder");
1✔
162
      this.transportCallCredentials = transportCallCredentials;
1✔
163
    }
1✔
164

165
    @Override
166
    public XdsClient getObject() {
167
      synchronized (lock) {
1✔
168
        if (refCount == 0) {
1✔
169
          if (LOG_XDS_NODE_ID) {
1✔
170
            log.log(Level.INFO, "xDS node ID: {0}", bootstrapInfo.node().getId());
×
171
          }
172
          scheduler = SharedResourceHolder.get(GrpcUtil.TIMER_SERVICE);
1✔
173
          metricReporter = new XdsClientMetricReporterImpl(metricRecorder, target);
1✔
174
          GrpcXdsTransportFactory xdsTransportFactory =
1✔
175
              new GrpcXdsTransportFactory(transportCallCredentials);
176
          xdsClient =
1✔
177
              new XdsClientImpl(
178
                  xdsTransportFactory,
179
                  bootstrapInfo,
180
                  scheduler,
181
                  BACKOFF_POLICY_PROVIDER,
1✔
182
                  GrpcUtil.STOPWATCH_SUPPLIER,
183
                  TimeProvider.SYSTEM_TIME_PROVIDER,
184
                  MessagePrinter.INSTANCE,
185
                  new TlsContextManagerImpl(bootstrapInfo),
186
                  metricReporter);
187
          metricReporter.setXdsClient(xdsClient);
1✔
188
        }
189
        refCount++;
1✔
190
        return xdsClient;
1✔
191
      }
192
    }
193

194
    @Override
195
    public XdsClient returnObject(Object object) {
196
      synchronized (lock) {
1✔
197
        refCount--;
1✔
198
        if (refCount == 0) {
1✔
199
          xdsClient.shutdown();
1✔
200
          xdsClient = null;
1✔
201
          metricReporter.close();
1✔
202
          metricReporter = null;
1✔
203
          targetToXdsClientMap.remove(target);
1✔
204
          scheduler = SharedResourceHolder.release(GrpcUtil.TIMER_SERVICE, scheduler);
1✔
205
        }
206
        return null;
1✔
207
      }
208
    }
209

210
    @VisibleForTesting
211
    @Nullable
212
    XdsClient getXdsClientForTest() {
213
      synchronized (lock) {
1✔
214
        return xdsClient;
1✔
215
      }
216
    }
217

218
    public String getTarget() {
219
      return target;
×
220
    }
221
  }
222

223
}
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