/** * @param firestore The Firestore Database client. * @param query The query that is used to order the document snapshots returned by this watch. * @param target A Firestore 'Target' proto denoting the target to listen on. */ private Watch(FirestoreImpl firestore, Query query, Target target) { this.firestore = firestore; this.target = target; this.query = query; this.comparator = query.comparator(); this.backoff = new ExponentialRetryAlgorithm(RETRY_SETTINGS, CurrentMillisClock.getDefaultClock()); this.firestoreExecutor = firestore.getClient().getExecutor(); this.isActive = new AtomicBoolean(); this.nextAttempt = backoff.createFirstAttempt(); }
nextAttempt = backoff.createFirstAttempt();
/** * @param firestore The Firestore Database client. * @param query The query that is used to order the document snapshots returned by this watch. * @param target A Firestore 'Target' proto denoting the target to listen on. */ private Watch(FirestoreImpl firestore, Query query, Target target) { this.firestore = firestore; this.target = target; this.query = query; this.comparator = query.comparator(); this.backoff = new ExponentialRetryAlgorithm(RETRY_SETTINGS, CurrentMillisClock.getDefaultClock()); this.firestoreExecutor = firestore.getClient().getExecutor(); this.isActive = new AtomicBoolean(); this.nextAttempt = backoff.createFirstAttempt(); }
@Test public void testShouldRetryTrue() { TimedAttemptSettings attempt = algorithm.createFirstAttempt(); for (int i = 0; i < 2; i++) { attempt = algorithm.createNextAttempt(attempt); } assertTrue(algorithm.shouldRetry(attempt)); }
protected Long getNextBackoff() { if (currentBackoff == null) { // Historically, the client waited for "total timeout" after the first failure. For now, // that behavior is preserved, even though that's not the ideal. // // TODO: Think through retries, and create policy that works with the mental model most // users would have of relating to retries. That would likely involve updating some // default settings in addition to changing the algorithm. currentBackoff = exponentialRetryAlgorithm.createFirstAttempt(); } currentBackoff = exponentialRetryAlgorithm.createNextAttempt(currentBackoff); if (!exponentialRetryAlgorithm.shouldRetry(currentBackoff)) { // TODO: consider creating a subclass of exponentialRetryAlgorithm to encapsulate this logic long timeLeftNs = currentBackoff.getGlobalSettings().getTotalTimeout().toNanos() - (clock.nanoTime() - currentBackoff.getFirstAttemptStartTimeNanos()); long timeLeftMs = TimeUnit.NANOSECONDS.toMillis(timeLeftNs); if (timeLeftMs > currentBackoff.getGlobalSettings().getInitialRetryDelay().toMillis()) { // The backoff algorithm doesn't always wait until the timeout is achieved. Wait // one final time so that retries hit return timeLeftMs; } else { // Finish for real. return null; } } else { return currentBackoff.getRetryDelay().toMillis(); } }
@Test public void testShouldRetryFalseOnMaxAttempts() { TimedAttemptSettings attempt = algorithm.createFirstAttempt(); for (int i = 0; i < 6; i++) { assertTrue(algorithm.shouldRetry(attempt)); attempt = algorithm.createNextAttempt(attempt); } assertFalse(algorithm.shouldRetry(attempt)); }
@Test public void testShouldRetryFalseOnMaxTimeout() { TimedAttemptSettings attempt = algorithm.createFirstAttempt(); for (int i = 0; i < 4; i++) { assertTrue(algorithm.shouldRetry(attempt)); attempt = algorithm.createNextAttempt(attempt); clock.incrementNanoTime(Duration.ofMillis(50L).toNanos()); } assertFalse(algorithm.shouldRetry(attempt)); } }
nextAttempt = backoff.createFirstAttempt();
@Test public void testCreateFirstAttempt() { TimedAttemptSettings attempt = algorithm.createFirstAttempt(); // Checking only the most core values, to not make this test too implementation specific. assertEquals(0, attempt.getAttemptCount()); assertEquals(0, attempt.getOverallAttemptCount()); assertEquals(Duration.ZERO, attempt.getRetryDelay()); assertEquals(Duration.ZERO, attempt.getRandomizedRetryDelay()); assertEquals(Duration.ofMillis(1L), attempt.getRpcTimeout()); assertEquals(Duration.ZERO, attempt.getRetryDelay()); }
@Test public void testCreateNextAttempt() { TimedAttemptSettings firstAttempt = algorithm.createFirstAttempt(); TimedAttemptSettings secondAttempt = algorithm.createNextAttempt(firstAttempt); // Checking only the most core values, to not make this test too implementation specific. assertEquals(1, secondAttempt.getAttemptCount()); assertEquals(1, secondAttempt.getOverallAttemptCount()); assertEquals(Duration.ofMillis(1L), secondAttempt.getRetryDelay()); assertEquals(Duration.ofMillis(1L), secondAttempt.getRandomizedRetryDelay()); assertEquals(Duration.ofMillis(2L), secondAttempt.getRpcTimeout()); TimedAttemptSettings thirdAttempt = algorithm.createNextAttempt(secondAttempt); assertEquals(2, thirdAttempt.getAttemptCount()); assertEquals(Duration.ofMillis(2L), thirdAttempt.getRetryDelay()); assertEquals(Duration.ofMillis(2L), thirdAttempt.getRandomizedRetryDelay()); assertEquals(Duration.ofMillis(4L), thirdAttempt.getRpcTimeout()); }