blob: 35772124cc513e1fc4649a59ea83efa48482b793 [file] [log] [blame]
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "net/dns/dns_session.h"
6
7#include "base/basictypes.h"
8#include "base/bind.h"
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +01009#include "base/lazy_instance.h"
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +010010#include "base/metrics/histogram.h"
11#include "base/metrics/sample_vector.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000012#include "base/rand_util.h"
13#include "base/stl_util.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000014#include "base/time.h"
15#include "net/base/ip_endpoint.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000016#include "net/base/net_errors.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000017#include "net/dns/dns_config_service.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000018#include "net/dns/dns_socket_pool.h"
19#include "net/socket/stream_socket.h"
20#include "net/udp/datagram_client_socket.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000021
22namespace net {
23
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +010024namespace {
25// Never exceed max timeout.
26const unsigned kMaxTimeoutMs = 5000;
27// Set min timeout, in case we are talking to a local DNS proxy.
28const unsigned kMinTimeoutMs = 10;
29
30// Number of buckets in the histogram of observed RTTs.
31const size_t kRTTBucketCount = 100;
32// Target percentile in the RTT histogram used for retransmission timeout.
33const unsigned kRTOPercentile = 99;
34} // namespace
35
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010036// Runtime statistics of DNS server.
37struct DnsSession::ServerStats {
38 ServerStats(base::TimeDelta rtt_estimate_param, RttBuckets* buckets)
39 : last_failure_count(0), rtt_estimate(rtt_estimate_param) {
40 rtt_histogram.reset(new base::SampleVector(buckets));
41 }
42
43 // Count of consecutive failures after last success.
44 int last_failure_count;
45
46 // Last time when server returned failure or timeout.
47 base::Time last_failure;
48 // Last time when server returned success.
49 base::Time last_success;
50
51 // Estimated RTT using moving average.
52 base::TimeDelta rtt_estimate;
53 // Estimated error in the above.
54 base::TimeDelta rtt_deviation;
55
56 // A histogram of observed RTT .
57 scoped_ptr<base::SampleVector> rtt_histogram;
58
59 DISALLOW_COPY_AND_ASSIGN(ServerStats);
60};
61
62// static
63base::LazyInstance<DnsSession::RttBuckets>::Leaky DnsSession::rtt_buckets_ =
64 LAZY_INSTANCE_INITIALIZER;
65
66DnsSession::RttBuckets::RttBuckets() : base::BucketRanges(kRTTBucketCount + 1) {
67 base::Histogram::InitializeBucketRanges(1, 5000, kRTTBucketCount, this);
68}
69
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000070DnsSession::SocketLease::SocketLease(scoped_refptr<DnsSession> session,
71 unsigned server_index,
72 scoped_ptr<DatagramClientSocket> socket)
73 : session_(session), server_index_(server_index), socket_(socket.Pass()) {}
74
75DnsSession::SocketLease::~SocketLease() {
76 session_->FreeSocket(server_index_, socket_.Pass());
77}
78
Torne (Richard Coles)58218062012-11-14 11:43:16 +000079DnsSession::DnsSession(const DnsConfig& config,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000080 scoped_ptr<DnsSocketPool> socket_pool,
Torne (Richard Coles)58218062012-11-14 11:43:16 +000081 const RandIntCallback& rand_int_callback,
82 NetLog* net_log)
83 : config_(config),
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000084 socket_pool_(socket_pool.Pass()),
Torne (Richard Coles)58218062012-11-14 11:43:16 +000085 rand_callback_(base::Bind(rand_int_callback, 0, kuint16max)),
86 net_log_(net_log),
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010087 server_index_(0) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000088 socket_pool_->Initialize(&config_.nameservers, net_log);
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010089 UMA_HISTOGRAM_CUSTOM_COUNTS(
90 "AsyncDNS.ServerCount", config_.nameservers.size(), 0, 10, 10);
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +010091 for (size_t i = 0; i < config_.nameservers.size(); ++i) {
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010092 server_stats_.push_back(new ServerStats(config_.timeout,
93 rtt_buckets_.Pointer()));
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +010094 }
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000095}
96
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010097DnsSession::~DnsSession() {
98 RecordServerStats();
99}
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000100
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100101int DnsSession::NextQueryId() const { return rand_callback_.Run(); }
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000102
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100103unsigned DnsSession::NextFirstServerIndex() {
104 unsigned index = NextGoodServerIndex(server_index_);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000105 if (config_.rotate)
106 server_index_ = (server_index_ + 1) % config_.nameservers.size();
107 return index;
108}
109
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100110unsigned DnsSession::NextGoodServerIndex(unsigned server_index) {
111 unsigned index = server_index;
112 base::Time oldest_server_failure(base::Time::Now());
113 unsigned oldest_server_failure_index = 0;
114
115 UMA_HISTOGRAM_BOOLEAN("AsyncDNS.ServerIsGood",
116 server_stats_[server_index]->last_failure.is_null());
117
118 do {
119 base::Time cur_server_failure = server_stats_[index]->last_failure;
120 // If number of failures on this server doesn't exceed number of allowed
121 // attempts, return its index.
122 if (server_stats_[server_index]->last_failure_count < config_.attempts) {
123 return index;
124 }
125 // Track oldest failed server.
126 if (cur_server_failure < oldest_server_failure) {
127 oldest_server_failure = cur_server_failure;
128 oldest_server_failure_index = index;
129 }
130 index = (index + 1) % config_.nameservers.size();
131 } while (index != server_index);
132
133 // If we are here it means that there are no successful servers, so we have
134 // to use one that has failed oldest.
135 return oldest_server_failure_index;
136}
137
138void DnsSession::RecordServerFailure(unsigned server_index) {
139 UMA_HISTOGRAM_CUSTOM_COUNTS(
140 "AsyncDNS.ServerFailureIndex", server_index, 0, 10, 10);
141 ++(server_stats_[server_index]->last_failure_count);
142 server_stats_[server_index]->last_failure = base::Time::Now();
143}
144
145void DnsSession::RecordServerSuccess(unsigned server_index) {
146 if (server_stats_[server_index]->last_success.is_null()) {
147 UMA_HISTOGRAM_COUNTS_100("AsyncDNS.ServerFailuresAfterNetworkChange",
148 server_stats_[server_index]->last_failure_count);
149 } else {
150 UMA_HISTOGRAM_COUNTS_100("AsyncDNS.ServerFailuresBeforeSuccess",
151 server_stats_[server_index]->last_failure_count);
152 }
153 server_stats_[server_index]->last_failure_count = 0;
154 server_stats_[server_index]->last_failure = base::Time();
155 server_stats_[server_index]->last_success = base::Time::Now();
156}
157
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100158void DnsSession::RecordRTT(unsigned server_index, base::TimeDelta rtt) {
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100159 DCHECK_LT(server_index, server_stats_.size());
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100160
161 // For measurement, assume it is the first attempt (no backoff).
162 base::TimeDelta timeout_jacobson = NextTimeoutFromJacobson(server_index, 0);
163 base::TimeDelta timeout_histogram = NextTimeoutFromHistogram(server_index, 0);
164 UMA_HISTOGRAM_TIMES("AsyncDNS.TimeoutErrorJacobson", rtt - timeout_jacobson);
165 UMA_HISTOGRAM_TIMES("AsyncDNS.TimeoutErrorHistogram",
166 rtt - timeout_histogram);
167 UMA_HISTOGRAM_TIMES("AsyncDNS.TimeoutErrorJacobsonUnder",
168 timeout_jacobson - rtt);
169 UMA_HISTOGRAM_TIMES("AsyncDNS.TimeoutErrorHistogramUnder",
170 timeout_histogram - rtt);
171
172 // Jacobson/Karels algorithm for TCP.
173 // Using parameters: alpha = 1/8, delta = 1/4, beta = 4
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100174 base::TimeDelta& estimate = server_stats_[server_index]->rtt_estimate;
175 base::TimeDelta& deviation = server_stats_[server_index]->rtt_deviation;
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100176 base::TimeDelta current_error = rtt - estimate;
177 estimate += current_error / 8; // * alpha
178 base::TimeDelta abs_error = base::TimeDelta::FromInternalValue(
179 std::abs(current_error.ToInternalValue()));
180 deviation += (abs_error - deviation) / 4; // * delta
181
182 // Histogram-based method.
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100183 server_stats_[server_index]->rtt_histogram
184 ->Accumulate(rtt.InMilliseconds(), 1);
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100185}
186
187void DnsSession::RecordLostPacket(unsigned server_index, int attempt) {
188 base::TimeDelta timeout_jacobson =
189 NextTimeoutFromJacobson(server_index, attempt);
190 base::TimeDelta timeout_histogram =
191 NextTimeoutFromHistogram(server_index, attempt);
192 UMA_HISTOGRAM_TIMES("AsyncDNS.TimeoutSpentJacobson", timeout_jacobson);
193 UMA_HISTOGRAM_TIMES("AsyncDNS.TimeoutSpentHistogram", timeout_histogram);
194}
195
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100196void DnsSession::RecordServerStats() {
197 for (size_t index = 0; index < server_stats_.size(); ++index) {
198 if (server_stats_[index]->last_failure_count) {
199 if (server_stats_[index]->last_success.is_null()) {
200 UMA_HISTOGRAM_COUNTS("AsyncDNS.ServerFailuresWithoutSuccess",
201 server_stats_[index]->last_failure_count);
202 } else {
203 UMA_HISTOGRAM_COUNTS("AsyncDNS.ServerFailuresAfterSuccess",
204 server_stats_[index]->last_failure_count);
205 }
206 }
207 }
208}
209
210
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100211base::TimeDelta DnsSession::NextTimeout(unsigned server_index, int attempt) {
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100212 DCHECK_LT(server_index, server_stats_.size());
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100213
214 base::TimeDelta timeout = config_.timeout;
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100215 // If this server has not responded successfully, then don't wait too long.
216 if (server_stats_[server_index]->last_success.is_null())
217 return timeout;
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100218
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000219 // The timeout doubles every full round (each nameserver once).
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000220 unsigned num_backoffs = attempt / config_.nameservers.size();
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100221
222 return std::min(timeout * (1 << num_backoffs),
223 base::TimeDelta::FromMilliseconds(kMaxTimeoutMs));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000224}
225
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000226// Allocate a socket, already connected to the server address.
227scoped_ptr<DnsSession::SocketLease> DnsSession::AllocateSocket(
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100228 unsigned server_index, const NetLog::Source& source) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000229 scoped_ptr<DatagramClientSocket> socket;
230
231 socket = socket_pool_->AllocateSocket(server_index);
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100232 if (!socket.get())
233 return scoped_ptr<SocketLease>();
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000234
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100235 socket->NetLog().BeginEvent(NetLog::TYPE_SOCKET_IN_USE,
236 source.ToEventParametersCallback());
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000237
238 SocketLease* lease = new SocketLease(this, server_index, socket.Pass());
239 return scoped_ptr<SocketLease>(lease);
240}
241
242scoped_ptr<StreamSocket> DnsSession::CreateTCPSocket(
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100243 unsigned server_index, const NetLog::Source& source) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000244 return socket_pool_->CreateTCPSocket(server_index, source);
245}
246
247// Release a socket.
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100248void DnsSession::FreeSocket(unsigned server_index,
249 scoped_ptr<DatagramClientSocket> socket) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000250 DCHECK(socket.get());
251
252 socket->NetLog().EndEvent(NetLog::TYPE_SOCKET_IN_USE);
253
254 socket_pool_->FreeSocket(server_index, socket.Pass());
255}
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000256
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100257base::TimeDelta DnsSession::NextTimeoutFromJacobson(unsigned server_index,
258 int attempt) {
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100259 DCHECK_LT(server_index, server_stats_.size());
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100260
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100261 base::TimeDelta timeout = server_stats_[server_index]->rtt_estimate +
262 4 * server_stats_[server_index]->rtt_deviation;
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100263
264 timeout = std::max(timeout, base::TimeDelta::FromMilliseconds(kMinTimeoutMs));
265
266 // The timeout doubles every full round.
267 unsigned num_backoffs = attempt / config_.nameservers.size();
268
269 return std::min(timeout * (1 << num_backoffs),
270 base::TimeDelta::FromMilliseconds(kMaxTimeoutMs));
271}
272
273base::TimeDelta DnsSession::NextTimeoutFromHistogram(unsigned server_index,
274 int attempt) {
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100275 DCHECK_LT(server_index, server_stats_.size());
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100276
277 COMPILE_ASSERT(std::numeric_limits<base::HistogramBase::Count>::is_signed,
278 histogram_base_count_assumed_to_be_signed);
279
280 // Use fixed percentile of observed samples.
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100281 const base::SampleVector& samples =
282 *server_stats_[server_index]->rtt_histogram;
283
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100284 base::HistogramBase::Count total = samples.TotalCount();
285 base::HistogramBase::Count remaining_count = kRTOPercentile * total / 100;
286 size_t index = 0;
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100287 while (remaining_count > 0 && index < rtt_buckets_.Get().size()) {
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100288 remaining_count -= samples.GetCountAtIndex(index);
289 ++index;
290 }
291
292 base::TimeDelta timeout =
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100293 base::TimeDelta::FromMilliseconds(rtt_buckets_.Get().range(index));
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100294
295 timeout = std::max(timeout, base::TimeDelta::FromMilliseconds(kMinTimeoutMs));
296
297 // The timeout still doubles every full round.
298 unsigned num_backoffs = attempt / config_.nameservers.size();
299
300 return std::min(timeout * (1 << num_backoffs),
301 base::TimeDelta::FromMilliseconds(kMaxTimeoutMs));
302}
303
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000304} // namespace net