17
17
import static com .google .firebase .firestore .util .Assert .hardAssert ;
18
18
19
19
import com .google .firebase .firestore .core .OnlineState ;
20
- import com .google .firebase .firestore .util .AsyncQueue ;
21
20
import com .google .firebase .firestore .util .AsyncQueue .DelayedTask ;
22
- import com .google .firebase .firestore .util .AsyncQueue .TimerId ;
23
21
import com .google .firebase .firestore .util .Logger ;
24
22
import io .grpc .Status ;
25
23
import java .util .Locale ;
30
28
* heuristics.
31
29
*
32
30
* <p>In particular, when the client is trying to connect to the backend, we allow up to
33
- * MAX_WATCH_STREAM_FAILURES within ONLINE_STATE_TIMEOUT_MS for a connection to succeed. If we have
34
- * too many failures or the timeout elapses, then we set the OnlineState to OFFLINE, and the client
35
- * will behave as if it is offline (get() calls will return cached data, etc.).
31
+ * MAX_WATCH_STREAM_FAILURES within CONNECTIVITY_ATTEMPT_TIMEOUT_MS for a connection to succeed. If
32
+ * we have too many failures or the timeout elapses, then we set the OnlineState to OFFLINE, and the
33
+ * client will behave as if it is offline (get() calls will return cached data, etc.).
36
34
*/
37
35
class OnlineStateTracker {
38
36
@@ -53,8 +51,9 @@ interface OnlineStateCallback {
53
51
54
52
// To deal with stream attempts that don't succeed or fail in a timely manner, we have a
55
53
// timeout for OnlineState to reach ONLINE or OFFLINE. If the timeout is reached, we transition
56
- // to OFFLINE rather than waiting indefinitely.
57
- private static final int ONLINE_STATE_TIMEOUT_MS = 10 * 1000 ;
54
+ // to OFFLINE rather than waiting indefinitely. This timeout is also used when attempting to
55
+ // establish a connection when in an OFFLINE state.
56
+ static final int CONNECTIVITY_ATTEMPT_TIMEOUT_MS = 5 * 1000 ;
58
57
59
58
/** The log tag to use for this class. */
60
59
private static final String LOG_TAG = "OnlineStateTracker" ;
@@ -66,23 +65,19 @@ interface OnlineStateCallback {
66
65
// MAX_WATCH_STREAM_FAILURES, we'll revert to OnlineState.OFFLINE.
67
66
private int watchStreamFailures ;
68
67
69
- // A timer that elapses after ONLINE_STATE_TIMEOUT_MS , at which point we transition from
68
+ // A timer that elapses after CONNECTIVITY_ATTEMPT_TIMEOUT_MS , at which point we transition from
70
69
// OnlineState.UNKNOWN to OFFLINE without waiting for the stream to actually fail
71
70
// (MAX_WATCH_STREAM_FAILURES times).
72
- private DelayedTask onlineStateTimer ;
71
+ private DelayedTask connectivityAttemptTimer ;
73
72
74
73
// Whether the client should log a warning message if it fails to connect to the backend
75
74
// (initially true, cleared after a successful stream, or if we've logged the message already).
76
75
private boolean shouldWarnClientIsOffline ;
77
76
78
- // The AsyncQueue to use for running timers (and calling OnlineStateCallback methods).
79
- private final AsyncQueue workerQueue ;
80
-
81
77
// The callback to notify on OnlineState changes.
82
78
private final OnlineStateCallback onlineStateCallback ;
83
79
84
- OnlineStateTracker (AsyncQueue workerQueue , OnlineStateCallback onlineStateCallback ) {
85
- this .workerQueue = workerQueue ;
80
+ OnlineStateTracker (OnlineStateCallback onlineStateCallback ) {
86
81
this .onlineStateCallback = onlineStateCallback ;
87
82
state = OnlineState .UNKNOWN ;
88
83
shouldWarnClientIsOffline = true ;
@@ -92,33 +87,18 @@ interface OnlineStateCallback {
92
87
* Called by RemoteStore when a watch stream is started (including on each backoff attempt).
93
88
*
94
89
* <p>If this is the first attempt, it sets the OnlineState to UNKNOWN and starts the
95
- * onlineStateTimer .
90
+ * setConnectivityAttemptTimer .
96
91
*/
97
- void handleWatchStreamStart () {
98
- if (watchStreamFailures == 0 ) {
99
- setAndBroadcastState (OnlineState .UNKNOWN );
92
+ void handleWatchStreamConnectionFailed () {
93
+ logClientOfflineWarningIfNecessary (
94
+ String .format (
95
+ Locale .ENGLISH ,
96
+ "Backend didn't respond within %d seconds\n " ,
97
+ CONNECTIVITY_ATTEMPT_TIMEOUT_MS / 1000 ));
98
+ setAndBroadcastState (OnlineState .OFFLINE );
100
99
101
- hardAssert (onlineStateTimer == null , "onlineStateTimer shouldn't be started yet" );
102
- onlineStateTimer =
103
- workerQueue .enqueueAfterDelay (
104
- TimerId .ONLINE_STATE_TIMEOUT ,
105
- ONLINE_STATE_TIMEOUT_MS ,
106
- () -> {
107
- onlineStateTimer = null ;
108
- hardAssert (
109
- state == OnlineState .UNKNOWN ,
110
- "Timer should be canceled if we transitioned to a different state." );
111
- logClientOfflineWarningIfNecessary (
112
- String .format (
113
- Locale .ENGLISH ,
114
- "Backend didn't respond within %d seconds\n " ,
115
- ONLINE_STATE_TIMEOUT_MS / 1000 ));
116
- setAndBroadcastState (OnlineState .OFFLINE );
117
-
118
- // NOTE: handleWatchStreamFailure() will continue to increment watchStreamFailures
119
- // even though we are already marked OFFLINE but this is non-harmful.
120
- });
121
- }
100
+ // NOTE: handleWatchStreamFailure() will continue to increment watchStreamFailures
101
+ // even though we are already marked OFFLINE but this is non-harmful.
122
102
}
123
103
124
104
/**
@@ -135,11 +115,11 @@ void handleWatchStreamFailure(Status status) {
135
115
// To get to OnlineState.ONLINE, updateState() must have been called which would have reset
136
116
// our heuristics.
137
117
hardAssert (this .watchStreamFailures == 0 , "watchStreamFailures must be 0" );
138
- hardAssert (this .onlineStateTimer == null , "onlineStateTimer must be null" );
118
+ hardAssert (this .connectivityAttemptTimer == null , "setConnectivityAttemptTimer must be null" );
139
119
} else {
140
120
watchStreamFailures ++;
141
121
if (watchStreamFailures >= MAX_WATCH_STREAM_FAILURES ) {
142
- clearOnlineStateTimer ();
122
+ clearConnectivityAttemptTimer ();
143
123
logClientOfflineWarningIfNecessary (
144
124
String .format (
145
125
Locale .ENGLISH ,
@@ -158,7 +138,7 @@ void handleWatchStreamFailure(Status status) {
158
138
* it must not be used in place of handleWatchStreamStart() and handleWatchStreamFailure().
159
139
*/
160
140
void updateState (OnlineState newState ) {
161
- clearOnlineStateTimer ();
141
+ clearConnectivityAttemptTimer ();
162
142
watchStreamFailures = 0 ;
163
143
164
144
if (newState == OnlineState .ONLINE ) {
@@ -171,6 +151,7 @@ void updateState(OnlineState newState) {
171
151
}
172
152
173
153
private void setAndBroadcastState (OnlineState newState ) {
154
+ Logger .debug ("OST" , "BCHEN: state set to " + newState );
174
155
if (newState != state ) {
175
156
state = newState ;
176
157
onlineStateCallback .handleOnlineStateChange (newState );
@@ -194,10 +175,21 @@ private void logClientOfflineWarningIfNecessary(String reason) {
194
175
}
195
176
}
196
177
197
- private void clearOnlineStateTimer () {
198
- if (onlineStateTimer != null ) {
199
- onlineStateTimer .cancel ();
200
- onlineStateTimer = null ;
178
+ /** Clears the connectivity attempt timer that has been passed in. */
179
+ void clearConnectivityAttemptTimer () {
180
+ if (connectivityAttemptTimer != null ) {
181
+ connectivityAttemptTimer .cancel ();
182
+ connectivityAttemptTimer = null ;
201
183
}
202
184
}
185
+
186
+ /** Returns the number of times the WatchStream has tried unsuccessfully to start. */
187
+ int getWatchStreamFailures () {
188
+ return watchStreamFailures ;
189
+ }
190
+
191
+ /** Set the connectivity attempt timer to track. */
192
+ void setConnectivityAttemptTimer (DelayedTask connectivityAttemptTimer ) {
193
+ this .connectivityAttemptTimer = connectivityAttemptTimer ;
194
+ }
203
195
}
0 commit comments