Is there any difference between `flatMap` and `block` in case of "nested" `Mono` - project-reactor

As I understand both flatMap and block ends with subscribe, so is there any difference in below statements?
Mono.just("a").flatMap(value -> Mono.just("b")).subscribe();
Mono.just("a").doOnNext(value -> Mono.just("b").block()).subscribe();

Actually, they are very different. The second one blocks the main thread. The following code is going to block the main thread for 5 seconds:
#Test
void test_blockingCode() {
Mono.just("a")
.doOnNext(value -> Mono.just("b").delayElement(Duration.ofSeconds(5)).block())
.subscribe();
}
Here is the thread dump:
"main" #1 prio=5 os_prio=0 cpu=1550,32ms elapsed=4,41s
tid=0x00007f376002c4e0 nid=0x2060 waiting on condition
[0x00007f3768ae3000] java.lang.Thread.State: WAITING (parking) at
jdk.internal.misc.Unsafe.$$BlockHound$$_park(java.base#16.0.2/Native
Method)
parking to wait for <0x0000000611d97458> (a java.util.concurrent.CountDownLatch$Sync) at
jdk.internal.misc.Unsafe.park(java.base#16.0.2/Unsafe.java) at
java.util.concurrent.locks.LockSupport.park(java.base#16.0.2/LockSupport.java:211)
at
java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(java.base#16.0.2/AbstractQueuedSynchronizer.java:714)
at
java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly(java.base#16.0.2/AbstractQueuedSynchronizer.java:1046)
at
java.util.concurrent.CountDownLatch.await(java.base#16.0.2/CountDownLatch.java:232)
at
reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:87)
at reactor.core.publisher.Mono.block(Mono.java:1704)

Related

Dart: Can't understand how async/awaut, Future and Event loop work together

I'm new to Dart. As i understood from some articles and docs (for example this article):
async/await uses the same mechanism as Future, and that is FIFO Event Loop.
Event Loop launches after the execution of main().
async functions runs synchronously up to the first await keyword. Then they pause the execution, and execution returns to the previous function in call stack.
the remaining code is wrapped to the Future and queued to the Event Loop (most unsure point for me).
Based on these points i expect, that the following code:
main() {
Future(() => print("1"));
Future(() {
print("2");
asyncFunc("2");
});
Future(() => print("3"));
print("main");
asyncFunc("main");
print("\nEvent loop starts?\n");
}
asyncFunc(String from) async {
print("start [from $from]");
await (() async => print("middle [from $from]"))();
print("end [from $from]");
}
Will create something similar to this event queue after main() execution:
future1 -> future2 -> future3 -> end_from_main
And after execution of future2, event end_from_future2 will be queued to the end:
future1 -> future2 -> future3 -> end_from_main -> end_from_future2
So result output i expect should be:
main
start [from main]
middle [from main]
Event loop starts?
1
2
start [from 2]
middle [from 2]
3
end [from main]
end [from 2]
But in fact it returns:
main
start [from main]
middle [from main]
Event loop starts?
end [from main]
1
2
start [from 2]
middle [from 2]
end [from 2]
3
So the conclusion i made: Either async/await events has priority over Fututre. Either they use diffrent mechanism, unrelated to EventLoop. Or maybe i misunderstand something hardly..
What i think i understood:
Future can represent either events in Event queue, either tasks in Microtask queue.
(for example: default Future() constructor puts event to Event queue, Future.microtask() puts task to Microtask queue)
await creates Future.than callback to recived Future.
My topic example explanation:
First three Futrues put events in Event queue.
Future<void> created implicitly in await (() async => print("asyncFunc middle"))() creates "empty" task in Microtask queue (i assume that all implicit Futures are scheduled to Microtask queue). Than await puts remaining instructions (print("end [from main]")) as Future.than callback to this "empty" task.
When it comes to Event Loop it first executes "empty" task from Microtask queue. Which ended with callback: print("end [from main]").
And than Event loop executes Event queue, where second event spawns similar "empty" task in Microtask queue. And that's why end [from 2] happens before third Future

How do I update my PID on a timer?

I am trying to update my process's state on a 10 second timer.
-define(INTERVAL, 3000).
start_link() ->
gen_server:start_link(?MODULE, [], []).
action(Pid, Action) ->
gen_server:call(Pid, Action).
init([]) ->
erlang:send_after(?INTERVAL, self(), trigger),
{ok, temple:new()}.
what I want to do is call this
handle_call({fight}, _From, Temple) ->
NewTemple = temple:fight(Temple),
{reply, NewTemple, NewTemple};
So I try
handle_info(trigger, _State) ->
land:action(self(), {fight}),
erlang:send_after(?INTERVAL, self(), trigger);
but I get
=ERROR REPORT==== 4-Dec-2016::19:00:35 ===
** Generic server <0.400.0> terminating
** Last message in was trigger
** When Server state == {{dict,0,16,16,8,80,48,
{[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],
[]},
{{[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],
[]}}},
[]}
** Reason for termination ==
** {function_clause,[{land,terminate,
[{timeout,{gen_server,call,[<0.400.0>,{fight}]}},
{{dict,0,16,16,8,80,48,
{[],[],[],[],[],[],[],[],[],[],[],[],[],[],
[],[]},
{{[],[],[],[],[],[],[],[],[],[],[],[],[],
[],[],[]}}},
[]}],
[{file,"src/land.erl"},{line,47}]}
It appears that with land:action(self(), {fight}), you're attempting to make a call to the same gen_server in which you're currently handling the trigger message. Two important facts will explain why this can't work:
A call always waits for the result to be returned.
A gen_server is a process, and a process can handle only one message at a time.
In handling the trigger message, you're saying to call back to yourself and wait for yourself to process the {fight} message. Since you're in the middle of handling the trigger message, though, you'll never get to the {fight} message. You're effectively in a deadlock with yourself. That's why you're getting a timeout.
P.S. Posting an SSCCE is far more likely to get you good answers.
The error message means that there is no terminate clause in the land server module, you should have a warning when you compile the land server module.
The terminate clause is called because a timeout occurs during the gen_server call with the parameters (Pid = <0.400.0>, Message = {fight}), which was called on line land:action(self(), {fight}),. A call to a gen_server must be completed within a maximum time, by default 5000ms, You have to reduce the time spent in the fight action.
Note that it is not a good idea to increase the server timeout since a gen_server call is blocking: during the execution of a gen_server call no new message can be handled, and in your example , it is also blocking the execution of the handle_info(trigger, _State) code.
Last point, the clause handle_info(trigger, _State) should return a tuple of the form {noreply,NewState} while the last line erlang:send_after(?INTERVAL, self(), trigger); returns a timer reference, you have to modify this line.

Erlang gen_server with long-running tasks

Good day,
I have a gen_server process which does some long-running state-updating tasks periodically in
handle_info:
handle_info(trigger, State) ->
NewState = some_long_running_task(),
erlang:send_after(?LOOP_TIME, self(), trigger),
{noreply, NewState}.
But when such task runs, then whole server gets unresponsive and any call to it leads to whole server crash:
my_gen_server:status().
** exception exit: {timeout,{gen_server,call,[my_gen_server,status]}}
in function gen_server:call/2
How it is possible to avoid blocking of gen_server ?
And when one call my_gen_server:status() at any time, the result should be something like:
{ok, task_active}
execute the long running task in a separate process. Let this process inform the gen_server of its progress with the task (that is if the task's progress can be tracked) OR let the process complete the task or fail but at least inform the gen_server of the results of the task.
Let the gen_server be linked with the process doing this long running task, and let the gen_server know the PID or registered name so that in case of exit signals, it can isolate the death of that important process from the Rest.
handle_info(trigger, State) ->
Pid = spawn_link(?MODULE,some_long_running_task,[State]),
NewState = save_pid(Pid,State),
{noreply, NewState};
handle_info({'EXIT',SomePid,_},State)->
case lookup_pid(State) == SomePid of
false -> %% some other process
{noreply,State};
true ->
%% our process has died
%% what do we do now ?
%% spawn another one ?
%% thats your decision to take
....
....
{noreply,State}
end;
handle_info({finished,TaskResult},State)->
.....%% update state e.t.c.
erlang:send_after(?LOOP_TIME, self(), trigger),
{noreply,NewState}.
some_long_running_task(ServerState)->
....do work
....return results
This call does not lead to a crash, but simply to an exception which can be caught:
status() ->
try gen_server:call(my_gen_server, status)
catch
exit:{timeout,_} -> {ok, task_active}
end.
However, the call will remain in the server's queue, and after it finishes handling the current message, it will send a reply message: {ServerRef, Reply}, which should be discarded by the calling process.
The only way to avoid blocking of any process in Erlang (whether gen_server or not) is not to run blocking tasks on it. So another alternative could be to run your long tasks on a different process which only talks to your server, so nobody cares that it's blocked.

is a gen_server is up?

Is there a way to tell a gen_server: "supervisor has initialised all gen_servers, now you can send then messages"?
I have a worker gen_server whose job is to set up states of other gen_servers in his supervision tree. If I just start sending messages in init function of my configuration server, sometimes it gets {noproc, _}. I suppose that means that config server was to fast: he sent messages before supervisor had enough time to start all workers. I fixed that by putting timer:sleep(500) in config_server:init(), which ensures all gen_server had enough time to initialise, but this seems like a inelegant solution.
Is there a proper way to do this?
Return tuple with timeout 0 from init. Then immediately after it returns, handle_info(timeout, State) will be called. In handle_info make some call which won't return until the supervisor finishes initialization (e.g. supervisor:which_children).
info(PlayerId) ->
Pid = case global:whereis_name(util:getRegName({?MODULE, PlayerId})) of
P when is_pid(P) ->
P;
_ ->
{ok, P} = player_sup:start_child(PlayerId),
P
end,
gen_server:call(Pid, info).
This is my case to handle this issue. This worker process is triggered only when it is requested.
in function init() call gen_server:cast(init, State). message "init" will be first in message queue

Good design for resending a message?

I need to send a message to a globally registered process, that might be unavailable for a short-time, when it is replaced with a backup process (i.e. failover).
Is the following snippet good Erlang code:
% send message to globally registered process, with possibility to retry once
send_message(To, Message, Retry) ->
try global:send(To, Message)
catch
% registered process To is unavailable
exit: {badarg, {To, Message}} ->
io:format("catch: exit: {badarg,{~w, ~w}}~n", [To, Message]), % dbg only
case Retry of
true ->
% retry sending message, after 1 second
sleep(1000),
send_message(To, Message, false);
false ->
% re-throw caught exit, including stack trace
erlang:raise(exit, {badarg, {To, Message}},
erlang:get_stacktrace())
end
end.
The Retry parameter is either true or false, indicating the message should be retried once if there was a problem. If the message can still not be sent, I want the same exception to be raised as would have been the case by calling global:send(To, Message) outside a try-catch block.
I know the above works, but I am concerned whether the false section of my case block is good erlang (e.g. using erlang:raise() and rlang:get_stacktrace()).
Any thoughts or suggestions to make the code "better"?
Make two different calls to global:send, one inside a try ... catch, the other not:
send_message(To, Message, 0, _, _) ->
global:send(To, Message);
send_message(To, Message, RetriesLeft, RetryDelayMs, MaxRetryDelayMs) ->
try
global:send(To, Message)
catch
% registered process To is unavailable
exit: {badarg, {To, Message}} ->
io:format("catch: exit: {badarg,{~w, ~w}}~n", [To, Message]), % dbg only
% retry after RetryDelayMs milliseconds
sleep(min(RetryDelayMs, MaxRetryDelayMs)),
send_message(To, Message, RetriesLeft - 1, 2 * RetryDelayMs, MaxRetryDelayMs)
end.
EDIT: Added exponential back-off. Be like … nope, just can't do it.
I would do
erlang:error({badarg,{To,Message}})
instead. No real difference as this also generates a stack trace but I think it is clearer. erlang:raise/3 is better for a more generic usage and if you want do things with the stacktrace.

Resources