1
#include "source/extensions/clusters/dns/dns_cluster.h"
2

            
3
// The purpose of these two headers is purely for backward compatibility.
4
// Never create new dependencies to symbols declared in these headers!
5
#include <chrono>
6

            
7
#include "envoy/common/exception.h"
8
#include "envoy/config/cluster/v3/cluster.pb.h"
9
#include "envoy/config/endpoint/v3/endpoint.pb.h"
10
#include "envoy/config/endpoint/v3/endpoint_components.pb.h"
11
#include "envoy/extensions/clusters/dns/v3/dns_cluster.pb.h"
12

            
13
#include "source/common/common/dns_utils.h"
14
#include "source/common/network/dns_resolver/dns_factory_util.h"
15
#include "source/extensions/clusters/common/dns_cluster_backcompat.h"
16
#include "source/extensions/clusters/common/logical_host.h"
17
#include "source/extensions/clusters/logical_dns/logical_dns_cluster.h"
18
#include "source/extensions/clusters/strict_dns/strict_dns_cluster.h"
19

            
20
namespace Envoy {
21
namespace Upstream {
22

            
23
absl::StatusOr<std::pair<ClusterImplBaseSharedPtr, ThreadAwareLoadBalancerPtr>>
24
DnsClusterFactory::createClusterWithConfig(
25
    const envoy::config::cluster::v3::Cluster& cluster,
26
    const envoy::extensions::clusters::dns::v3::DnsCluster& proto_config,
27
140
    Upstream::ClusterFactoryContext& context) {
28
140
  auto dns_resolver_or_error = selectDnsResolver(proto_config.typed_dns_resolver_config(), context);
29

            
30
140
  RETURN_IF_NOT_OK(dns_resolver_or_error.status());
31

            
32
140
  absl::StatusOr<std::unique_ptr<ClusterImplBase>> cluster_or_error;
33

            
34
140
  if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.enable_new_dns_implementation")) {
35
15
    cluster_or_error =
36
15
        DnsClusterImpl::create(cluster, proto_config, context, std::move(*dns_resolver_or_error));
37
125
  } else if (proto_config.all_addresses_in_single_endpoint()) {
38
8
    cluster_or_error = LogicalDnsCluster::create(cluster, proto_config, context,
39
8
                                                 std::move(*dns_resolver_or_error));
40
125
  } else {
41
117
    cluster_or_error = StrictDnsClusterImpl::create(cluster, proto_config, context,
42
117
                                                    std::move(*dns_resolver_or_error));
43
117
  }
44

            
45
140
  RETURN_IF_NOT_OK(cluster_or_error.status());
46
121
  return std::make_pair(ClusterImplBaseSharedPtr(std::move(*cluster_or_error)), nullptr);
47
140
}
48

            
49
REGISTER_FACTORY(DnsClusterFactory, ClusterFactory);
50

            
51
class LegacyDnsClusterFactory : public ClusterFactoryImplBase {
52
public:
53
  LegacyDnsClusterFactory(const std::string& name, bool set_all_addresses_in_single_endpoint)
54
114
      : ClusterFactoryImplBase(name),
55
114
        set_all_addresses_in_single_endpoint_(set_all_addresses_in_single_endpoint) {}
56
  virtual absl::StatusOr<std::pair<ClusterImplBaseSharedPtr, ThreadAwareLoadBalancerPtr>>
57
  createClusterImpl(const envoy::config::cluster::v3::Cluster& cluster,
58
385
                    ClusterFactoryContext& context) override {
59
385
    absl::StatusOr<Network::DnsResolverSharedPtr> dns_resolver_or_error =
60
385
        selectDnsResolver(cluster, context);
61
385
    RETURN_IF_NOT_OK(dns_resolver_or_error.status());
62

            
63
385
    envoy::extensions::clusters::dns::v3::DnsCluster typed_config;
64
385
    createDnsClusterFromLegacyFields(cluster, typed_config);
65

            
66
385
    typed_config.set_all_addresses_in_single_endpoint(set_all_addresses_in_single_endpoint_);
67

            
68
385
    absl::StatusOr<std::unique_ptr<ClusterImplBase>> cluster_or_error;
69

            
70
385
    if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.enable_new_dns_implementation")) {
71
66
      cluster_or_error =
72
66
          DnsClusterImpl::create(cluster, typed_config, context, std::move(*dns_resolver_or_error));
73
319
    } else if (set_all_addresses_in_single_endpoint_) {
74
136
      cluster_or_error = LogicalDnsCluster::create(cluster, typed_config, context,
75
136
                                                   std::move(*dns_resolver_or_error));
76
267
    } else {
77
183
      cluster_or_error = StrictDnsClusterImpl::create(cluster, typed_config, context,
78
183
                                                      std::move(*dns_resolver_or_error));
79
183
    }
80

            
81
385
    RETURN_IF_NOT_OK(cluster_or_error.status());
82
344
    return std::make_pair(ClusterImplBaseSharedPtr(std::move(*cluster_or_error)), nullptr);
83
385
  }
84

            
85
private:
86
  bool set_all_addresses_in_single_endpoint_{false};
87
};
88

            
89
/**
90
 * LogicalDNSFactory: making it back compatible with ClusterFactoryImplBase.
91
 */
