dapr / java-sdk

Dapr SDK for Java
Apache License 2.0
260 stars 206 forks source link

Blocking call detected by BlockHound when invoke Actor methods, and access actorStateManager methods #562

Closed xiaoweil closed 3 years ago

xiaoweil commented 3 years ago

Hi there,

We are experimenting with the dapr actor service, and have seen some blocking calls detected by BlockHound. Wondering if these are false positive or we did something wrong with the actor usage. Please see below for example:

Create a simple actor call Test

@ActorType(name = "Test")
public interface Test {
  @ActorMethod(name = "testHello")
  Mono<Void> testHello(String name);

  @ActorMethod(name = "testName")
  Mono<String> testName(String name);
}
public class TestImpl extends AbstractActor implements Test {
  private static final String NAME = "name_";

  public TestImpl(ActorRuntimeContext runtimeContext, ActorId id) {
    super(runtimeContext, id);
  }

  @Override
  public Mono<Void> testHello(String name) {
    return super.getActorStateManager().set(NAME + name, name);
  }

  @Override
  public Mono<String> testName(String name) {
    return super.getActorStateManager().get(NAME + name, TypeRef.STRING);
  }
}

Create testing endpoints with spring webflux:

  @PostMapping(path = "/testHello")
  public Mono<Void> testHello(@RequestBody(required = false) byte[] body) {
    return Mono.just(new String(body))
        .map(id -> new ActorProxyBuilder<Test>(Test.class, actorClient).build(new ActorId(id)))
        .flatMap(test -> test.testHello(new String(body)));
  }

  @PostMapping(path = "/testName")
  public Mono<String> testName(@RequestBody(required = false) byte[] body) {
    return Mono.just(new String(body))
        .map(id -> new ActorProxyBuilder<Test>(Test.class, actorClient).build(new ActorId(id)))
        .flatMap(test -> test.testName(new String(body)));
  }

Register the actor: ActorRuntime.getInstance().registerActor(TestImpl.class);

Now make a remote call to the dapr service: http://localhost:3500/v1.0/invoke/testapp/method/testName

We got the following BlockHound warning on DaprGrpcClient.invoke:

reactor.blockhound.BlockingOperationError: Blocking call! jdk.internal.misc.Unsafe#park
    at java.base/jdk.internal.misc.Unsafe.park(Unsafe.java) ~[na:na]
    Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Assembly trace from producer [reactor.core.publisher.MonoCallable] :
    reactor.core.publisher.Mono.fromCallable
    io.dapr.actors.client.DaprGrpcClient.invoke(DaprGrpcClient.java:41)
Error has been observed at the following site(s):
    |_  Mono.fromCallable ⇢ at io.dapr.actors.client.DaprGrpcClient.invoke(DaprGrpcClient.java:41)
    |_           Mono.map ⇢ at io.dapr.actors.client.DaprGrpcClient.invoke(DaprGrpcClient.java:51)
    |_                    ⇢ at io.dapr.actors.client.ActorClient.invoke(ActorClient.java:68)
    |_          Mono.then ⇢ at io.dapr.actors.client.ActorProxyImpl.invokeMethod(ActorProxyImpl.java:124)
    |_                    ⇢ at com.test.alert.controller.AlertController.lambda$testName$3(AlertController.java:56)
    |_       Mono.flatMap ⇢ at com.test.alert.controller.AlertController.testName(AlertController.java:56)

If we add a scheduler to the actor method call:

  @PostMapping(path = "/testName")
  public Mono<String> testName(@RequestBody(required = false) byte[] body) {
    return Mono.just(new String(body))
        .map(id -> new ActorProxyBuilder<Test>(Test.class, actorClient).build(new ActorId(id)))
        .flatMap(test -> test.testName(new String(body)))
        .subscribeOn(Schedulers.boundedElastic());
  }

then we got the following on DaprGrpcClient.getState:

reactor.blockhound.BlockingOperationError: Blocking call! jdk.internal.misc.Unsafe#park
    at java.base/jdk.internal.misc.Unsafe.park(Unsafe.java) ~[na:na]
    Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Assembly trace from producer [reactor.core.publisher.MonoCallable] :
    reactor.core.publisher.Mono.fromCallable
    io.dapr.actors.runtime.DaprGrpcClient.getState(DaprGrpcClient.java:70)
Error has been observed at the following site(s):
    |_  Mono.fromCallable ⇢ at io.dapr.actors.runtime.DaprGrpcClient.getState(DaprGrpcClient.java:70)
    |_           Mono.map ⇢ at io.dapr.actors.runtime.DaprGrpcClient.getState(DaprGrpcClient.java:80)
    |_       Mono.flatMap ⇢ at io.dapr.actors.runtime.DaprStateAsyncProvider.load(DaprStateAsyncProvider.java:65)
    |_ Mono.switchIfEmpty ⇢ at io.dapr.actors.runtime.ActorStateManager.get(ActorStateManager.java:135)
    |_           Mono.map ⇢ at io.dapr.actors.runtime.ActorStateManager.get(ActorStateManager.java:136)
    |_ Mono.switchIfEmpty ⇢ at io.dapr.actors.runtime.ActorStateManager.get(ActorStateManager.java:133)
    |_                    ⇢ at com.zafin.microhub.apollo.alert.actor.TestImpl.testName(TestImpl.java:23)

Thanks for your time and let me know what you guys think.

Cheers

RELEASE NOTE: Fix Actor gRPC client to be non-blocking

artursouza commented 3 years ago

Thanks for reporting this issue. This needs to be fixed. We did fix this in the non-Actor client but did not update this one.