What is the usecase of Flux.repeat() without any parameter? - project-reactor

I was looking at the following code and was confused with what the repeat() operator is doing here.
return inboundFlux
.groupBy(record -> record.receiverOffset().topicPartition())
.flatMap(partitionFlux -> partitionFlux
.concatMap(el -> Flux.just(el)
.doOnNext(receiverRecord -> {
log.info("Starting to process {}", receiverRecord);
messageProcessor.processMessage(receiverRecord);
receiverRecord.receiverOffset().acknowledge();
log.info("Message acknowledged");
})
.doOnError(e -> log.error("ERRRRRRROOORRRRRR"))
.retryWhen(Retry.backoff(3, Duration.ofSeconds(5)).maxBackoff(Duration.ofSeconds(20)).transientErrors(true))
.onErrorResume(e -> {
// code to handle retry exhaustion
})
).repeat()
)
.subscribeOn(scheduler)
.subscribe();

Related

Spring-Integration-DSL: Nested Scatter Gather hangs

Here is a broken, but executable example code:
#Bean
IntegrationFlow testFlow() {
return IntegrationFlows
.from(Http.inboundChannelAdapter("test")
.requestMapping(mapping -> mapping.methods(HttpMethod.GET))
.get())
.scatterGather(
scatterer -> scatterer
.applySequence(true)
.recipientFlow(flow -> flow
.scatterGather(
scatterer1 -> scatterer1
.applySequence(true)
.recipientFlow(IntegrationFlowDefinition::bridge),
gatherer -> gatherer.outputProcessor(MessageGroup::getOne))
.log(INFO, m -> "THIS HAPPENS")),
gatherer -> gatherer.outputProcessor(MessageGroup::getOne))
.log(INFO, m -> "THIS NEVER HAPPENS")
.get();
}
The expected output is:
THIS HAPPENS
THIS NEVER HAPPENS
The actual output is:
THIS HAPPENS
I found this identical looking issue on Github, but it claims it has been fixed with versions 5.1.10 and 5.2.4. I am running spring-boot-starter-integration 5.5.0 which includes spring-integration-core of the same version.
What can I do to get this nested scatter gather to work ? Is it a bug with the DSL or with my code ?
After having a similar problem using only one level of scatter-gather, I realized it was the log message that was blocking the output from being returned to the parent flow. Replace .log() with .logAndReply() or .log().bridge() and everything should work again.
Like this:
#Bean
IntegrationFlow testFlow() {
return IntegrationFlows
.from(Http.inboundChannelAdapter("test")
.requestMapping(mapping -> mapping.methods(HttpMethod.GET))
.get())
.scatterGather(
scatterer -> scatterer
.applySequence(true)
.recipientFlow(flow -> flow
.scatterGather(
scatterer1 -> scatterer1
.applySequence(true)
.recipientFlow(IntegrationFlowDefinition::bridge),
gatherer -> gatherer.outputProcessor(MessageGroup::getOne))
.logAndReply(INFO, m -> "THIS HAPPENS")), // this fixes the problem
gatherer -> gatherer.outputProcessor(MessageGroup::getOne))
.log(INFO, m -> "THIS NEVER HAPPENS")
.get();
}

Kotlin multiplatform lambda call without Unit return type

I have a multiplatform Kotlin project for Android and iOS. The functions in common module are written like this:
fun test(successCallback: (String) -> Unit, errorCallback: (Error) -> Unit) {
successCallback("success")
}
Android usage of function:
Common.test(successCallback = {
Log.d(TAG, it)
},
errorCallback = {
Log.d(TAG, it)
}
})
iOS usage of function:
Common.test(successCallback: { it in
print(it)
return KotlinUnit()
}, errorCallback: { error in
print(error)
return KotlinUnit()
})
JVM world seems to handle the return type (Unit) by itself (nothing needs to be returned), while you must return KotlinUnit() in iOS.
I would like to achieve the same on iOS (callback without return KotlinUnit())

Handle Mono.empty() in GatewayFilter