92

            
93
class LogicalDNSFactory : public LegacyDnsClusterFactory {
94
public:
95
57
  LogicalDNSFactory() : LegacyDnsClusterFactory("envoy.cluster.logical_dns", true) {}
96
};
97

            
98
REGISTER_FACTORY(LogicalDNSFactory, ClusterFactory);
99

            
100
/**
101
 * StrictDNSFactory: making it back compatible with ClusterFactoryImplBase
102
 */
103

            
104
class StrictDNSFactory : public LegacyDnsClusterFactory {
105
public:
106
57
  StrictDNSFactory() : LegacyDnsClusterFactory("envoy.cluster.strict_dns", false) {}
107
};
108

            
109
REGISTER_FACTORY(StrictDNSFactory, ClusterFactory);
110

            
111
envoy::config::endpoint::v3::ClusterLoadAssignment
112
DnsClusterImpl::extractAndProcessLoadAssignment(const envoy::config::cluster::v3::Cluster& cluster,
113
92
                                                bool all_addresses_in_single_endpoint) {
114
  // In Logical DNS we convert the priority set by the configuration back to zero.
115
  // This helps ensure that we don't blow up later when using zone aware routing,
116
  // as it only supports load assignments with priority 0.
117
  //
118
  // Since Logical DNS is limited to exactly one host declared per load_assignment
119
  // (enforced in DnsClusterImpl::DnsClusterImpl), we can safely just rewrite the priority
120
  // to zero.
121
92
  if (all_addresses_in_single_endpoint) {
122
24
    envoy::config::endpoint::v3::ClusterLoadAssignment converted;
123
24
    converted.MergeFrom(cluster.load_assignment());
124
25
    for (auto& endpoint : *converted.mutable_endpoints()) {
125
25
      endpoint.set_priority(0);
126
25
    }
127
24
    return converted;
128
24
  }
129

            
130
68
  return cluster.load_assignment();
131
92
}
132

            
133
/**
134
 * DnsClusterImpl: implementation for both logical and strict DNS.
135
 */
136

            
137
absl::StatusOr<std::unique_ptr<DnsClusterImpl>>
138
DnsClusterImpl::create(const envoy::config::cluster::v3::Cluster& cluster,
139
                       const envoy::extensions::clusters::dns::v3::DnsCluster& dns_cluster,
140
93
                       ClusterFactoryContext& context, Network::DnsResolverSharedPtr dns_resolver) {
141
93
  absl::Status creation_status = absl::OkStatus();
142
93
  auto ret = std::unique_ptr<DnsClusterImpl>(
143
93
      new DnsClusterImpl(cluster, dns_cluster, context, std::move(dns_resolver), creation_status));
144

            
145
93
  RETURN_IF_NOT_OK(creation_status);
146
67
  return ret;
147
93
}
148

            
149
DnsClusterImpl::DnsClusterImpl(const envoy::config::cluster::v3::Cluster& cluster,
150
                               const envoy::extensions::clusters::dns::v3::DnsCluster& dns_cluster,
151
                               ClusterFactoryContext& context,
152
                               Network::DnsResolverSharedPtr dns_resolver,
153
                               absl::Status& creation_status)
154
93
    : BaseDynamicClusterImpl(cluster, context, creation_status),
155
      load_assignment_(
156
93
          extractAndProcessLoadAssignment(cluster, dns_cluster.all_addresses_in_single_endpoint())),
157
93
      local_info_(context.serverFactoryContext().localInfo()), dns_resolver_(dns_resolver),
