YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/yb/util/net/dns_resolver.cc
Line
Count
Source (jump to first uncovered line)
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
//
18
// The following only applies to changes made to this file as part of YugaByte development.
19
//
20
// Portions Copyright (c) YugaByte, Inc.
21
//
22
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
23
// in compliance with the License.  You may obtain a copy of the License at
24
//
25
// http://www.apache.org/licenses/LICENSE-2.0
26
//
27
// Unless required by applicable law or agreed to in writing, software distributed under the License
28
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
29
// or implied.  See the License for the specific language governing permissions and limitations
30
// under the License.
31
//
32
33
#include "yb/util/net/dns_resolver.h"
34
35
#include <mutex>
36
#include <shared_mutex>
37
#include <unordered_map>
38
#include <vector>
39
40
#include <glog/logging.h>
41
42
#include "yb/util/metrics.h"
43
#include "yb/util/net/net_fwd.h"
44
#include "yb/util/net/inetaddress.h"
45
#include "yb/util/net/net_util.h"
46
#include "yb/util/net/sockaddr.h"
47
#include "yb/util/result.h"
48
#include "yb/util/status_format.h"
49
50
using namespace std::literals;
51
52
DEFINE_int64(dns_cache_expiration_ms, 60000, "Time to store DNS resolution results in cache.");
53
54
namespace yb {
55
56
namespace {
57
58
59
Result<IpAddress> PickResolvedAddress(
60
    const std::string& host, const boost::system::error_code& error,
61
1
    const ResolverResults& entries) {
62
1
  if (error) {
63
0
    return STATUS_FORMAT(NetworkError, "Resolve failed $0: $1", host, error.message());
64
0
  }
65
1
  std::vector<IpAddress> addresses;
66
2
  for (const auto& entry : entries) {
67
2
    addresses.push_back(entry.endpoint().address());
68
0
    VLOG(3) << "Resolved address " << entry.endpoint().address().to_string()
69
0
            << " for host " << host;
70
2
  }
71
1
  FilterAddresses(FLAGS_net_address_filter, &addresses);
72
1
  if (addresses.empty()) {
73
0
    return STATUS_FORMAT(NetworkError, "No endpoints resolved for: $0", host);
74
0
  }
75
1
  std::sort(addresses.begin(), addresses.end());
76
1
  addresses.erase(std::unique(addresses.begin(), addresses.end()), addresses.end());
77
1
  if (addresses.size() > 1) {
78
1
    LOG(WARNING) << "Peer address '" << host << "' "
79
1
                 << "resolves to " << yb::ToString(addresses) << " different addresses. Using "
80
1
                 << yb::ToString(addresses.front());
81
1
  }
82
83
0
  VLOG(3) << "Returned address " << addresses[0].to_string() << " for host "
84
0
          << host;
85
1
  return addresses.front();
86
1
}
87
88
} // namespace
89
90
class DnsResolver::Impl {
91
 public:
92
21.3k
  explicit Impl(IoService* io_service) : io_service_(*io_service), resolver_(*io_service) {}
93
94
385k
  std::shared_future<Result<IpAddress>> ResolveFuture(const std::string& host) {
95
385k
    return ObtainEntry(host)->DoResolve(host, /* callback= */ nullptr, &io_service_, &resolver_);
96
385k
  }
97
98
0
  void AsyncResolve(const std::string& host, const AsyncResolveCallback& callback) {
99
0
    ObtainEntry(host)->DoResolve(host, &callback, &io_service_, &resolver_);
100
0
  }
101
102
 private:
103
  using Resolver = boost::asio::ip::basic_resolver<boost::asio::ip::tcp>;
104
105
  struct CacheEntry {
106
    std::mutex mutex;
107
    CoarseTimePoint expiration GUARDED_BY(mutex) = CoarseTimePoint::min();
108
    std::shared_future<Result<IpAddress>> future GUARDED_BY(mutex);
109
    std::vector<AsyncResolveCallback> waiters GUARDED_BY(mutex);
110
111
    void SetResult(
112
        const Result<IpAddress>& result,
113
1
        std::promise<Result<IpAddress>>* promise) EXCLUDES(mutex) {
114
1
      try {
115
1
        promise->set_value(result);
116
0
      } catch (std::future_error& error) {
117
0
        return;
118
0
      }
119
120
1
      decltype(waiters) to_notify;
121
1
      {
122
1
        std::lock_guard<std::mutex> lock(mutex);
123
1
        expiration = CoarseMonoClock::now() + FLAGS_dns_cache_expiration_ms * 1ms;
124
1
        waiters.swap(to_notify);
125
1
      }
126
0
      for (const auto& waiter : to_notify) {
127
0
        waiter(result);
128
0
      }
129
1
    }
130
131
    std::shared_future<Result<IpAddress>> DoResolve(
132
        const std::string& host, const AsyncResolveCallback* callback, IoService* io_service,
133
375k
        Resolver* resolver) {
134
375k
      std::shared_ptr<std::promise<Result<IpAddress>>> promise;
135
375k
      std::shared_future<Result<IpAddress>> result;
136
375k
      {
137
375k
        std::lock_guard<std::mutex> lock(mutex);
138
375k
        promise = StartResolve(host);
139
375k
        result = future;
140
375k
        if (callback && expiration == CoarseTimePoint::max()) {
141
          // Resolve is in progress by a different caller.
142
0
          waiters.push_back(*callback);
143
0
          callback = nullptr;
144
0
        }
145
375k
      }
146
147
375k
      if (callback) {
148
0
        (*callback)(result.get());
149
0
      }
150
151
375k
      if (promise) {
152
0
        static const std::string kService = "";
153
0
        resolver->async_resolve(
154
0
            Resolver::query(host, kService),
155
0
            [this, host, promise](
156
0
                const boost::system::error_code& error,
157
0
                const Resolver::results_type& entries) mutable {
158
          // Unfortunately there is no safe way to set promise value from 2 different threads, w/o
159
          // catching exception in case of concurrency.
160
0
          SetResult(PickResolvedAddress(host, error, entries), promise.get());
161
0
        });
162
163
0
        if (io_service->stopped()) {
164
0
          SetResult(STATUS(Aborted, "Messenger already stopped"), promise.get());
165
0
        }
166
0
      }
167
168
375k
      return result;
169
375k
    }
170
171
    std::shared_ptr<std::promise<Result<IpAddress>>> StartResolve(
172
386k
        const std::string& host) REQUIRES(mutex) {
173
386k
      if (expiration >= CoarseMonoClock::now()) {
174
368k
        return nullptr;
175
368k
      }
176
177
17.9k
      auto promise = std::make_shared<std::promise<Result<IpAddress>>>();
178
17.9k
      future = promise->get_future().share();
179
180
17.9k
      auto address = TryFastResolve(host);
181
17.9k
      if (address) {
182
17.9k
        expiration = CoarseTimePoint::max() - 1ms;
183
17.9k
        promise->set_value(*address);
184
17.9k
        return nullptr;
185
0
      } else {
186
0
        expiration = CoarseTimePoint::max();
187
0
      }
188
189
0
      return promise;
190
17.9k
    }
191
  };
192
193
386k
  CacheEntry* ObtainEntry(const std::string& host) {
194
386k
    {
195
386k
      std::shared_lock<decltype(mutex_)> lock(mutex_);
196
386k
      auto it = cache_.find(host);
197
386k
      if (it != cache_.end()) {
198
368k
        return &it->second;
199
368k
      }
200
18.0k
    }
201
202
18.0k
    std::lock_guard<decltype(mutex_)> lock(mutex_);
203
18.0k
    return &cache_[host];
204
18.0k
  }
205
206
  IoService& io_service_;
207
  Resolver resolver_;
208
  std::shared_timed_mutex mutex_;
209
  std::unordered_map<std::string, CacheEntry> cache_;
210
};
211
212
21.3k
DnsResolver::DnsResolver(IoService* io_service) : impl_(new Impl(io_service)) {
213
21.3k
}
214
215
4.04k
DnsResolver::~DnsResolver() {
216
4.04k
}
217
218
namespace {
219
220
thread_local Histogram* active_metric_ = nullptr;
221
222
} // anonymous namespace
223
224
ScopedDnsTracker::ScopedDnsTracker(const scoped_refptr<Histogram>& metric)
225
51.2k
    : old_metric_(active_metric()), metric_(metric) {
226
51.2k
  active_metric_ = metric.get();
227
51.2k
}
228
229
51.1k
ScopedDnsTracker::~ScopedDnsTracker() {
230
51.1k
  DCHECK_EQ(metric_.get(), active_metric());
231
51.1k
  active_metric_ = old_metric_;
232
51.1k
}
233
234
247k
Histogram* ScopedDnsTracker::active_metric() {
235
247k
  return active_metric_;
236
247k
}
237
238
386k
std::shared_future<Result<IpAddress>> DnsResolver::ResolveFuture(const std::string& host) {
239
386k
  return impl_->ResolveFuture(host);
240
386k
}
241
242
0
void DnsResolver::AsyncResolve(const std::string& host, const AsyncResolveCallback& callback) {
243
0
  impl_->AsyncResolve(host, callback);
244
0
}
245
246
88.6k
Result<IpAddress> DnsResolver::Resolve(const std::string& host) {
247
88.6k
  return ResolveFuture(host).get();
248
88.6k
}
249
250
} // namespace yb