I'm building a GatewayFilter, but I'm having problems handling cases where I don't have data. Here is a sample of the GatewayFilterFactory<>:
return (exchange, chain) -> getDataFromRedis()
.flatMap(redisData -> {
ServerHttpRequest request = exchange.getRequest().mutate()
.header("header", redisData).build();
return chain.filter(exchange.mutate().request(request).build());
});
Here is the getDataFromRedis method:
Mono<String> getDataFromRedis() {
return redis.get("key");
}
It returns a Mono with a String value, or empty if it wasn't found.
The above example works as expected, but when the data is redis is not found, and an empty Mono is returned, then I need it to return a redirect. So this is my attempt:
return (exchange, chain) -> getDataFromRedis()
.flatMap(redisData -> {
ServerHttpRequest request = exchange.getRequest().mutate()
.header("header", redisData).build();
return chain.filter(exchange.mutate().request(request).build());
})
.switchIfEmpty(Mono.defer(() -> {
exchange.getResponse().setStatusCode(HttpStatus.FOUND);
exchange.getResponse().getHeaders().add(HttpHeaders.LOCATION, "some other url");
return exchange.getResponse().setComplete();
}));
The deferred mono in switchIfEmpty is always invoked, irrespective of whether there is data in redis or not.
To summarise, I need the filter to add a header value it gets from Redis, but redirect if there is no data available.

Using node modules from fable

I'm trying to import a node module in my fable code. Being new to fable I did expect so problems and understanding the import flow seems to be one of those. I have the below code which compiles fine but fails run time with Cannot read property 'request' of undefined on the line of the printfn statement
module Session =
let inline f (f: 'a->'b->'c->'d) = Func<_,_,_,_> f
[<Import("default","request")>]
type Http =
abstract request : string -> System.Func<obj,obj,obj,unit> -> unit
let http : Http = failwith "js only"
let start () =
http.request "http://dr.dk" (ff (fun error response body ->
printfn "%A" body
))
do
start()
I was able to get your example working in the Fable REPL:
open System
open Fable
open Fable.Core
open Fable.Core.JS
open Fable.Core.JsInterop
type RequestCallback = Func<obj, obj, obj, unit>
type Request = Func<string, RequestCallback, unit>
[<ImportDefault("request")>]
let request : Request = jsNative
let start () =
request.Invoke("http://dr.dk", (fun error response body ->
console.log("error", error)
console.log("response", response)
console.log("body", body)
))
start ()
And here is the JavaScript that it generated:
import request from "request";
import { some } from "fable-library/Option.js";
export function start() {
request("http://dr.dk", (error, response, body) => {
console.log(some("error"), error);
console.log(some("response"), response);
console.log(some("body"), body);
});
}
start();
Note that there are bindings for many modules already. For this particular task, I would suggest using Fable.Fetch. If you want a library that works in the browser and .NET, try Fable.SimpleHttp.

Problems gen_tcp:accept

I've made a tcp server witch spawns a process to listen incoming connections. Here is the sample code (removed a few things from my original code):
module a:
main([]) ->
{ ok, Pid } = b:start(),
receive
_ ->
ok
end.
module b:
-define(TCP_OPTIONS, [binary, { active, false}, { packet, 0 } , {reuseaddr, true}]).
...
start_link(Port) ->
Pid = spawn_link(server_listener, init, [ Port ]),
{ ok , self() }.
init(Port, Processor) ->
case gen_tcp:listen(Port, ?TCP_OPTIONS) of
{ ok , LSocket } ->
accept_loop(LSocket);
{ error, Reason } ->
{ stop, Reason }
end.
accept_loop( LSocket) ->
?LOG("Current socket acceptor PID [~w]~n", [self()]),
case gen_tcp:accept(LSocket) of
{ ok, Socket } ->
%do stuff here
spawn(server_listener , accept_loop, [ LSocket ]);
{ error, Reason } ->
?LOG("Error accepting socket! [ ~s ]~n", [ Reason ])
end.
The problem is: EVERY time that I try to connect from telnet on this port, I'm receiving an error { error, closed } on gen_tcp:accept. This is already driving me nuts trying to figure out what is happening.
Thanks,
Your "accept loop" isn't really a loop... and it is contrived.
You probably want "do_accept_loop" and a proper "server_loop" for handling a connection. Have a look at this.
You want something along the lines of:
% Call echo:listen(Port) to start the service.
listen(Port) ->
{ok, LSocket} = gen_tcp:listen(Port, ?TCP_OPTIONS),
accept(LSocket).
% Wait for incoming connections and spawn the echo loop when we get one.
accept(LSocket) ->
{ok, Socket} = gen_tcp:accept(LSocket),
spawn(fun() -> loop(Socket) end),
accept(LSocket).
% Echo back whatever data we receive on Socket.
loop(Socket) ->
case gen_tcp:recv(Socket, 0) of
{ok, Data} ->
gen_tcp:send(Socket, Data),
loop(Socket);
{error, closed} ->
ok
end.

Resources