Search in sources :

Example 1 with TestExecutor

use of io.servicetalk.concurrent.api.TestExecutor in project servicetalk by apple.

the class TimeoutPublisherTest method concurrentTimeoutInvocation.

@Test
void concurrentTimeoutInvocation() throws InterruptedException {
    CountDownLatch latch = new CountDownLatch(2);
    AtomicReference<Throwable> causeRef = new AtomicReference<>();
    // In order to simulate concurrent execution, we introduce an Executor that does not respect the delay for the
    // first timer schedule. Internally, we expect the TimeoutPublisher to reschedule the timer. For that we use
    // TestExecutor, which will allow us to advance the time and trigger the actual timeout, which will simulate
    // concurrent execution.
    toSource(publisher.timeout(10, MILLISECONDS, new Executor() {

        private final AtomicInteger timerCount = new AtomicInteger();

        @Override
        public Cancellable schedule(final Runnable task, final long delay, final TimeUnit unit) {
            int count = timerCount.incrementAndGet();
            if (count <= 2) {
                if (count == 1) {
                    try {
                        task.run();
                    } catch (Throwable cause) {
                        causeRef.compareAndSet(null, cause);
                        countDownToZero(latch);
                    }
                    latch.countDown();
                } else {
                    try {
                        try {
                            testExecutor.schedule(task, delay, unit);
                            testExecutor.advanceTimeBy(delay, unit);
                        } catch (Throwable cause) {
                            causeRef.compareAndSet(null, cause);
                            countDownToZero(latch);
                        }
                        latch.countDown();
                    } catch (Throwable cause) {
                        causeRef.compareAndSet(null, cause);
                        countDownToZero(latch);
                    }
                }
            }
            return IGNORE_CANCEL;
        }

        @Override
        public long currentTime(final TimeUnit unit) {
            return testExecutor.currentTime(unit);
        }

        @Override
        public Completable closeAsync() {
            throw new UnsupportedOperationException();
        }

        @Override
        public Completable onClose() {
            throw new UnsupportedOperationException();
        }

        @Override
        public Cancellable execute(final Runnable task) throws RejectedExecutionException {
            throw new UnsupportedOperationException();
        }
    })).subscribe(subscriber);
    latch.await();
    assertNull(causeRef.get());
    assertThat(subscriber.awaitOnError(), instanceOf(TimeoutException.class));
}
Also used : Completable(io.servicetalk.concurrent.api.Completable) Cancellable(io.servicetalk.concurrent.Cancellable) AtomicReference(java.util.concurrent.atomic.AtomicReference) CountDownLatch(java.util.concurrent.CountDownLatch) RejectedExecutionException(java.util.concurrent.RejectedExecutionException) Executor(io.servicetalk.concurrent.api.Executor) DelegatingExecutor(io.servicetalk.concurrent.api.DelegatingExecutor) TestExecutor(io.servicetalk.concurrent.api.TestExecutor) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) TimeUnit(java.util.concurrent.TimeUnit) TimeoutException(java.util.concurrent.TimeoutException) Test(org.junit.jupiter.api.Test) ParameterizedTest(org.junit.jupiter.params.ParameterizedTest)

Example 2 with TestExecutor

use of io.servicetalk.concurrent.api.TestExecutor in project servicetalk by apple.

the class TimeoutCompletableTest method defaultExecutorSubscribeTimeout.

@Test
@Disabled("Requires the notion of globally mocked time source using a TestExecutor")
void defaultExecutorSubscribeTimeout() {
    DelayedOnSubscribeCompletable delayedCompletable = new DelayedOnSubscribeCompletable();
    // Assume the timeout call is not under the user's control, e.g. comes from a library of Completable providers
    final Completable operationThatInternallyTimesOut = delayedCompletable.timeout(Duration.ofDays(1));
    TestExecutor testExecutor = new TestExecutor();
    // TODO(dariusz): Replace all executors created with the test instance
    // Executors.setFactory(AllExecutorFactory.create(() -> testExecutor));
    toSource(operationThatInternallyTimesOut).subscribe(listener);
    testExecutor.advanceTimeBy(1, DAYS);
    CompletableSource.Subscriber subscriber = delayedCompletable.subscriber;
    assertNotNull(subscriber);
    assertThat(listener.awaitOnError(), instanceOf(TimeoutException.class));
}
Also used : TestExecutor(io.servicetalk.concurrent.api.TestExecutor) Subscriber(io.servicetalk.concurrent.CompletableSource.Subscriber) Completable(io.servicetalk.concurrent.api.Completable) CompletableSource(io.servicetalk.concurrent.CompletableSource) TimeoutException(java.util.concurrent.TimeoutException) Test(org.junit.jupiter.api.Test) Disabled(org.junit.jupiter.api.Disabled)

Example 3 with TestExecutor

use of io.servicetalk.concurrent.api.TestExecutor in project servicetalk by apple.

the class RoundRobinLoadBalancerTest method hostUnhealthyDoesntRaceToRunHealthCheck.

