/** * Helper method for creating a `CompletableFuture` that is using the tracing helpers and has the CompletableFuture * wrapped in a `CircuitBreaker` * <p> * <pre> * AsyncNettyHelper.supplyAsync(() -> { * //do some work in a background thread * return VOID; * }, circuitBreaker, executor, ctx); * </pre> */ public static <U> CompletableFuture<U> supplyAsync(Supplier<U> f, CircuitBreaker<U> circuitBreaker, Executor executor, ChannelHandlerContext ctx) { return circuitBreaker.executeAsyncCall(() -> supplyAsync(f, executor, ctx)); }
/** * Helper method for creating a `CompletableFuture` that is using the tracing helpers and has the CompletableFuture * wrapped in a `CircuitBreaker` * <p> * <pre> * AsyncNettyHelper.supplyAsync(() -> { * //do some work in a background thread * return VOID; * }, circuitBreaker, executor, ctx); * </pre> */ public static <U> CompletableFuture<U> supplyAsync(Supplier<U> f, CircuitBreaker<U> circuitBreaker, Executor executor, ChannelHandlerContext ctx) { return circuitBreaker.executeAsyncCall(() -> supplyAsync(f, executor, ctx)); }
/** * Helper method for creating a `CompletableFuture` that is wrapped by a CircuitBreaker and * has the `Supplier` wrapped around a SubSpan. * <p> * You would prefer this method over {@link #supplyAsync(Supplier, Executor, ChannelHandlerContext)} or * {@link #supplyAsync(Supplier, CircuitBreaker, Executor, ChannelHandlerContext)} * when your `Supplier` has logic that makes an outbound/downstream call. This will net you distributed tracing logs. * <p> * An example would be using a client SDK that makes blocking HTTP calls. * <p> * The SubSpan purpose will be set to `CLIENT` as this is the typical use case when utilizing these helpers. * <p> * <pre> * AsyncNettyHelper.supplyAsync("someWorkToBeDone", () -> { * //do some work in a background thread * return VOID; * }, circuitBreaker, executor, ctx); * </pre> */ public static <U> CompletableFuture<U> supplyAsync(String subSpanName, Supplier<U> f, CircuitBreaker<U> circuitBreaker, Executor executor, ChannelHandlerContext ctx) { return circuitBreaker.executeAsyncCall(() -> supplyAsync(subSpanName, f, executor, ctx)); }
/** * Helper method for creating a `CompletableFuture` that is wrapped by a CircuitBreaker and * has the `Supplier` wrapped around a SubSpan. * <p> * You would prefer this method over {@link #supplyAsync(Supplier, Executor, ChannelHandlerContext)} or * {@link #supplyAsync(Supplier, CircuitBreaker, Executor, ChannelHandlerContext)} * when your `Supplier` has logic that makes an outbound/downstream call. This will net you distributed tracing logs. * <p> * An example would be using a client SDK that makes blocking HTTP calls. * <p> * The SubSpan purpose will be set to `CLIENT` as this is the typical use case when utilizing these helpers. * <p> * <pre> * AsyncNettyHelper.supplyAsync("someWorkToBeDone", () -> { * //do some work in a background thread * return VOID; * }, circuitBreaker, executor, ctx); * </pre> */ public static <U> CompletableFuture<U> supplyAsync(String subSpanName, Supplier<U> f, CircuitBreaker<U> circuitBreaker, Executor executor, ChannelHandlerContext ctx) { return circuitBreaker.executeAsyncCall(() -> supplyAsync(subSpanName, f, executor, ctx)); }
@Test public void executeAsyncCall_shouldReturnCompletableFuture() throws Exception { // given String expectedResult = UUID.randomUUID().toString(); ctxMock = TestUtil.mockChannelHandlerContextWithTraceInfo().mockContext; Span parentSpan = Tracer.getInstance().getCurrentSpan(); AtomicReference<Span> runningSpan = new AtomicReference<>(); // when CompletableFuture<String> completableFuture = AsyncNettyHelper.supplyAsync( () -> { runningSpan.set(Tracer.getInstance().getCurrentSpan()); return expectedResult; }, executor, ctxMock); // then assertThat(completableFuture.isCompletedExceptionally()).isFalse(); assertThat(completableFuture.get()).isEqualTo(expectedResult); // verify new span is not created, but existing span does successfully hop threads assertThat(runningSpan.get()).isEqualTo(parentSpan); }
@Test public void executeAsyncCall_shouldReturnCompletableFutureUsingCircuitBreaker() throws Exception { // given String expectedResult = UUID.randomUUID().toString(); ctxMock = TestUtil.mockChannelHandlerContextWithTraceInfo().mockContext; CircuitBreaker<String> circuitBreaker = spy(new CircuitBreakerImpl<>()); Span parentSpan = Tracer.getInstance().getCurrentSpan(); AtomicReference<Span> runningSpan = new AtomicReference<>(); // when CompletableFuture<String> circuitBreakerCompletableFuture = AsyncNettyHelper.supplyAsync( () -> { runningSpan.set(Tracer.getInstance().getCurrentSpan()); return expectedResult; }, circuitBreaker, executor, ctxMock); // then verify(circuitBreaker).executeAsyncCall(anyObject()); assertThat(circuitBreakerCompletableFuture.isCompletedExceptionally()).isFalse(); assertThat(circuitBreakerCompletableFuture.get()).isEqualTo(expectedResult); // verify new span is not created, but existing span does successfully hop threads assertThat(runningSpan.get()).isEqualTo(parentSpan); }
@Test public void executeAsyncCall_shouldReturnCompletableFutureUsingSpanName() throws Exception { // given String expectedResult = UUID.randomUUID().toString(); String expectedSpanName = "nonCircuitBreakerWithSpan"; ctxMock = TestUtil.mockChannelHandlerContextWithTraceInfo().mockContext; Span parentSpan = Tracer.getInstance().getCurrentSpan(); AtomicReference<Span> runningSpan = new AtomicReference<>(); // when CompletableFuture<String> completableFuture = AsyncNettyHelper.supplyAsync( expectedSpanName, () -> { runningSpan.set(Tracer.getInstance().getCurrentSpan()); return expectedResult; }, executor, ctxMock); // then assertThat(completableFuture.isCompletedExceptionally()).isFalse(); assertThat(completableFuture.get()).isEqualTo(expectedResult); // verify span is as expected assertThat(runningSpan.get().getParentSpanId()).isEqualTo(parentSpan.getSpanId()); assertThat(runningSpan.get().getSpanName()).isEqualTo(expectedSpanName); assertThat(runningSpan.get().getTraceId()).isEqualTo(parentSpan.getTraceId()); }
@Test public void executeAsyncCall_shouldReturnCompletableFutureUsingCircuitBreakerWithSpanName() throws Exception { // given String expectedResult = UUID.randomUUID().toString(); String expectedSpanName = "circuitBreakerWithSpan"; ctxMock = TestUtil.mockChannelHandlerContextWithTraceInfo().mockContext; Span parentSpan = Tracer.getInstance().getCurrentSpan(); CircuitBreaker<String> circuitBreaker = spy(new CircuitBreakerImpl<>()); AtomicReference<Span> runningSpan = new AtomicReference<>(); // when CompletableFuture<String> circuitBreakerFuture = AsyncNettyHelper.supplyAsync( expectedSpanName, () -> { runningSpan.set(Tracer.getInstance().getCurrentSpan()); return expectedResult; }, circuitBreaker, executor, ctxMock); // then assertThat(circuitBreakerFuture.isCompletedExceptionally()).isFalse(); assertThat(circuitBreakerFuture.get()).isEqualTo(expectedResult); verify(circuitBreaker).executeAsyncCall(anyObject()); // verify span is as expected assertThat(runningSpan.get().getParentSpanId()).isEqualTo(parentSpan.getSpanId()); assertThat(runningSpan.get().getSpanName()).isEqualTo(expectedSpanName); assertThat(runningSpan.get().getTraceId()).isEqualTo(parentSpan.getTraceId()); } }