1

Let's say I have such a method in class implementing BeforeConvertCall<Object>

 @Override
    public Publisher<Object> onBeforeConvert(Object o, SqlIdentifier sqlIdentifier) {
        
        return Mono.fromCallable(() -> enhance(o)); (A)
        //
        return Mono.just(o)
        .map(this::enhance); (B)

    }

private Object enhance(Object o) {

    if (o instanceof Foo && ((Foo) o).getId() == null) {
        ((Foo) o).setId(UUID.randomUUID().toString());
    } 
    return o;
}
  1. Is this code blocking? I mean should one be concerned?
  2. Does it make sense to wrap UUID generation by ThreadLocal in reactor?
  3. Is there any difference between (A) and (B) calls?

I tried also put inside a enhance method this time-consuming operation:

for (int i = 0; i < Integer.MAX_VALUE; i++) {
        String id = UUID.randomUUID().toString();
    }

and made 3 concurrent requests. 4 reactor-http-nio threads were spawned all in the runnable state. First did lock SecureRandom and rest 3:

- locked <0x0000000600c4b510> (a io.netty.channel.nio.SelectedSelectionKeySet)
- locked <0x0000000600c4b4b0> (a sun.nio.ch.KQueueSelectorImpl)
  1. Why the rest 3 threads were not blocked because of the lock on the SecureRandom instance?

I tried to place the same for cycle into main pipeline within doNext() hook and there suddenly rest threads got blocked as expected.

  1. Why did they get blocked here but with the same approach within the onBeforeConvert() they did not? Moreover, when I tried to wrap the call with the ThreadLocal, it did not help.

java.lang.Thread.State: BLOCKED (on object monitor) at sun.security.provider.SecureRandom.engineNextBytes(java.base@11.0.10/SecureRandom.java:216)

  • waiting to lock <0x0000000600118b28> (a sun.security.provider.SecureRandom)
  1. I would expect to all 4 threads have own instance of SecureRandom.. or not? Is it perhaps so that only main thread own the instance and it populates work to the working threads which have no thread-local of SecureRandom?

Last one experiment:

 @Override
    public Publisher<Object> onBeforeConvert(Object o, SqlIdentifier sqlIdentifier) {
        
        return Mono.fromCallable(() -> enhance(o))
        .map(this::enhance)
        .subscribeOn(Schedulers.boundedElastic()); (C)

    }

After adding line (C) and running 4 concurrent requests, boundedElastic-2,3,4 did get blocked.

  1. Why did they get blocked though reactor-http-nio in the previous approach did not get blocked?
lkatiforis
  • 5,703
  • 2
  • 16
  • 35

1 Answers1

0
  1. Yes, in the way you show this code snippet - here, the UUID generation is blocking.

  2. No

  3. Till subscribeOn is not called, there is no significant difference between those two. The just is evaluated imediately for all the subscribers, when the fromCallable is evaluated at the time of subscription and separately for each subscriber. See more here: link

The best idea is to use:

return Mono.fromCallable(() -> enhance(o))
        .subscribeOn(Schedulers.boundedElastic());

With this approach you delegate blocking logic to the separate thread pool Schedulers.boundedElastic() so the main reactor thread pool is not blocked and you make not impact on whole application

mslowiak
  • 1,688
  • 12
  • 25