// Concurrency test, run multiple times (at least 1000).
@Test
void hostUnhealthyDoesntRaceToRunHealthCheck() throws Exception {
    serviceDiscoveryPublisher.onComplete();
    final Single<TestLoadBalancedConnection> properConnection = newRealizedConnectionSingle("address-1");
    final int timeAdvancementsTillHealthy = 3;
    final UnhealthyHostConnectionFactory unhealthyHostConnectionFactory = new UnhealthyHostConnectionFactory("address-1", timeAdvancementsTillHealthy, properConnection);
    final DelegatingConnectionFactory connectionFactory = unhealthyHostConnectionFactory.createFactory();
    lb = defaultLb(connectionFactory);
    sendServiceDiscoveryEvents(upEvent("address-1"));
    // Imitate concurrency by running multiple threads attempting to establish connections.
    ExecutorService executor = Executors.newFixedThreadPool(3);
    try {
        final Runnable runnable = () -> assertThrows(ExecutionException.class, () -> lb.selectConnection(any()).toFuture().get());
        for (int i = 0; i < 1000; i++) {
            executor.submit(runnable);
        }
        // From test main thread, wait until the host becomes UNHEALTHY, which is apparent from
        // NoHostAvailableException being thrown from selection AFTER a health check was scheduled by any thread.
        final Executor executorForRetries = io.servicetalk.concurrent.api.Executors.newFixedSizeExecutor(1);
        try {
            awaitIndefinitely(lb.selectConnection(any()).retryWhen(retryWithConstantBackoffFullJitter((t) -> t instanceof DeliberateException || testExecutor.scheduledTasksPending() == 0, // try to prevent stack overflow
            Duration.ofMillis(30), executorForRetries)));
        } catch (Exception e) {
            assertThat(e.getCause(), instanceOf(NoAvailableHostException.class));
        } finally {
            executorForRetries.closeAsync().toFuture().get();
        }
        // is not selected. If our assumption doesn't hold, it means more than one health check was scheduled.
        for (int i = 0; i < timeAdvancementsTillHealthy - 1; ++i) {
            unhealthyHostConnectionFactory.advanceTime(testExecutor);
            // Assert still unhealthy
            Exception e = assertThrows(ExecutionException.class, () -> lb.selectConnection(any()).toFuture().get());
            assertThat(e.getCause(), instanceOf(NoAvailableHostException.class));
        }
    } finally {
        // Shutdown the concurrent validation of unhealthiness.
        executor.shutdownNow();
        executor.awaitTermination(10, SECONDS);
    }
    unhealthyHostConnectionFactory.advanceTime(testExecutor);
    final TestLoadBalancedConnection selectedConnection = lb.selectConnection(any()).toFuture().get();
    assertThat(selectedConnection, equalTo(properConnection.toFuture().get()));
}
Also used : Executor(io.servicetalk.concurrent.api.Executor) DelegatingExecutor(io.servicetalk.concurrent.api.DelegatingExecutor) TestExecutor(io.servicetalk.concurrent.api.TestExecutor) NoAvailableHostException(io.servicetalk.client.api.NoAvailableHostException) ExecutorService(java.util.concurrent.ExecutorService) DeliberateException(io.servicetalk.concurrent.internal.DeliberateException) ConnectionRejectedException(io.servicetalk.client.api.ConnectionRejectedException) DeliberateException(io.servicetalk.concurrent.internal.DeliberateException) NoSuchElementException(java.util.NoSuchElementException) NoAvailableHostException(io.servicetalk.client.api.NoAvailableHostException) ExecutionException(java.util.concurrent.ExecutionException) Test(org.junit.jupiter.api.Test)

Aggregations

TestExecutor (io.servicetalk.concurrent.api.TestExecutor)3 Test (org.junit.jupiter.api.Test)3 Completable (io.servicetalk.concurrent.api.Completable)2 DelegatingExecutor (io.servicetalk.concurrent.api.DelegatingExecutor)2 Executor (io.servicetalk.concurrent.api.Executor)2 TimeoutException (java.util.concurrent.TimeoutException)2 ConnectionRejectedException (io.servicetalk.client.api.ConnectionRejectedException)1 NoAvailableHostException (io.servicetalk.client.api.NoAvailableHostException)1 Cancellable (io.servicetalk.concurrent.Cancellable)1 CompletableSource (io.servicetalk.concurrent.CompletableSource)1 Subscriber (io.servicetalk.concurrent.CompletableSource.Subscriber)1 DeliberateException (io.servicetalk.concurrent.internal.DeliberateException)1 NoSuchElementException (java.util.NoSuchElementException)1 CountDownLatch (java.util.concurrent.CountDownLatch)1 ExecutionException (java.util.concurrent.ExecutionException)1 ExecutorService (java.util.concurrent.ExecutorService)1 RejectedExecutionException (java.util.concurrent.RejectedExecutionException)1 TimeUnit (java.util.concurrent.TimeUnit)1 AtomicInteger (java.util.concurrent.atomic.AtomicInteger)1 AtomicReference (java.util.concurrent.atomic.AtomicReference)1