| Index: net/http/http_cache_transaction.cc
|
| diff --git a/net/http/http_cache_transaction.cc b/net/http/http_cache_transaction.cc
|
| index 7dd8ecfae9c9d5b631216946d9906127124d134c..a52309b69552081b74a8300b3c045d9b86b1bd53 100644
|
| --- a/net/http/http_cache_transaction.cc
|
| +++ b/net/http/http_cache_transaction.cc
|
| @@ -40,6 +40,16 @@ using base::Time;
|
| using base::TimeDelta;
|
| using base::TimeTicks;
|
|
|
| +namespace {
|
| +
|
| +// The cutoff for tagging small transactions in histograms; this size was chosen
|
| +// to cover resources likely to be received in a single TCP window. With an
|
| +// initial CWND of 10, and an MTU of 1500 bytes, with TCP and HTTP framing
|
| +// overhead this is a size relatively likely to take only one RTT.
|
| +const int kSmallResourceMaxBytes = 14 * 1024;
|
| +
|
| +} // namespace
|
| +
|
| namespace net {
|
|
|
| struct HeaderNameAndValue {
|
| @@ -130,6 +140,9 @@ HttpCache::Transaction::Transaction(
|
| ALLOW_THIS_IN_INITIALIZER_LIST(io_callback_(
|
| base::Bind(&Transaction::OnIOComplete,
|
| weak_factory_.GetWeakPtr()))),
|
| + transaction_pattern_(PATTERN_UNDEFINED),
|
| + bytes_read_from_cache_(0),
|
| + bytes_read_from_network_(0),
|
| transaction_delegate_(transaction_delegate) {
|
| COMPILE_ASSERT(HttpCache::Transaction::kNumValidationHeaders ==
|
| arraysize(kValidationHeaders),
|
| @@ -326,6 +339,7 @@ int HttpCache::Transaction::Read(IOBuffer* buf, int buf_len,
|
| // user wishes to read the network response (the error page). If there is a
|
| // previous response in the cache then we should leave it intact.
|
| if (auth_response_.headers && mode_ != NONE) {
|
| + UpdateTransactionPattern(PATTERN_NOT_COVERED);
|
| DCHECK(mode_ & WRITE);
|
| DoneWritingToEntry(mode_ == READ_WRITE);
|
| mode_ = NONE;
|
| @@ -724,6 +738,8 @@ int HttpCache::Transaction::DoSendRequest() {
|
| DCHECK(mode_ & WRITE || mode_ == NONE);
|
| DCHECK(!network_trans_.get());
|
|
|
| + send_request_since_ = TimeTicks::Now();
|
| +
|
| // Create a network transaction.
|
| int rv = cache_->network_layer_->CreateTransaction(&network_trans_, NULL);
|
| if (rv != OK)
|
| @@ -743,6 +759,8 @@ int HttpCache::Transaction::DoSendRequestComplete(int result) {
|
| return OK;
|
| }
|
|
|
| + // Do not record requests that have network errors or restarts.
|
| + UpdateTransactionPattern(PATTERN_NOT_COVERED);
|
| if (IsCertificateError(result)) {
|
| const HttpResponseInfo* response = network_trans_->GetResponseInfo();
|
| // If we get a certificate error, then there is a certificate in ssl_info,
|
| @@ -773,24 +791,29 @@ int HttpCache::Transaction::DoSuccessfulSendRequest() {
|
| // If we have an authentication response, we are exposed to weird things
|
| // hapenning if the user cancels the authentication before we receive
|
| // the new response.
|
| + UpdateTransactionPattern(PATTERN_NOT_COVERED);
|
| response_ = HttpResponseInfo();
|
| network_trans_.reset();
|
| new_response_ = NULL;
|
| next_state_ = STATE_SEND_REQUEST;
|
| return OK;
|
| }
|
| + bytes_read_from_network_ += new_response_->headers->raw_headers().size();
|
| if (handling_206_ && mode_ == READ_WRITE && !truncated_ && !is_sparse_) {
|
| // We have stored the full entry, but it changed and the server is
|
| // sending a range. We have to delete the old entry.
|
| + UpdateTransactionPattern(PATTERN_NOT_COVERED);
|
| DoneWritingToEntry(false);
|
| }
|
| -
|
| if (new_response_->headers->response_code() == 416) {
|
| DCHECK_EQ(NONE, mode_);
|
| response_ = *new_response_;
|
| return OK;
|
| }
|
|
|
| + if (mode_ == WRITE)
|
| + UpdateTransactionPattern(PATTERN_ENTRY_NOT_CACHED);
|
| +
|
| if (mode_ == WRITE &&
|
| (request_->method == "PUT" || request_->method == "DELETE")) {
|
| if (new_response->headers->response_code() == 200) {
|
| @@ -803,9 +826,11 @@ int HttpCache::Transaction::DoSuccessfulSendRequest() {
|
| // Are we expecting a response to a conditional query?
|
| if (mode_ == READ_WRITE || mode_ == UPDATE) {
|
| if (new_response->headers->response_code() == 304 || handling_206_) {
|
| + UpdateTransactionPattern(PATTERN_ENTRY_VALIDATED);
|
| next_state_ = STATE_UPDATE_CACHED_RESPONSE;
|
| return OK;
|
| }
|
| + UpdateTransactionPattern(PATTERN_ENTRY_UPDATED);
|
| mode_ = WRITE;
|
| }
|
|
|
| @@ -824,6 +849,9 @@ int HttpCache::Transaction::DoNetworkReadComplete(int result) {
|
| if (!cache_)
|
| return ERR_UNEXPECTED;
|
|
|
| + if (result > 0)
|
| + bytes_read_from_network_ += result;
|
| +
|
| // If there is an error or we aren't saving the data, we are done; just wait
|
| // until the destructor runs to see if we can keep the data.
|
| if (mode_ == NONE || result < 0)
|
| @@ -853,6 +881,7 @@ int HttpCache::Transaction::DoOpenEntry() {
|
| next_state_ = STATE_OPEN_ENTRY_COMPLETE;
|
| cache_pending_ = true;
|
| net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_OPEN_ENTRY);
|
| + first_cache_access_since_ = TimeTicks::Now();
|
| ReportCacheActionStart();
|
| return cache_->OpenEntry(cache_key_, &new_entry_, this);
|
| }
|
| @@ -941,6 +970,8 @@ int HttpCache::Transaction::DoCreateEntryComplete(int result) {
|
| int HttpCache::Transaction::DoDoomEntry() {
|
| next_state_ = STATE_DOOM_ENTRY_COMPLETE;
|
| cache_pending_ = true;
|
| + if (first_cache_access_since_.is_null())
|
| + first_cache_access_since_ = TimeTicks::Now();
|
| net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_DOOM_ENTRY);
|
| ReportCacheActionStart();
|
| return cache_->DoomEntry(cache_key_, this);
|
| @@ -953,7 +984,6 @@ int HttpCache::Transaction::DoDoomEntryComplete(int result) {
|
| cache_pending_ = false;
|
| if (result == ERR_CACHE_RACE)
|
| next_state_ = STATE_INIT_ENTRY;
|
| -
|
| return OK;
|
| }
|
|
|
| @@ -1239,6 +1269,7 @@ int HttpCache::Transaction::DoCacheReadResponseComplete(int result) {
|
| &response_, &truncated_)) {
|
| return OnCacheReadError(result, true);
|
| }
|
| + bytes_read_from_cache_ += result;
|
|
|
| // Some resources may have slipped in as truncated when they're not.
|
| int current_size = entry_->disk_entry->GetDataSize(kResponseContentIndex);
|
| @@ -1260,6 +1291,7 @@ int HttpCache::Transaction::DoCacheReadResponseComplete(int result) {
|
| //
|
| switch (mode_) {
|
| case READ:
|
| + UpdateTransactionPattern(PATTERN_ENTRY_USED);
|
| result = BeginCacheRead();
|
| break;
|
| case READ_WRITE:
|
| @@ -1333,7 +1365,6 @@ int HttpCache::Transaction::DoCacheReadMetadataComplete(int result) {
|
| net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_READ_INFO, result);
|
| if (result != response_.metadata->size())
|
| return OnCacheReadError(result, false);
|
| -
|
| return OK;
|
| }
|
|
|
| @@ -1378,12 +1409,18 @@ int HttpCache::Transaction::DoCacheReadDataComplete(int result) {
|
| if (!cache_)
|
| return ERR_UNEXPECTED;
|
|
|
| - if (partial_.get())
|
| + if (partial_.get()) {
|
| + // Partial requests are confusing to report in histograms because they may
|
| + // have multiple underlying requests.
|
| + UpdateTransactionPattern(PATTERN_NOT_COVERED);
|
| return DoPartialCacheReadCompleted(result);
|
| + }
|
|
|
| if (result > 0) {
|
| read_offset_ += result;
|
| + bytes_read_from_cache_ += result;
|
| } else if (result == 0) { // End of file.
|
| + RecordHistograms();
|
| cache_->DoneReadingFromEntry(entry_, this);
|
| entry_ = NULL;
|
| } else {
|
| @@ -1537,6 +1574,7 @@ void HttpCache::Transaction::SetRequest(const BoundNetLog& net_log,
|
| }
|
|
|
| if (range_found && !(effective_load_flags_ & LOAD_DISABLE_CACHE)) {
|
| + UpdateTransactionPattern(PATTERN_NOT_COVERED);
|
| partial_.reset(new PartialData);
|
| if (request_->method == "GET" && partial_->Init(request_->extra_headers)) {
|
| // We will be modifying the actual range requested to the server, so
|
| @@ -1608,8 +1646,12 @@ int HttpCache::Transaction::BeginCacheValidation() {
|
| bool skip_validation = effective_load_flags_ & LOAD_PREFERRING_CACHE ||
|
| !RequiresValidation();
|
|
|
| - if (truncated_)
|
| + if (truncated_) {
|
| + // Truncated entries can cause partial gets, so we shouldn't record this
|
| + // load in histograms.
|
| + UpdateTransactionPattern(PATTERN_NOT_COVERED);
|
| skip_validation = !partial_->initial_validation();
|
| + }
|
|
|
| if (partial_.get() && (is_sparse_ || truncated_) &&
|
| (!partial_->IsCurrentRangeCached() || invalid_range_)) {
|
| @@ -1620,6 +1662,7 @@ int HttpCache::Transaction::BeginCacheValidation() {
|
| }
|
|
|
| if (skip_validation) {
|
| + UpdateTransactionPattern(PATTERN_ENTRY_USED);
|
| if (partial_.get()) {
|
| if (truncated_ || is_sparse_ || !invalid_range_) {
|
| // We are going to return the saved response headers to the caller, so
|
| @@ -1641,6 +1684,7 @@ int HttpCache::Transaction::BeginCacheValidation() {
|
| // Our mode remains READ_WRITE for a conditional request. We'll switch to
|
| // either READ or WRITE mode once we hear back from the server.
|
| if (!ConditionalizeRequest()) {
|
| + UpdateTransactionPattern(PATTERN_NOT_COVERED);
|
| if (partial_.get())
|
| return DoRestartPartialRequest();
|
|
|
| @@ -1659,6 +1703,8 @@ int HttpCache::Transaction::BeginPartialCacheValidation() {
|
| !truncated_)
|
| return BeginCacheValidation();
|
|
|
| + // Partial requests should not be recorded in histograms.
|
| + UpdateTransactionPattern(PATTERN_NOT_COVERED);
|
| if (range_requested_) {
|
| next_state_ = STATE_CACHE_QUERY_DATA;
|
| return OK;
|
| @@ -1713,6 +1759,7 @@ int HttpCache::Transaction::BeginExternallyConditionalizedRequest() {
|
| validator.empty() || validator != external_validation_.values[i]) {
|
| // The externally conditionalized request is not a validation request
|
| // for our existing cache entry. Proceed with caching disabled.
|
| + UpdateTransactionPattern(PATTERN_NOT_COVERED);
|
| DoneWritingToEntry(true);
|
| }
|
| }
|
| @@ -1946,6 +1993,7 @@ bool HttpCache::Transaction::ValidatePartialResponse() {
|
|
|
| if (failure) {
|
| // We cannot truncate this entry, it has to be deleted.
|
| + UpdateTransactionPattern(PATTERN_NOT_COVERED);
|
| DoomPartialEntry(false);
|
| mode_ = NONE;
|
| if (!reading_ && !partial_->IsLastRange()) {
|
| @@ -1970,6 +2018,7 @@ void HttpCache::Transaction::IgnoreRangeRequest() {
|
| // returned the headers), but we'll just pretend that this request is not
|
| // using the cache and see what happens. Most likely this is the first
|
| // response from the server (it's not changing its mind midway, right?).
|
| + UpdateTransactionPattern(PATTERN_NOT_COVERED);
|
| if (mode_ & WRITE) {
|
| DoneWritingToEntry(mode_ != WRITE);
|
| } else if (mode_ & READ && entry_) {
|
| @@ -2068,9 +2117,7 @@ void HttpCache::Transaction::DoneWritingToEntry(bool success) {
|
| if (!entry_)
|
| return;
|
|
|
| - if (cache_->mode() == RECORD)
|
| - DVLOG(1) << "Recorded: " << request_->method << request_->url
|
| - << " status: " << response_.headers->response_code();
|
| + RecordHistograms();
|
|
|
| cache_->DoneWritingToEntry(entry_, success);
|
| entry_ = NULL;
|
| @@ -2185,4 +2232,127 @@ void HttpCache::Transaction::ReportCacheActionFinish() {
|
| transaction_delegate_->OnCacheActionFinish();
|
| }
|
|
|
| +void HttpCache::Transaction::UpdateTransactionPattern(
|
| + TransactionPattern new_transaction_pattern) {
|
| + if (transaction_pattern_ == PATTERN_NOT_COVERED)
|
| + return;
|
| + DCHECK(transaction_pattern_ == PATTERN_UNDEFINED ||
|
| + new_transaction_pattern == PATTERN_NOT_COVERED);
|
| + transaction_pattern_ = new_transaction_pattern;
|
| +}
|
| +
|
| +void HttpCache::Transaction::RecordHistograms() {
|
| + DCHECK_NE(PATTERN_UNDEFINED, transaction_pattern_);
|
| + if (!cache_ || !cache_->GetCurrentBackend() ||
|
| + cache_->GetCurrentBackend()->GetCacheType() != DISK_CACHE ||
|
| + cache_->mode() != NORMAL || request_->method != "GET") {
|
| + return;
|
| + }
|
| + UMA_HISTOGRAM_BOOLEAN("HttpCache.HasPattern",
|
| + transaction_pattern_ != PATTERN_NOT_COVERED);
|
| + if (transaction_pattern_ == PATTERN_NOT_COVERED)
|
| + return;
|
| + DCHECK(!range_requested_);
|
| + DCHECK(!first_cache_access_since_.is_null());
|
| +
|
| + TimeDelta total_time = base::TimeTicks::Now() - first_cache_access_since_;
|
| +
|
| + UMA_HISTOGRAM_TIMES("HttpCache.AccessToDone", total_time);
|
| +
|
| + bool did_send_request = !send_request_since_.is_null();
|
| + DCHECK(
|
| + (did_send_request && (transaction_pattern_ == PATTERN_ENTRY_NOT_CACHED ||
|
| + transaction_pattern_ == PATTERN_ENTRY_VALIDATED ||
|
| + transaction_pattern_ == PATTERN_ENTRY_UPDATED)) ||
|
| + (!did_send_request && transaction_pattern_ == PATTERN_ENTRY_USED));
|
| +
|
| + int resource_size;
|
| + if (transaction_pattern_ == PATTERN_ENTRY_NOT_CACHED ||
|
| + transaction_pattern_ == PATTERN_ENTRY_UPDATED) {
|
| + resource_size = bytes_read_from_network_;
|
| + } else {
|
| + DCHECK(transaction_pattern_ == PATTERN_ENTRY_VALIDATED ||
|
| + transaction_pattern_ == PATTERN_ENTRY_USED);
|
| + resource_size = bytes_read_from_cache_;
|
| + }
|
| +
|
| + bool is_small_resource = resource_size < kSmallResourceMaxBytes;
|
| + if (is_small_resource)
|
| + UMA_HISTOGRAM_TIMES("HttpCache.AccessToDone.SmallResource", total_time);
|
| +
|
| + if (!did_send_request) {
|
| + DCHECK(transaction_pattern_ == PATTERN_ENTRY_USED);
|
| + UMA_HISTOGRAM_TIMES("HttpCache.AccessToDone.Used", total_time);
|
| + if (is_small_resource) {
|
| + UMA_HISTOGRAM_TIMES(
|
| + "HttpCache.AccessToDone.Used.SmallResource", total_time);
|
| + }
|
| + return;
|
| + }
|
| +
|
| + TimeDelta before_send_time = send_request_since_ - first_cache_access_since_;
|
| + int before_send_percent =
|
| + total_time.ToInternalValue() == 0 ? 0
|
| + : before_send_time * 100 / total_time;
|
| + DCHECK_LE(0, before_send_percent);
|
| + DCHECK_GE(100, before_send_percent);
|
| +
|
| + UMA_HISTOGRAM_TIMES("HttpCache.AccessToDone.SentRequest", total_time);
|
| + UMA_HISTOGRAM_TIMES("HttpCache.BeforeSend", before_send_time);
|
| + UMA_HISTOGRAM_PERCENTAGE("HttpCache.PercentBeforeSend", before_send_percent);
|
| + if (is_small_resource) {
|
| + UMA_HISTOGRAM_TIMES(
|
| + "HttpCache.AccessToDone.SentRequest.SmallResource", total_time);
|
| + UMA_HISTOGRAM_TIMES("HttpCache.BeforeSend.SmallResource", before_send_time);
|
| + UMA_HISTOGRAM_PERCENTAGE(
|
| + "HttpCache.PercentBeforeSend.SmallResource", before_send_percent);
|
| + }
|
| +
|
| + // TODO(gavinp): Remove or minimize these histograms, particularly the ones
|
| + // below this comment after we have received initial data.
|
| + switch (transaction_pattern_) {
|
| + case PATTERN_ENTRY_NOT_CACHED: {
|
| + UMA_HISTOGRAM_TIMES("HttpCache.BeforeSend.NotCached", before_send_time);
|
| + UMA_HISTOGRAM_PERCENTAGE(
|
| + "HttpCache.PercentBeforeSend.NotCached", before_send_percent);
|
| + if (is_small_resource) {
|
| + UMA_HISTOGRAM_TIMES(
|
| + "HttpCache.BeforeSend.NotCached.SmallResource", before_send_time);
|
| + UMA_HISTOGRAM_PERCENTAGE(
|
| + "HttpCache.PercentBeforeSend.NotCached.SmallResource",
|
| + before_send_percent);
|
| + }
|
| + break;
|
| + }
|
| + case PATTERN_ENTRY_VALIDATED: {
|
| + UMA_HISTOGRAM_TIMES("HttpCache.BeforeSend.Validated", before_send_time);
|
| + UMA_HISTOGRAM_PERCENTAGE(
|
| + "HttpCache.PercentBeforeSend.Validated", before_send_percent);
|
| + if (is_small_resource) {
|
| + UMA_HISTOGRAM_TIMES(
|
| + "HttpCache.BeforeSend.Validated.SmallResource", before_send_time);
|
| + UMA_HISTOGRAM_PERCENTAGE(
|
| + "HttpCache.PercentBeforeSend.Validated.SmallResource",
|
| + before_send_percent);
|
| + }
|
| + break;
|
| + }
|
| + case PATTERN_ENTRY_UPDATED: {
|
| + UMA_HISTOGRAM_TIMES("HttpCache.BeforeSend.Updated", before_send_time);
|
| + UMA_HISTOGRAM_PERCENTAGE(
|
| + "HttpCache.PercentBeforeSend.Updated", before_send_percent);
|
| + if (is_small_resource) {
|
| + UMA_HISTOGRAM_TIMES(
|
| + "HttpCache.BeforeSend.Updated.SmallResource", before_send_time);
|
| + UMA_HISTOGRAM_PERCENTAGE(
|
| + "HttpCache.PercentBeforeSend.Updated.SmallResource",
|
| + before_send_percent);
|
| + }
|
| + break;
|
| + }
|
| + default:
|
| + NOTREACHED();
|
| + }
|
| +}
|
| +
|
| } // namespace net
|
|
|