1 // 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 "sync/engine/backoff_delay_provider.h" 6 7 #include "base/rand_util.h" 8 #include "sync/internal_api/public/engine/polling_constants.h" 9 #include "sync/internal_api/public/sessions/model_neutral_state.h" 10 #include "sync/internal_api/public/util/syncer_error.h" 11 12 using base::TimeDelta; 13 14 namespace syncer { 15 16 // static 17 BackoffDelayProvider* BackoffDelayProvider::FromDefaults() { 18 return new BackoffDelayProvider( 19 TimeDelta::FromSeconds(kInitialBackoffRetrySeconds), 20 TimeDelta::FromSeconds(kInitialBackoffImmediateRetrySeconds)); 21 } 22 23 // static 24 BackoffDelayProvider* BackoffDelayProvider::WithShortInitialRetryOverride() { 25 return new BackoffDelayProvider( 26 TimeDelta::FromSeconds(kInitialBackoffShortRetrySeconds), 27 TimeDelta::FromSeconds(kInitialBackoffImmediateRetrySeconds)); 28 } 29 30 BackoffDelayProvider::BackoffDelayProvider( 31 const base::TimeDelta& default_initial_backoff, 32 const base::TimeDelta& short_initial_backoff) 33 : default_initial_backoff_(default_initial_backoff), 34 short_initial_backoff_(short_initial_backoff) { 35 } 36 37 BackoffDelayProvider::~BackoffDelayProvider() {} 38 39 TimeDelta BackoffDelayProvider::GetDelay(const base::TimeDelta& last_delay) { 40 if (last_delay.InSeconds() >= kMaxBackoffSeconds) 41 return TimeDelta::FromSeconds(kMaxBackoffSeconds); 42 43 // This calculates approx. base_delay_seconds * 2 +/- base_delay_seconds / 2 44 int64 backoff_s = 45 std::max(static_cast<int64>(1), 46 last_delay.InSeconds() * kBackoffRandomizationFactor); 47 48 // Flip a coin to randomize backoff interval by +/- 50%. 49 int rand_sign = base::RandInt(0, 1) * 2 - 1; 50 51 // Truncation is adequate for rounding here. 52 backoff_s = backoff_s + 53 (rand_sign * (last_delay.InSeconds() / kBackoffRandomizationFactor)); 54 55 // Cap the backoff interval. 56 backoff_s = std::max(static_cast<int64>(1), 57 std::min(backoff_s, kMaxBackoffSeconds)); 58 59 return TimeDelta::FromSeconds(backoff_s); 60 } 61 62 TimeDelta BackoffDelayProvider::GetInitialDelay( 63 const sessions::ModelNeutralState& state) const { 64 // NETWORK_CONNECTION_UNAVAILABLE implies we did not even manage to hit the 65 // wire; the failure occurred locally. Note that if commit_result is *not* 66 // UNSET, this implies download_updates_result succeeded. Also note that 67 // last_get_key_result is coupled to last_download_updates_result in that 68 // they are part of the same GetUpdates request, so we only check if 69 // the download request is CONNECTION_UNAVAILABLE. 70 // 71 // TODO(tim): Should we treat NETWORK_IO_ERROR similarly? It's different 72 // from CONNECTION_UNAVAILABLE in that a request may well have succeeded 73 // in contacting the server (e.g we got a 200 back), but we failed 74 // trying to parse the response (actual content length != HTTP response 75 // header content length value). For now since we're considering 76 // merging this code to branches and I haven't audited all the 77 // NETWORK_IO_ERROR cases carefully, I'm going to target the fix 78 // very tightly (see bug chromium-os:35073). DIRECTORY_LOOKUP_FAILED is 79 // another example of something that shouldn't backoff, though the 80 // scheduler should probably be handling these cases differently. See 81 // the TODO(rlarocque) in ScheduleNextSync. 82 if (state.commit_result == NETWORK_CONNECTION_UNAVAILABLE || 83 state.last_download_updates_result == NETWORK_CONNECTION_UNAVAILABLE) { 84 return short_initial_backoff_; 85 } 86 87 if (SyncerErrorIsError(state.last_get_key_result)) 88 return default_initial_backoff_; 89 90 // Note: If we received a MIGRATION_DONE on download updates, then commit 91 // should not have taken place. Moreover, if we receive a MIGRATION_DONE 92 // on commit, it means that download updates succeeded. Therefore, we only 93 // need to check if either code is equal to SERVER_RETURN_MIGRATION_DONE, 94 // and not if there were any more serious errors requiring the long retry. 95 if (state.last_download_updates_result == SERVER_RETURN_MIGRATION_DONE || 96 state.commit_result == SERVER_RETURN_MIGRATION_DONE) { 97 return short_initial_backoff_; 98 } 99 100 // When the server tells us we have a conflict, then we should download the 101 // latest updates so we can see the conflict ourselves, resolve it locally, 102 // then try again to commit. Running another sync cycle will do all these 103 // things. There's no need to back off, we can do this immediately. 104 // 105 // TODO(sync): We shouldn't need to handle this in BackoffDelayProvider. 106 // There should be a way to deal with protocol errors before we get to this 107 // point. 108 if (state.commit_result == SERVER_RETURN_CONFLICT) { 109 return short_initial_backoff_; 110 } 111 112 return default_initial_backoff_; 113 } 114 115 } // namespace syncer 116