Skip to content

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

Description

@xiaoweil

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

Metadata

Metadata

Assignees

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions