Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / sync / engine / backoff_delay_provider.cc
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   // 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_;
104
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.
109   //
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
112   // point.
113   if (state.commit_result == SERVER_RETURN_CONFLICT)
114     return short_initial_backoff_;
115
116   return default_initial_backoff_;
117 }
118
119 }  // namespace syncer