158
93
      dns_refresh_rate_ms_(std::chrono::milliseconds(
159
93
          PROTOBUF_GET_MS_OR_DEFAULT(dns_cluster, dns_refresh_rate, 5000))),
160
93
      dns_jitter_ms_(PROTOBUF_GET_MS_OR_DEFAULT(dns_cluster, dns_jitter, 0)),
161
93
      respect_dns_ttl_(dns_cluster.respect_dns_ttl()),
162
      dns_lookup_family_(
163
93
          Envoy::DnsUtils::getDnsLookupFamilyFromEnum(dns_cluster.dns_lookup_family())),
164
93
      all_addresses_in_single_endpoint_(dns_cluster.all_addresses_in_single_endpoint()) {
165
93
  failure_backoff_strategy_ = Config::Utility::prepareDnsRefreshStrategy(
166
93
      dns_cluster, dns_refresh_rate_ms_.count(),
167
93
      context.serverFactoryContext().api().randomGenerator());
168

            
169
93
  std::list<ResolveTargetPtr> resolve_targets;
170
93
  const auto& locality_lb_endpoints = load_assignment_.endpoints();
171

            
172
93
  if (all_addresses_in_single_endpoint_) { // Logical DNS
173
    // For Logical DNS, we make sure we have just a single endpoint.
174
23
    if (locality_lb_endpoints.size() != 1 || locality_lb_endpoints[0].lb_endpoints().size() != 1) {
175
7
      if (cluster.has_load_assignment()) {
176
6
        creation_status =
177
6
            absl::InvalidArgumentError("LOGICAL_DNS clusters must have a single "
178
6
                                       "locality_lb_endpoint and a single lb_endpoint");
179
6
      } else {
180
1
        creation_status =
181
1
            absl::InvalidArgumentError("LOGICAL_DNS clusters must have a single host");
182
1
      }
183
7
      return;
184
7
    }
185
92
  } else { // Strict DNS
186
    // Strict DNS clusters must ensure that the priority for all localities
187
    // are set to zero when using zone-aware routing. Zone-aware routing only
188
    // works for localities with priority zero (the highest).
189
70
    SET_AND_RETURN_IF_NOT_OK(validateEndpoints(locality_lb_endpoints, {}), creation_status);
190
70
  }
191

            
192
86
  for (const auto& locality_lb_endpoint : locality_lb_endpoints) {
193
47
    for (const auto& lb_endpoint : locality_lb_endpoint.lb_endpoints()) {
194
47
      const auto& socket_address = lb_endpoint.endpoint().address().socket_address();
195
47
      if (!socket_address.resolver_name().empty()) {
196
3
        creation_status = absl::InvalidArgumentError(
197
3
            all_addresses_in_single_endpoint_
198
3
                ? "LOGICAL_DNS clusters must NOT have a custom resolver name set"
199
3
                : "STRICT_DNS clusters must NOT have a custom resolver name set");
200
3
        return;
201
3
      }
202

            
203
44
      resolve_targets.emplace_back(new ResolveTarget(
204
44
          *this, context.serverFactoryContext().mainThreadDispatcher(), socket_address.address(),
205
44
          socket_address.port_value(), locality_lb_endpoint, lb_endpoint));
206
44
    }
207
44
  }
208
83
  resolve_targets_ = std::move(resolve_targets);
209

            
210
83
  overprovisioning_factor_ = PROTOBUF_GET_WRAPPED_OR_DEFAULT(
211
83
      load_assignment_.policy(), overprovisioning_factor, kDefaultOverProvisioningFactor);
212
83
  weighted_priority_health_ = load_assignment_.policy().weighted_priority_health();
213
83
}
214

            
215
33
void DnsClusterImpl::startPreInit() {
216
37
  for (const ResolveTargetPtr& target : resolve_targets_) {
217
37
    target->startResolve();
218
37
  }
219
  // If the config provides no endpoints, the cluster is initialized immediately as if all hosts are
220
  // resolved in failure.
221
33
  if (resolve_targets_.empty() || !wait_for_warm_on_init_) {
222
6
    onPreInitComplete();
223
6
  }
224
33
}
225

            
226
void DnsClusterImpl::updateAllHosts(const HostVector& hosts_added, const HostVector& hosts_removed,
227
49
                                    uint32_t current_priority) {
228
49
  PriorityStateManager priority_state_manager(*this, local_info_, nullptr);
229
  // At this point we know that we are different so make a new host list and notify.
230
  //
231
  // TODO(dio): The uniqueness of a host address resolved in STRICT_DNS cluster per priority is not
232
  // guaranteed. Need a clear agreement on the behavior here, whether it is allowable to have
233
  // duplicated hosts inside a priority. And if we want to enforce this behavior, it should be done
234
  // inside the priority state manager.
235
73
  for (const ResolveTargetPtr& target : resolve_targets_) {
236
73
    priority_state_manager.initializePriorityFor(target->locality_lb_endpoints_);
237
75
    for (const HostSharedPtr& host : target->hosts_) {
238
75
      if (target->locality_lb_endpoints_.priority() == current_priority) {
239
73
        priority_state_manager.registerHostForPriority(host, target->locality_lb_endpoints_);
240
73
      }
241
75
    }
242
73
  }
243

            
244
  // TODO(dio): Add assertion in here.
245
49
  priority_state_manager.updateClusterPrioritySet(
246
49
      current_priority, std::move(priority_state_manager.priorityState()[current_priority].first),
247
49
      hosts_added, hosts_removed, absl::nullopt, weighted_priority_health_,
248
49
      overprovisioning_factor_);
249
49
}
250

            
251
DnsClusterImpl::ResolveTarget::ResolveTarget(
252
    DnsClusterImpl& parent, Event::Dispatcher& dispatcher, const std::string& dns_address,
253
    const uint32_t dns_port,
254
    const envoy::config::endpoint::v3::LocalityLbEndpoints& locality_lb_endpoint,
255
    const envoy::config::endpoint::v3::LbEndpoint& lb_endpoint)
