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.
5 #include "sync/engine/backoff_delay_provider.h"
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"
12 using base::TimeDelta
;
17 BackoffDelayProvider
* BackoffDelayProvider::FromDefaults() {
18 return new BackoffDelayProvider(
19 TimeDelta::FromSeconds(kInitialBackoffRetrySeconds
),
20 TimeDelta::FromSeconds(kInitialBackoffImmediateRetrySeconds
));
24 BackoffDelayProvider
* BackoffDelayProvider::WithShortInitialRetryOverride() {
25 return new BackoffDelayProvider(
26 TimeDelta::FromSeconds(kInitialBackoffShortRetrySeconds
),
27 TimeDelta::FromSeconds(kInitialBackoffImmediateRetrySeconds
));
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
) {
37 BackoffDelayProvider::~BackoffDelayProvider() {}
39 TimeDelta
BackoffDelayProvider::GetDelay(const base::TimeDelta
& last_delay
) {
40 if (last_delay
.InSeconds() >= kMaxBackoffSeconds
)
41 return TimeDelta::FromSeconds(kMaxBackoffSeconds
);
43 // This calculates approx. base_delay_seconds * 2 +/- base_delay_seconds / 2
45 std::max(static_cast<int64
>(1),
46 last_delay
.InSeconds() * kBackoffRandomizationFactor
);
48 // Flip a coin to randomize backoff interval by +/- 50%.
49 int rand_sign
= base::RandInt(0, 1) * 2 - 1;
51 // Truncation is adequate for rounding here.
52 backoff_s
= backoff_s
+
53 (rand_sign
* (last_delay
.InSeconds() / kBackoffRandomizationFactor
));
55 // Cap the backoff interval.
56 backoff_s
= std::max(static_cast<int64
>(1),
57 std::min(backoff_s
, kMaxBackoffSeconds
));
59 return TimeDelta::FromSeconds(backoff_s
);
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.
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_
;
87 if (SyncerErrorIsError(state
.last_get_key_result
))
88 return default_initial_backoff_
;
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_
;
100 // If a datatype decides the GetUpdates must be retried (e.g. because the
101 // context has been updated since the request), use the short delay.
102 if (state
.last_download_updates_result
== DATATYPE_TRIGGERED_RETRY
)
103 return short_initial_backoff_
;
105 // When the server tells us we have a conflict, then we should download the
106 // latest updates so we can see the conflict ourselves, resolve it locally,
107 // then try again to commit. Running another sync cycle will do all these
108 // things. There's no need to back off, we can do this immediately.
110 // TODO(sync): We shouldn't need to handle this in BackoffDelayProvider.
111 // There should be a way to deal with protocol errors before we get to this
113 if (state
.commit_result
== SERVER_RETURN_CONFLICT
)
114 return short_initial_backoff_
;
116 return default_initial_backoff_
;
119 } // namespace syncer