szetszwo commented on code in PR #1478:
URL: https://github.com/apache/ratis/pull/1478#discussion_r3405206235
##########
ratis-common/src/main/java/org/apache/ratis/trace/TraceUtils.java:
##########
@@ -67,130 +46,29 @@ public static void setTracerWhenEnabled(RaftProperties
properties) {
}
/**
- * Enables or disables the tracer without reading {@link RaftProperties}.
Intended for tests and
+ * Enables or disables tracing without reading {@link RaftProperties}.
Intended for tests and
* simple toggles; production code should prefer {@link
#setTracerWhenEnabled(RaftProperties)}.
*
- * @param enabled when true, lazily obtains the OpenTelemetry tracer; when
false, clears it
+ * @param enabled when true, enables the OpenTelemetry provider; when false,
clears it
*/
public static void setTracerWhenEnabled(boolean enabled) {
- if (enabled) {
- TRACER.updateAndGet(previous -> previous != null ? previous
- : GlobalOpenTelemetry.getTracer("org.apache.ratis",
VersionInfo.getSoftwareInfoVersion()));
- } else {
- TRACER.set(null);
- }
+ PROVIDER.set(enabled ? tryNewOpenTelemetryTraceProvider() :
NoOpTraceProvider.INSTANCE);
}
public static boolean isEnabled() {
- return TRACER.get() != null;
+ return getProvider().isEnabled();
}
- /**
- * Traces an asynchronous operation represented by a {@link
CompletableFuture}. The returned future
- * completes with the same outcome as the supplied future; the span is ended
when that future
- * completes.
- */
- static <T, THROWABLE extends Throwable> CompletableFuture<T>
traceAsyncMethod(
- CheckedSupplier<CompletableFuture<T>, THROWABLE> action, Supplier<Span>
spanSupplier) throws THROWABLE {
- final Span span = spanSupplier.get();
- try (Scope ignored = span.makeCurrent()) {
- final CompletableFuture<T> future;
- try {
- future = action.get();
- } catch (RuntimeException | Error e) {
- setError(span, e);
- span.end();
- throw e;
- } catch (Throwable t) {
- setError(span, t);
- span.end();
- throw JavaUtils.<THROWABLE>cast(t);
- }
- endSpan(future, span);
- return future;
- }
+ static TraceProvider getProvider() {
+ return PROVIDER.get();
}
- private static void endSpan(CompletableFuture<?> future, Span span) {
- if (span == null) {
- LOG.debug("Span is null, cannot trace the future {}", future);
- return;
+ private static TraceProvider tryNewOpenTelemetryTraceProvider() {
+ try {
+ return new OpenTelemetryTraceProvider();
+ } catch (LinkageError e) {
+ LOG.warn("OpenTelemetry tracing is enabled but OpenTelemetry is not
available; tracing is disabled", e);
Review Comment:
Let's throw an exception instead of fallback:
```java
private static TraceProvider newOpenTelemetryTraceProvider() {
try {
return new OpenTelemetryTraceProvider();
} catch (Throwable e) {
throw new IllegalStateException("OpenTelemetry tracing is enabled but
OpenTelemetry is not available; tracing is disabled", e);
}
}
```
##########
ratis-common/src/main/java/org/apache/ratis/trace/TraceUtils.java:
##########
@@ -67,130 +46,29 @@ public static void setTracerWhenEnabled(RaftProperties
properties) {
}
/**
- * Enables or disables the tracer without reading {@link RaftProperties}.
Intended for tests and
+ * Enables or disables tracing without reading {@link RaftProperties}.
Intended for tests and
* simple toggles; production code should prefer {@link
#setTracerWhenEnabled(RaftProperties)}.
*
- * @param enabled when true, lazily obtains the OpenTelemetry tracer; when
false, clears it
+ * @param enabled when true, enables the OpenTelemetry provider; when false,
clears it
*/
public static void setTracerWhenEnabled(boolean enabled) {
- if (enabled) {
- TRACER.updateAndGet(previous -> previous != null ? previous
- : GlobalOpenTelemetry.getTracer("org.apache.ratis",
VersionInfo.getSoftwareInfoVersion()));
- } else {
- TRACER.set(null);
- }
+ PROVIDER.set(enabled ? tryNewOpenTelemetryTraceProvider() :
NoOpTraceProvider.INSTANCE);
}
public static boolean isEnabled() {
- return TRACER.get() != null;
+ return getProvider().isEnabled();
}
Review Comment:
Let's check the provider class:
```java
public static boolean isEnabled() {
return !(getProvider() instanceof NoOpTraceProvider);
}
```
##########
ratis-common/src/main/java/org/apache/ratis/trace/OpenTelemetryTraceProvider.java:
##########
@@ -0,0 +1,177 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ratis.trace;
+
+import io.opentelemetry.api.trace.Span;
+import io.opentelemetry.api.trace.SpanKind;
+import io.opentelemetry.api.trace.StatusCode;
+import io.opentelemetry.api.trace.Tracer;
+import io.opentelemetry.context.Context;
+import io.opentelemetry.context.Scope;
+import org.apache.ratis.proto.RaftProtos.AppendEntriesRequestProto;
+import org.apache.ratis.proto.RaftProtos.RaftRpcRequestProto;
+import org.apache.ratis.proto.RaftProtos.SpanContextProto;
+import org.apache.ratis.protocol.RaftClientRequest;
+import org.apache.ratis.protocol.RaftPeerId;
+import org.apache.ratis.util.JavaUtils;
+import org.apache.ratis.util.Preconditions;
+import org.apache.ratis.util.function.CheckedSupplier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.BiConsumer;
+import java.util.function.Supplier;
+
+final class OpenTelemetryTraceProvider implements TraceProvider {
+ private static final Logger LOG =
LoggerFactory.getLogger(OpenTelemetryTraceProvider.class);
+ private static final String LEADER = "LEADER";
+
+ private final Tracer tracer = OpenTelemetryTraceUtils.getGlobalTracer();
Review Comment:
Check null for tracer:
```java
private final Tracer tracer = Objects.requireNonNull(
GlobalOpenTelemetry.getTracer("org.apache.ratis",
VersionInfo.getSoftwareInfoVersion()),
"tracer == null");
```
##########
ratis-common/src/main/java/org/apache/ratis/trace/TraceProvider.java:
##########
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ratis.trace;
+
+import org.apache.ratis.proto.RaftProtos.AppendEntriesRequestProto;
+import org.apache.ratis.protocol.RaftClientRequest;
+import org.apache.ratis.protocol.RaftPeerId;
+import org.apache.ratis.util.function.CheckedSupplier;
+
+import java.io.IOException;
+import java.util.concurrent.CompletableFuture;
+
+interface TraceProvider {
+ boolean isEnabled();
Review Comment:
Remove isEnabled().
##########
ratis-common/src/main/java/org/apache/ratis/trace/OpenTelemetryTraceUtils.java:
##########
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ratis.trace;
+
+import io.opentelemetry.api.GlobalOpenTelemetry;
+import io.opentelemetry.api.trace.Tracer;
+import io.opentelemetry.context.Context;
+import io.opentelemetry.context.propagation.TextMapGetter;
+import io.opentelemetry.context.propagation.TextMapPropagator;
+import org.apache.ratis.proto.RaftProtos.SpanContextProto;
+import org.apache.ratis.util.VersionInfo;
+
+import java.util.Map;
+import java.util.Optional;
+import java.util.TreeMap;
+
+/** OpenTelemetry-specific helpers. Callers using this class must provide
OpenTelemetry jars. */
+public final class OpenTelemetryTraceUtils {
+ private OpenTelemetryTraceUtils() {
+ }
+
+ public static Tracer getGlobalTracer() {
+ return GlobalOpenTelemetry.getTracer("org.apache.ratis",
VersionInfo.getSoftwareInfoVersion());
+ }
Review Comment:
Let's remove it since it is only used once.
##########
ratis-common/src/main/java/org/apache/ratis/trace/OpenTelemetryTraceProvider.java:
##########
@@ -0,0 +1,177 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ratis.trace;
Review Comment:
Move it to a new package:
```java
package org.apache.ratis.trace.opentelemetry;
```
##########
ratis-common/src/main/java/org/apache/ratis/trace/OpenTelemetryTraceUtils.java:
##########
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ratis.trace;
Review Comment:
Move it to a new package:
```java
package org.apache.ratis.trace.opentelemetry;
```
##########
ratis-common/src/main/java/org/apache/ratis/trace/TraceUtils.java:
##########
@@ -67,130 +46,29 @@ public static void setTracerWhenEnabled(RaftProperties
properties) {
}
/**
- * Enables or disables the tracer without reading {@link RaftProperties}.
Intended for tests and
+ * Enables or disables tracing without reading {@link RaftProperties}.
Intended for tests and
* simple toggles; production code should prefer {@link
#setTracerWhenEnabled(RaftProperties)}.
*
- * @param enabled when true, lazily obtains the OpenTelemetry tracer; when
false, clears it
+ * @param enabled when true, enables the OpenTelemetry provider; when false,
clears it
*/
public static void setTracerWhenEnabled(boolean enabled) {
- if (enabled) {
- TRACER.updateAndGet(previous -> previous != null ? previous
- : GlobalOpenTelemetry.getTracer("org.apache.ratis",
VersionInfo.getSoftwareInfoVersion()));
- } else {
- TRACER.set(null);
- }
+ PROVIDER.set(enabled ? tryNewOpenTelemetryTraceProvider() :
NoOpTraceProvider.INSTANCE);
}
public static boolean isEnabled() {
- return TRACER.get() != null;
+ return getProvider().isEnabled();
}
- /**
- * Traces an asynchronous operation represented by a {@link
CompletableFuture}. The returned future
- * completes with the same outcome as the supplied future; the span is ended
when that future
- * completes.
- */
- static <T, THROWABLE extends Throwable> CompletableFuture<T>
traceAsyncMethod(
- CheckedSupplier<CompletableFuture<T>, THROWABLE> action, Supplier<Span>
spanSupplier) throws THROWABLE {
- final Span span = spanSupplier.get();
- try (Scope ignored = span.makeCurrent()) {
- final CompletableFuture<T> future;
- try {
- future = action.get();
- } catch (RuntimeException | Error e) {
- setError(span, e);
- span.end();
- throw e;
- } catch (Throwable t) {
- setError(span, t);
- span.end();
- throw JavaUtils.<THROWABLE>cast(t);
- }
- endSpan(future, span);
- return future;
- }
+ static TraceProvider getProvider() {
+ return PROVIDER.get();
}
- private static void endSpan(CompletableFuture<?> future, Span span) {
- if (span == null) {
- LOG.debug("Span is null, cannot trace the future {}", future);
- return;
+ private static TraceProvider tryNewOpenTelemetryTraceProvider() {
+ try {
+ return new OpenTelemetryTraceProvider();
+ } catch (LinkageError e) {
+ LOG.warn("OpenTelemetry tracing is enabled but OpenTelemetry is not
available; tracing is disabled", e);
}
- addListener(future, (resp, error) -> {
- try {
- if (error != null) {
- setError(span, error);
- } else {
- span.setStatus(StatusCode.OK);
- }
- } catch (Throwable t) {
- LOG.error("Error setting span status, ending span anyway", t);
- } finally {
- span.end();
- }
- });
+ return NoOpTraceProvider.INSTANCE;
}
-
- public static void setError(Span span, Throwable error) {
- span.recordException(error);
- span.setStatus(StatusCode.ERROR);
- }
-
- /**
- * This is method is used when you just want to add a listener to the given
future. We will call
- * {@link CompletableFuture#whenComplete(BiConsumer)} to register the {@code
action} to the
- * {@code future}. Ignoring the return value of a Future is considered as a
bad practice as it may
- * suppress exceptions thrown from the code that completes the future, and
this method will catch
- * all the exception thrown from the {@code action} to catch possible code
bugs.
- * <p/>
- * And the error phone check will always report FutureReturnValueIgnored
because every method in
- * the {@link CompletableFuture} class will return a new {@link
CompletableFuture}, so you always
- * have one future that has not been checked. So we introduce this method
and add a suppression
- * warnings annotation here.
- */
- @SuppressWarnings("FutureReturnValueIgnored")
- private static <T> void addListener(CompletableFuture<T> future,
- BiConsumer<? super T, ? super Throwable> action) {
- future.whenComplete((resp, error) -> {
- try {
- // https://s.apache.org/completionexception — unwrap
CompletionException for callers
- action.accept(resp, error == null ? null :
JavaUtils.unwrapCompletionException(error));
- } catch (Throwable t) {
- LOG.error("Unexpected error caught when processing CompletableFuture",
t);
- }
- });
- }
-
- private static final TextMapPropagator PROPAGATOR =
- GlobalOpenTelemetry.getPropagators().getTextMapPropagator();
Review Comment:
Add it back to OpenTelemetryTraceUtils and use it.
##########
ratis-common/src/main/java/org/apache/ratis/trace/OpenTelemetryTraceProvider.java:
##########
@@ -0,0 +1,177 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ratis.trace;
+
+import io.opentelemetry.api.trace.Span;
+import io.opentelemetry.api.trace.SpanKind;
+import io.opentelemetry.api.trace.StatusCode;
+import io.opentelemetry.api.trace.Tracer;
+import io.opentelemetry.context.Context;
+import io.opentelemetry.context.Scope;
+import org.apache.ratis.proto.RaftProtos.AppendEntriesRequestProto;
+import org.apache.ratis.proto.RaftProtos.RaftRpcRequestProto;
+import org.apache.ratis.proto.RaftProtos.SpanContextProto;
+import org.apache.ratis.protocol.RaftClientRequest;
+import org.apache.ratis.protocol.RaftPeerId;
+import org.apache.ratis.util.JavaUtils;
+import org.apache.ratis.util.Preconditions;
+import org.apache.ratis.util.function.CheckedSupplier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.BiConsumer;
+import java.util.function.Supplier;
+
+final class OpenTelemetryTraceProvider implements TraceProvider {
+ private static final Logger LOG =
LoggerFactory.getLogger(OpenTelemetryTraceProvider.class);
+ private static final String LEADER = "LEADER";
+
+ private final Tracer tracer = OpenTelemetryTraceUtils.getGlobalTracer();
+
+ @Override
+ public boolean isEnabled() {
+ return tracer != null;
+ }
+
+ @Override
+ public <T, THROWABLE extends Throwable> CompletableFuture<T> traceClientSend(
+ CheckedSupplier<CompletableFuture<T>, THROWABLE> action,
+ RaftClientRequest.Type type, RaftPeerId server) throws THROWABLE {
+ if (!isEnabled()) {
+ return action.get();
+ }
+ return traceAsyncMethod(action, () -> createClientOperationSpan(type,
server, SpanNames.ASYNC_SEND));
+ }
+
+ @Override
+ public <T, THROWABLE extends Throwable> CompletableFuture<T>
traceServerRequest(
+ CheckedSupplier<CompletableFuture<T>, THROWABLE> action,
+ RaftClientRequest request, String memberId, String spanName) throws
THROWABLE {
+ if (!isEnabled()) {
+ return action.get();
+ }
+ return traceAsyncMethod(action, () ->
createServerSpanFromClientRequest(request, memberId, spanName));
+ }
+
+ @Override
+ public <T> CompletableFuture<T> traceAppendEntries(
+ CheckedSupplier<CompletableFuture<T>, IOException> action,
+ AppendEntriesRequestProto request, String memberId) throws IOException {
+ if (!isEnabled()) {
+ return action.get();
+ }
+ final RaftRpcRequestProto rpc = request.getServerRequest();
+ final SpanContextProto spanContext = rpc.getSpanContext();
+ final Context remoteContext = (spanContext == null ||
spanContext.getContextMap().isEmpty())
+ ? Context.root()
+ : OpenTelemetryTraceUtils.extractContextFromProto(spanContext);
+ return traceAsyncMethod(action, () -> {
+ final Span span = tracer.spanBuilder(SpanNames.APPEND_ENTRIES_ASYNC)
+ .setParent(remoteContext)
+ .setSpanKind(SpanKind.INTERNAL)
+ .startSpan();
+ span.setAttribute(RatisAttributes.MEMBER_ID, memberId);
+ span.setAttribute(RatisAttributes.PEER_ID,
String.valueOf(RaftPeerId.valueOf(rpc.getRequestorId())));
+ span.setAttribute(RatisAttributes.APPEND_ENTRIES_COUNT, (long)
request.getEntriesCount());
+ return span;
+ });
+ }
+
+ private Span createClientOperationSpan(RaftClientRequest.Type type,
RaftPeerId server, String spanName) {
+ Preconditions.assertNotNull(spanName, () -> "Span name cannot be null");
+ Preconditions.assertTrue(!spanName.isEmpty(), "Span name should not be
empty");
+ final String peerId = server == null ? LEADER : String.valueOf(server);
+ final Span span = tracer.spanBuilder(spanName)
+ .setSpanKind(SpanKind.CLIENT)
+ .startSpan();
+ span.setAttribute(RatisAttributes.PEER_ID, peerId);
+ span.setAttribute(RatisAttributes.OPERATION_NAME, spanName);
+ span.setAttribute(RatisAttributes.OPERATION_TYPE, String.valueOf(type));
+ return span;
+ }
+
+ private Span createServerSpanFromClientRequest(RaftClientRequest request,
String memberId, String spanName) {
+ final Context remoteContext =
OpenTelemetryTraceUtils.extractContextFromProto(request.getSpanContext());
+ final Span span = tracer.spanBuilder(spanName)
+ .setParent(remoteContext)
+ .setSpanKind(SpanKind.SERVER)
+ .startSpan();
+ span.setAttribute(RatisAttributes.CLIENT_ID,
String.valueOf(request.getClientId()));
+ span.setAttribute(RatisAttributes.CALL_ID,
String.valueOf(request.getCallId()));
+ span.setAttribute(RatisAttributes.MEMBER_ID, memberId);
+ return span;
+ }
+
+ @SuppressWarnings("try")
+ private static <T, THROWABLE extends Throwable> CompletableFuture<T>
traceAsyncMethod(
+ CheckedSupplier<CompletableFuture<T>, THROWABLE> action, Supplier<Span>
spanSupplier) throws THROWABLE {
+ final Span span = spanSupplier.get();
+ try (Scope ignored = span.makeCurrent()) {
+ final CompletableFuture<T> future;
+ try {
+ future = action.get();
+ } catch (RuntimeException | Error e) {
+ setError(span, e);
+ span.end();
+ throw e;
+ } catch (Throwable t) {
+ setError(span, t);
+ span.end();
+ throw JavaUtils.<THROWABLE>cast(t);
+ }
+ endSpan(future, span);
+ return future;
+ }
+ }
+
+ private static void endSpan(CompletableFuture<?> future, Span span) {
+ addListener(future, (resp, error) -> {
+ try {
+ if (error != null) {
+ setError(span, error);
+ } else {
+ span.setStatus(StatusCode.OK);
+ }
+ } catch (Throwable t) {
+ LOG.error("Error setting span status, ending span anyway", t);
+ } finally {
+ span.end();
+ }
+ });
+ }
+
+ private static void setError(Span span, Throwable error) {
+ span.recordException(error);
+ span.setStatus(StatusCode.ERROR);
+ }
+
+ @SuppressWarnings("FutureReturnValueIgnored")
+ private static <T> void addListener(CompletableFuture<T> future,
Review Comment:
Let's keep the javadoc:
```java
/**
* This is method is used when you just want to add a listener to the
given future. We will call
* {@link CompletableFuture#whenComplete(BiConsumer)} to register the
{@code action} to the
* {@code future}. Ignoring the return value of a Future is considered as
a bad practice as it may
* suppress exceptions thrown from the code that completes the future, and
this method will catch
* all the exception thrown from the {@code action} to catch possible code
bugs.
* <p/>
* And the error phone check will always report FutureReturnValueIgnored
because every method in
* the {@link CompletableFuture} class will return a new {@link
CompletableFuture}, so you always
* have one future that has not been checked. So we introduce this method
and add a suppression
* warnings annotation here.
*/
@SuppressWarnings("FutureReturnValueIgnored")
private static <T> void addListener(CompletableFuture<T> future,
```
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]