256
44
    : parent_(parent), locality_lb_endpoints_(locality_lb_endpoint), lb_endpoint_(lb_endpoint),
257
44
      dns_address_(dns_address),
258
44
      hostname_(lb_endpoint_.endpoint().hostname().empty() ? dns_address_
259
44
                                                           : lb_endpoint_.endpoint().hostname()),
260
44
      port_(dns_port),
261
44
      resolve_timer_(dispatcher.createTimer([this]() -> void { startResolve(); })) {}
262

            
263
44
DnsClusterImpl::ResolveTarget::~ResolveTarget() {
264
44
  if (active_query_) {
265
10
    active_query_->cancel(Network::ActiveDnsQuery::CancelReason::QueryAbandoned);
266
10
  }
267
44
}
268

            
269
bool DnsClusterImpl::ResolveTarget::isSuccessfulResponse(
270
    const std::list<Network::DnsResponse>& response,
271
74
    const Network::DnsResolver::ResolutionStatus& status) {
272
74
  if (parent_.all_addresses_in_single_endpoint_) {
273
    // Logical DNS doesn't accept empty responses.
274
29
    return status == Network::DnsResolver::ResolutionStatus::Completed && !response.empty();
275
74
  } else {
276
    // For Strict DNS, an empty response just means no available hosts.
277
45
    return status == Network::DnsResolver::ResolutionStatus::Completed;
278
45
  }
279
74
}
280

            
281
absl::StatusOr<DnsClusterImpl::ResolveTarget::ParsedHosts>
282
DnsClusterImpl::ResolveTarget::createLogicalDnsHosts(
283
21
    const std::list<Network::DnsResponse>& response) {
284
21
  ParsedHosts result;
285
21
  const auto& addrinfo = response.front().addrInfo();
286
21
  Network::Address::InstanceConstSharedPtr new_address =
287
21
      Network::Utility::getAddressWithPort(*(addrinfo.address_), port_);
288
21
  auto address_list = DnsUtils::generateAddressList(response, port_);
289
21
  auto logical_host_or_error =
290
21
      LogicalHost::create(parent_.info_, hostname_, new_address, address_list,
291
21
                          locality_lb_endpoints_, lb_endpoint_, nullptr);
292

            
293
21
  RETURN_IF_NOT_OK(logical_host_or_error.status());
294

            
295
21
  result.hosts.emplace_back(std::move(logical_host_or_error.value()));
296
21
  result.host_addresses.emplace(new_address->asString());
297
21
  result.ttl_refresh_rate = min(result.ttl_refresh_rate, addrinfo.ttl_);
298
21
  return result;
299
21
}
300

            
301
absl::StatusOr<DnsClusterImpl::ResolveTarget::ParsedHosts>
302
DnsClusterImpl::ResolveTarget::createStrictDnsHosts(
303
41
    const std::list<Network::DnsResponse>& response) {
304
41
  ParsedHosts result;
305
63
  for (const auto& resp : response) {
306
63
    const auto& addrinfo = resp.addrInfo();
307
    // TODO(mattklein123): Currently the DNS interface does not consider port. We need to
308
    // make a new address that has port in it. We need to both support IPv6 as well as
309
    // potentially move port handling into the DNS interface itself, which would work
310
    // better for SRV.
311
63
    ASSERT(addrinfo.address_ != nullptr);
312
63
    auto address = Network::Utility::getAddressWithPort(*(addrinfo.address_), port_);
313
63
    if (result.host_addresses.count(address->asString()) > 0) {
314
4
      continue;
315
4
    }
316

            
317
59
    auto host_or_error = HostImpl::create(
318
59
        parent_.info_, hostname_, address,
319
        // TODO(zyfjeff): Created through metadata shared pool
320
59
        std::make_shared<const envoy::config::core::v3::Metadata>(lb_endpoint_.metadata()),
321
59
        std::make_shared<const envoy::config::core::v3::Metadata>(
322
59
            locality_lb_endpoints_.metadata()),
323
59
        lb_endpoint_.load_balancing_weight().value(),
324
59
        parent_.constLocalitySharedPool()->getObject(locality_lb_endpoints_.locality()),
325
59
        lb_endpoint_.endpoint().health_check_config(), locality_lb_endpoints_.priority(),
326
59
        lb_endpoint_.health_status());
327

            
328
59
    RETURN_IF_NOT_OK(host_or_error.status());
329

            
330
59
    result.hosts.emplace_back(std::move(host_or_error.value()));
331
59
    result.host_addresses.emplace(address->asString());
332
59
    result.ttl_refresh_rate = min(result.ttl_refresh_rate, addrinfo.ttl_);
333
59
  }
334
41
  return result;
335
41
}
336

            
337
void DnsClusterImpl::ResolveTarget::updateLogicalDnsHosts(
338
21
    const std::list<Network::DnsResponse>& response, const ParsedHosts& new_hosts) {
339
21
  Network::Address::InstanceConstSharedPtr primary_address =
340
21
      Network::Utility::getAddressWithPort(*(response.front().addrInfo().address_), port_);
341
21
  auto all_addresses = DnsUtils::generateAddressList(response, port_);
342
21
  if (!logic_dns_cached_address_ ||
343
21
      (*primary_address != *logic_dns_cached_address_ ||
344
19
       DnsUtils::listChanged(logic_dns_cached_address_list_, all_addresses))) {
345
19
    logic_dns_cached_address_ = primary_address;
346
19
    logic_dns_cached_address_list_ = std::move(all_addresses);
347
19
    ENVOY_LOG(debug, "DNS hosts have changed for {}", dns_address_);
348
19
    const auto previous_hosts = std::move(hosts_);
349
19
    hosts_ = std::move(new_hosts.hosts);
350
    // For logical DNS, we remove the unique logical host, and add the new one.
351
19
    parent_.updateAllHosts(new_hosts.hosts, previous_hosts, locality_lb_endpoints_.priority());
352
19
  } else {
353
2
    parent_.info_->configUpdateStats().update_no_rebuild_.inc();
354
2
  }
355
21
}
356

            
357
41
void DnsClusterImpl::ResolveTarget::updateStrictDnsHosts(const ParsedHosts& new_hosts) {
358
41
  HostVector hosts_added;
359
41
  HostVector hosts_removed;
360
41
  if (parent_.updateDynamicHostList(new_hosts.hosts, hosts_, hosts_added, hosts_removed, all_hosts_,
361
41
                                    new_hosts.host_addresses)) {
362
30
    ENVOY_LOG(debug, "DNS hosts have changed for {}", dns_address_);
363
30
    ASSERT(std::all_of(hosts_.begin(), hosts_.end(), [&](const auto& host) {
364
30
      return host->priority() == locality_lb_endpoints_.priority();
365
30
    }));
366

            
367
    // Update host map for current resolve target.
368
30
    for (const auto& host : hosts_removed) {
369
13
      all_hosts_.erase(host->address()->asString());
370
13
    }
371
41
    for (const auto& host : hosts_added) {
372
41
      all_hosts_.insert({host->address()->asString(), host});
373
41
    }
374

            
375
30
    parent_.updateAllHosts(hosts_added, hosts_removed, locality_lb_endpoints_.priority());
376
30
  } else {
377
11
    parent_.info_->configUpdateStats().update_no_rebuild_.inc();
378
11
  }
379
41
}
380

            
381
66
void DnsClusterImpl::ResolveTarget::startResolve() {
382
66
  ENVOY_LOG(trace, "starting async DNS resolution for {}", dns_address_);
383
66
  parent_.info_->configUpdateStats().update_attempt_.inc();
384

            
385
66
  active_query_ = parent_.dns_resolver_->resolve(
386
66
      dns_address_, parent_.dns_lookup_family_,
387
66
      [this](Network::DnsResolver::ResolutionStatus status, absl::string_view details,
388
74
             std::list<Network::DnsResponse>&& response) -> void {
389
74
        active_query_ = nullptr;
390
74
        ENVOY_LOG(trace, "async DNS resolution complete for {} details {}", dns_address_, details);
391

            
392
74
        std::chrono::milliseconds final_refresh_rate = parent_.dns_refresh_rate_ms_;
393

            
394
74
        if (isSuccessfulResponse(response, status)) {
395
62
          parent_.info_->configUpdateStats().update_success_.inc();
396

            
397
62
          absl::StatusOr<ParsedHosts> new_hosts_or_error;
398

            
399
62
          if (parent_.all_addresses_in_single_endpoint_) {
400
21
            new_hosts_or_error = createLogicalDnsHosts(response);
401
62
          } else {
402
41
            new_hosts_or_error = createStrictDnsHosts(response);
403
41
          }
404

            
405
62
          if (!new_hosts_or_error.ok()) {
406
            ENVOY_LOG(error, "Failed to process DNS response for {} with error: {}", dns_address_,
407
                      new_hosts_or_error.status().message());
408
            parent_.info_->configUpdateStats().update_failure_.inc();
409
            return;
410
          }
411

            
412
62
          const auto& new_hosts = new_hosts_or_error.value();
413

            
414
62
          if (parent_.all_addresses_in_single_endpoint_) {
415
21
            updateLogicalDnsHosts(response, new_hosts);
416
62
          } else {
417
41
            updateStrictDnsHosts(new_hosts);
418
41
          }
419

            
420
          // reset failure backoff strategy because there was a success.
421
62
          parent_.failure_backoff_strategy_->reset();
422

            
423
62
          if (!response.empty() && parent_.respect_dns_ttl_ &&
424
62
              new_hosts.ttl_refresh_rate != std::chrono::seconds(0)) {
425
6
            final_refresh_rate = new_hosts.ttl_refresh_rate;
426
6
            ASSERT(new_hosts.ttl_refresh_rate != std::chrono::seconds::max() &&
427
6
                   final_refresh_rate.count() > 0);
428
6
          }
429
62
          if (parent_.dns_jitter_ms_.count() > 0) {
430
            // Note that `parent_.random_.random()` returns a uint64 while
431
            // `parent_.dns_jitter_ms_.count()` returns a signed long that gets cast into a uint64.
432
            // Thus, the modulo of the two will be a positive as long as
433
            // `parent_dns_jitter_ms_.count()` is positive.
434
            // It is important that this be positive, otherwise `final_refresh_rate` could be
435
            // negative causing Envoy to crash.
436
4
            final_refresh_rate += std::chrono::milliseconds(parent_.random_.random() %
437
4
                                                            parent_.dns_jitter_ms_.count());
438
4
          }
439

            
440
62
          ENVOY_LOG(debug, "DNS refresh rate reset for {}, refresh rate {} ms", dns_address_,
441
62
                    final_refresh_rate.count());
442
62
        } else {
443
12
          parent_.info_->configUpdateStats().update_failure_.inc();
444

            
445
12
          final_refresh_rate =
446
12
              std::chrono::milliseconds(parent_.failure_backoff_strategy_->nextBackOffMs());
447
12
          ENVOY_LOG(debug, "DNS refresh rate reset for {}, (failure) refresh rate {} ms",
448
12
                    dns_address_, final_refresh_rate.count());
449
12
        }
450

            
451
        // If there is an initialize callback, fire it now. Note that if the cluster refers to
452
        // multiple DNS names, this will return initialized after a single DNS resolution
453
        // completes. This is not perfect but is easier to code and unclear if the extra
454
        // complexity is needed so will start with this.
455
74
        parent_.onPreInitComplete();
456
74
        resolve_timer_->enableTimer(final_refresh_rate);
457
74
      });
458
66
}
459

            
460
} // namespace Upstream
461
} // namespace Envoy