Skip to content

Commit 3d9b1d3

Browse files
committed
store: Report if transient polling errors persist.
Signed-off-by: Zixuan James Li <[email protected]>
1 parent 04dcda4 commit 3d9b1d3

File tree

2 files changed

+34
-17
lines changed

2 files changed

+34
-17
lines changed

lib/model/store.dart

+7-1
Original file line numberDiff line numberDiff line change
@@ -764,6 +764,8 @@ class UpdateMachine {
764764

765765
void poll() async {
766766
BackoffMachine? backoffMachine;
767+
int accumlatedTransientFailureCount = 0;
768+
const transientFailureCountNotifyThreshold = 5;
767769

768770
while (true) {
769771
if (_debugLoopSignal != null) {
@@ -791,8 +793,11 @@ class UpdateMachine {
791793
case Server5xxException() || NetworkException():
792794
assert(debugLog('Transient error polling event queue for $store: $e\n'
793795
'Backing off, then will retry…'));
794-
// TODO tell user if transient polling errors persist
795796
// TODO reset to short backoff eventually
797+
accumlatedTransientFailureCount++;
798+
if (accumlatedTransientFailureCount > transientFailureCountNotifyThreshold) {
799+
reportErrorToUserBriefly('Failed to reach server. Will retry: $e');
800+
}
796801
await (backoffMachine ??= BackoffMachine()).wait();
797802
assert(debugLog('… Backoff wait complete, retrying poll.'));
798803
continue;
@@ -825,6 +830,7 @@ class UpdateMachine {
825830
// and failures, the successes themselves should space out the requests.
826831
backoffMachine = null;
827832
store.isLoading = false;
833+
accumlatedTransientFailureCount = 0;
828834

829835
final events = result.events;
830836
for (final event in events) {

test/model/store_test.dart

+27-16
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,7 @@ void main() {
430430

431431
void checkRetry(String description, void Function() prepareError, {
432432
String? errorMessage,
433+
int failureCountNofityThreshold = 0,
433434
}) {
434435
test(description, () {
435436
awaitFakeAsync((async) async {
@@ -438,18 +439,24 @@ void main() {
438439
updateMachine.poll();
439440
check(async.pendingTimers).length.equals(0);
440441

441-
// Make the request, inducing an error in it.
442-
prepareError();
443-
updateMachine.debugAdvanceLoop();
444-
check(debugLastReportedError).isNull();
445-
async.elapse(Duration.zero);
446-
if (errorMessage == null) {
447-
check(takeLastReportedError()).isNull();
448-
} else {
449-
check(takeLastReportedError()).isNotNull().contains(errorMessage);
442+
for (int i = 0; i < failureCountNofityThreshold + 1; i++) {
443+
// Make the request, inducing an error in it.
444+
prepareError();
445+
if (i > 0) {
446+
// End polling backoff from the previous iteration.
447+
async.flushTimers();
448+
}
449+
updateMachine.debugAdvanceLoop();
450+
check(debugLastReportedError).isNull();
451+
async.elapse(Duration.zero);
452+
if (i < failureCountNofityThreshold || errorMessage == null) {
453+
check(takeLastReportedError()).isNull();
454+
} else {
455+
check(takeLastReportedError()).isNotNull().contains(errorMessage);
456+
}
457+
checkLastRequest(lastEventId: 1);
458+
check(store).isLoading.isTrue();
450459
}
451-
checkLastRequest(lastEventId: 1);
452-
check(store).isLoading.isTrue();
453460

454461
// Polling doesn't resume immediately; there's a timer.
455462
check(async.pendingTimers).length.equals(1);
@@ -471,11 +478,15 @@ void main() {
471478
}
472479

473480
group('retries', () {
474-
checkRetry('retries on Server5xxException',
475-
() => connection.prepare(httpStatus: 500, body: 'splat'));
476-
477-
checkRetry('retries on NetworkException',
478-
() => connection.prepare(exception: Exception("failed")));
481+
checkRetry('too many retries on Server5xxException',
482+
() => connection.prepare(httpStatus: 500, body: 'splat'),
483+
errorMessage: 'Failed to reach server. Will retry',
484+
failureCountNofityThreshold: 5);
485+
486+
checkRetry('too many retries on NetworkException',
487+
() => connection.prepare(exception: Exception("failed")),
488+
errorMessage: 'Failed to reach server. Will retry',
489+
failureCountNofityThreshold: 5);
479490

480491
checkRetry('retries on ZulipApiException',
481492
() => connection.prepare(httpStatus: 400, json: {

0 commit comments

Comments
 (0)