Code review comment: We've seen in the past that calling `get()` can result in other dependent stages being run in the current thread, implying that the thread steps into Flink land.

Code under review:
private Object invokeRpc(Method method, Object[] args) throws Exception {
        String methodName = method.getName();
        Class<?>[] parameterTypes = method.getParameterTypes();
        Annotation[][] parameterAnnotations = method.getParameterAnnotations();
        Time futureTimeout = extractRpcTimeout(parameterAnnotations, args, timeout);

        final RpcInvocation rpcInvocation =
                createRpcInvocationMessage(methodName, parameterTypes, args);

        Class<?> returnType = method.getReturnType();

        final Object result;

        if (Objects.equals(returnType, Void.TYPE)) {
            tell(rpcInvocation);

            result = null;
        } else {
            
            
            
            
            
            final Throwable callStackCapture = captureAskCallStack ? new Throwable() : null;

            
            final CompletableFuture<?> resultFuture = ask(rpcInvocation, futureTimeout);

            final CompletableFuture<Object> completableFuture = new CompletableFuture<>();
            resultFuture.whenComplete(
                    (resultValue, failure) ->
                            ClassLoadingUtils.runWithFlinkContextClassLoader(
                                    () -> {
                                        if (failure != null) {
                                            completableFuture.completeExceptionally(
                                                    resolveTimeoutException(
                                                            failure, callStackCapture, method));
                                        } else {
                                            completableFuture.complete(
                                                    deserializeValueIfNeeded(resultValue, method));
                                        }
                                    }));

            if (Objects.equals(returnType, CompletableFuture.class)) {
                result = completableFuture;
            } else {
                try {
                    result =
                            ClassLoadingUtils.runWithFlinkContextClassLoader(
                                    () ->
                                            completableFuture.get(
                                                    futureTimeout.getSize(),
                                                    futureTimeout.getUnit()));
                } catch (ExecutionException ee) {
                    throw new RpcException(
                            "Failure while obtaining synchronous RPC result.",
                            ExceptionUtils.stripExecutionException(ee));
                }
            }
        }

        return result;
    }

Line commented on: futureTimeout.getUnit()));

Revised code:
private Object invokeRpc(Method method, Object[] args) throws Exception {
        String methodName = method.getName();
        Class<?>[] parameterTypes = method.getParameterTypes();
        Annotation[][] parameterAnnotations = method.getParameterAnnotations();
        Time futureTimeout = extractRpcTimeout(parameterAnnotations, args, timeout);

        final RpcInvocation rpcInvocation =
                createRpcInvocationMessage(
                        method.getDeclaringClass().getSimpleName(),
                        methodName,
                        parameterTypes,
                        args);

        Class<?> returnType = method.getReturnType();

        final Object result;

        if (Objects.equals(returnType, Void.TYPE)) {
            tell(rpcInvocation);

            result = null;
        } else {
            
            
            
            
            
            final Throwable callStackCapture = captureAskCallStack ? new Throwable() : null;

            
            final CompletableFuture<?> resultFuture = ask(rpcInvocation, futureTimeout);

            final CompletableFuture<Object> completableFuture = new CompletableFuture<>();
            resultFuture.whenComplete(
                    (resultValue, failure) -> {
                        if (failure != null) {
                            completableFuture.completeExceptionally(
                                    resolveTimeoutException(
                                            failure, callStackCapture, address, rpcInvocation));
                        } else {
                            completableFuture.complete(
                                    deserializeValueIfNeeded(resultValue, method));
                        }
                    });

            if (Objects.equals(returnType, CompletableFuture.class)) {
                result = completableFuture;
            } else {
                try {
                    result =
                            completableFuture.get(futureTimeout.getSize(), futureTimeout.getUnit());
                } catch (ExecutionException ee) {
                    throw new RpcException(
                            "Failure while obtaining synchronous RPC result.",
                            ExceptionUtils.stripExecutionException(ee));
                }
            }
        }

        return result;
    }