@Override public void run() { try { if (ackHandler .totalExpiration .plusSeconds(messageDeadlineSeconds.get()) .isBefore(now())) { // Message expired while waiting. We don't extend these messages anymore, // so it was probably sent to someone else. Don't work on it. // Don't nack it either, because we'd be nacking someone else's message. ackHandler.forget(); return; } receiver.receiveMessage(message, consumer); } catch (Exception e) { response.setException(e); } } });
@InternalApi void extendDeadlines() { int extendSeconds = getMessageDeadlineSeconds(); List<PendingModifyAckDeadline> modacks = new ArrayList<>(); PendingModifyAckDeadline modack = new PendingModifyAckDeadline(extendSeconds); Instant now = now(); Instant extendTo = now.plusSeconds(extendSeconds); for (Map.Entry<String, AckHandler> entry : pendingMessages.entrySet()) { String ackId = entry.getKey(); Instant totalExpiration = entry.getValue().totalExpiration; if (totalExpiration.isAfter(extendTo)) { modack.ackIds.add(ackId); continue; } // forget removes from pendingMessages; this is OK, concurrent maps can // handle concurrent iterations and modifications. entry.getValue().forget(); if (totalExpiration.isAfter(now)) { int sec = Math.max(1, (int) now.until(totalExpiration, ChronoUnit.SECONDS)); modacks.add(new PendingModifyAckDeadline(sec, ackId)); } } logger.log(Level.FINER, "Sending {0} modacks", modack.ackIds.size() + modacks.size()); modacks.add(modack); List<String> acksToSend = Collections.emptyList(); ackProcessor.sendAckOperations(acksToSend, modacks); }
/** * Returns a copy of this instant with the specified duration in seconds subtracted. * <p> * This instance is immutable and unaffected by this method call. * * @param secondsToSubtract the seconds to subtract, positive or negative * @return an {@code Instant} based on this instant with the specified seconds subtracted, not null * @throws DateTimeException if the result exceeds the maximum or minimum instant * @throws ArithmeticException if numeric overflow occurs */ public Instant minusSeconds(long secondsToSubtract) { if (secondsToSubtract == Long.MIN_VALUE) { return plusSeconds(Long.MAX_VALUE).plusSeconds(1); } return plusSeconds(-secondsToSubtract); }
/** * Returns a copy of this instant with the specified duration in seconds subtracted. * <p> * This instance is immutable and unaffected by this method call. * * @param secondsToSubtract the seconds to subtract, positive or negative * @return an {@code Instant} based on this instant with the specified seconds subtracted, not null * @throws DateTimeException if the result exceeds the maximum or minimum instant * @throws ArithmeticException if numeric overflow occurs */ public Instant minusSeconds(long secondsToSubtract) { if (secondsToSubtract == Long.MIN_VALUE) { return plusSeconds(Long.MAX_VALUE).plusSeconds(1); } return plusSeconds(-secondsToSubtract); }
/** * {@inheritDoc} * @throws DateTimeException {@inheritDoc} * @throws ArithmeticException {@inheritDoc} */ @Override public Instant plus(long amountToAdd, TemporalUnit unit) { if (unit instanceof ChronoUnit) { switch ((ChronoUnit) unit) { case NANOS: return plusNanos(amountToAdd); case MICROS: return plus(amountToAdd / 1000000, (amountToAdd % 1000000) * 1000); case MILLIS: return plusMillis(amountToAdd); case SECONDS: return plusSeconds(amountToAdd); case MINUTES: return plusSeconds(Jdk8Methods.safeMultiply(amountToAdd, SECONDS_PER_MINUTE)); case HOURS: return plusSeconds(Jdk8Methods.safeMultiply(amountToAdd, SECONDS_PER_HOUR)); case HALF_DAYS: return plusSeconds(Jdk8Methods.safeMultiply(amountToAdd, SECONDS_PER_DAY / 2)); case DAYS: return plusSeconds(Jdk8Methods.safeMultiply(amountToAdd, SECONDS_PER_DAY)); } throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit); } return unit.addTo(this, amountToAdd); }
/** * {@inheritDoc} * @throws DateTimeException {@inheritDoc} * @throws ArithmeticException {@inheritDoc} */ @Override public Instant plus(long amountToAdd, TemporalUnit unit) { if (unit instanceof ChronoUnit) { switch ((ChronoUnit) unit) { case NANOS: return plusNanos(amountToAdd); case MICROS: return plus(amountToAdd / 1000000, (amountToAdd % 1000000) * 1000); case MILLIS: return plusMillis(amountToAdd); case SECONDS: return plusSeconds(amountToAdd); case MINUTES: return plusSeconds(Jdk8Methods.safeMultiply(amountToAdd, SECONDS_PER_MINUTE)); case HOURS: return plusSeconds(Jdk8Methods.safeMultiply(amountToAdd, SECONDS_PER_HOUR)); case HALF_DAYS: return plusSeconds(Jdk8Methods.safeMultiply(amountToAdd, SECONDS_PER_DAY / 2)); case DAYS: return plusSeconds(Jdk8Methods.safeMultiply(amountToAdd, SECONDS_PER_DAY)); } throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit); } return unit.addTo(this, amountToAdd); }
@Test public void testTimeoutAfterDeadline() { HttpJsonChannel mockChannel = Mockito.mock(HttpJsonChannel.class); String expectedRequest = "fake"; HttpJsonDirectCallable<String, String> callable = new HttpJsonDirectCallable<>(API_DESCRIPTOR); // Mock the channel that captures the call options ArgumentCaptor<HttpJsonCallOptions> capturedCallOptions = ArgumentCaptor.forClass(HttpJsonCallOptions.class); Mockito.when( mockChannel.issueFutureUnaryCall( capturedCallOptions.capture(), Mockito.anyString(), Mockito.any(ApiMethodDescriptor.class))) .thenReturn(SettableApiFuture.create()); // Compose the call context Instant priorDeadline = Instant.now().plusSeconds(5); Duration timeout = Duration.ofSeconds(10); HttpJsonCallContext callContext = HttpJsonCallContext.createDefault() .withChannel(mockChannel) .withDeadline(priorDeadline) .withTimeout(timeout); callable.futureCall(expectedRequest, callContext); // Verify that the timeout was ignored assertThat(capturedCallOptions.getValue().getDeadline()).isEqualTo(priorDeadline); }
@Test public void testTimeoutBeforeDeadline() { HttpJsonChannel mockChannel = Mockito.mock(HttpJsonChannel.class); String expectedRequest = "fake"; HttpJsonDirectCallable<String, String> callable = new HttpJsonDirectCallable<>(API_DESCRIPTOR); // Mock the channel that captures the call options ArgumentCaptor<HttpJsonCallOptions> capturedCallOptions = ArgumentCaptor.forClass(HttpJsonCallOptions.class); Mockito.when( mockChannel.issueFutureUnaryCall( capturedCallOptions.capture(), Mockito.anyString(), Mockito.any(ApiMethodDescriptor.class))) .thenReturn(SettableApiFuture.create()); // Compose the call context Duration timeout = Duration.ofSeconds(10); Instant subsequentDeadline = Instant.now().plusSeconds(15); Instant minExpectedDeadline = Instant.now().plus(timeout); HttpJsonCallContext callContext = HttpJsonCallContext.createDefault() .withChannel(mockChannel) .withDeadline(subsequentDeadline) .withTimeout(timeout); callable.futureCall(expectedRequest, callContext); Instant maxExpectedDeadline = Instant.now().plus(timeout); // Verify that the timeout was converted into a deadline assertThat(capturedCallOptions.getValue().getDeadline()).isAtLeast(minExpectedDeadline); assertThat(capturedCallOptions.getValue().getDeadline()).isAtMost(maxExpectedDeadline); }