Need to start a code line after react subscribe() functions end - kotlin

val totalNumInst = TotalNumObj()
devSupportService.sendAllTalktalkMessages(naverId)
devSupportService.sendAllAutoDepositTalktalkMessages(naverId, totalNum)
logger.info("${totalNumInst.totalNum}")
Mono<>
.doOnSuccess { }
.subscribe()
First two lines execute several Mono<>.subscribe() functions. In each Mono<>'s .doOnSuccess{} the totalNum variable is increasing. At the last line, I added a log which shows totalNum. But the totalNum variable always shows the initial value, 0.
I need to leave a log which shows how many times does the Mono<>.subscribe() is executed.
Thank you for reading my question.

There are 2 ways of solving your issue. The blocking and non-blocking.
Blocking
create a countDownLatch, pass it to sendAllTalktalkMessages and sendAllAutoDepositTalktalkMessages, then wait for it being latched
val totalNumInst = TotalNumObj()
val latch = CountDownLatch(2)
devSupportService.sendAllTalktalkMessages(naverId, totalNumInst, latch)
devSupportService.sendAllAutoDepositTalktalkMessages(naverId, totalNumInst, latch)
if (!latch.await(30, TimeUnit.SECONDS)) {
throw TimeoutException("Waiting timed out")
}
logger.info("${totalNumInst.totalNum}")
And add latch.countDown() to each doOnSuccess (but I'd recommend to countDown in doFinally in case of the chain sending error signal)
Mono<>
.doOnSuccess { latch.countDown() }
.subscribe()
This is blocking solution, it is against reactive non-blocking concept.
Non-blocking
Make sendAllTalktalkMessages and sendAllAutoDepositTalktalkMessages returning Mono and zip them (moreover in that case you don't need to pass totalNumInst to them)
Mono.zip(
devSupportService.sendAllTalktalkMessages(naverId)
.map { 1 }
.onErrorResume { Mono.just(0) }
.defaultIfEmpty(0),
devSupportService.sendAllAutoDepositTalktalkMessages(naverId)
.map { 1 }
.onErrorResume { 0 }
.defaultIfEmpty(0)
) { counter1, counter2 -> counter1 + counter2 }
.subscribe { totalNum -> logger.info("$totalNum") }
in this realisation you count each success as 1 and each error or empty signal as 0.

Related

Unit testing infinite Flux speed of generation

I have Flux, which generates event in some pace (in infinite manner). I would like to use StepVerifier that after 5 seconds there is at least 2 events generated. How can I verify this behavior using StepVerifier?
Sample flux for testing can look like this:
public fluxTest() {
final AtomicLong counter = new AtomicLong(0);
final Random rnd = new Random();
final Flux<String> randomIntervalEmitter = Flux.generate(generator -> {
try {
final long counterDivided = counter.get() % 12;
if (counterDivided > 0) {
TimeUnit.SECONDS.sleep(rnd.nextInt(1, 10));
} else {
TimeUnit.MILLISECONDS.sleep(rnd.nextInt(1, 50));
}
generator.next("asdf " + counterDivided);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
final Flux<String> regularDummyUpdate = Flux.interval(Duration.ofSeconds(5)).map(e -> "" + (88 + (System.currentTimeMillis() % 104)));
final Flux<String> stringFluxWithSomePace = randomIntervalEmitter.mergeWith(regularDummyUpdate);
stringFluxWithSomePace.subscribe(System.out::println);
}
As stated in javadoc of StepVerifier:
The verification must be triggered after the terminal expectations
(completion, error, cancellation) have been declared, by calling one
of the verify() methods.
So when I have infinite source, I need to trigger some terminal operation. Complete and Error is emitted from source (I am not changing it in any way for tests) and Cancel can be triggered by subscriber (StepVerifier). So using thenCancel() and verify() on StepVerifier, I can achieve to assert only on the beginning of the infinite stream.
final var sv = StepVerifier.create(stringFluxWithSomePace.log())
// in 23 seconds should at least some "heartbeat" come at least three times
.expectSubscription()
.expectNextCount(3)
.thenCancel()
.verify(Duration.ofSeconds(23));
Nice thing is, that after receiving any 3 items, stream is cancelled and test finished (in my case usually under a second).

Kotlin: Is there a tool that allows me to control parallelism when executing suspend functions?

I'm trying to execute certain suspend function multiple times, in such a way that never more than N of these are being executed at the same time.
For those acquainted with Akka and Scala Streaming libraries, something like mapAsync.
I did my own implementation using one input channel (as in kotlin channels) and N output channels. But it seems cumbersome and not very efficient.
The code I'm currently using is somewhat like this:
val inChannel = Channel<T>()
val outChannels = (0..n).map{
Channel<T>()
}
launch{
var i = 0
for(t in inChannel){
outChannels[i].offer(t)
i = ((i+1)%n)
}
}
outChannels.forEach{outChannel ->
launch{
for(t in outChannel){
fn(t)
}
}
}
Of course it has error management and everything, but still...
Edit: I did the following test, and it failed.
test("Parallelism is correctly capped") {
val scope = CoroutineScope(Dispatchers.Default.limitedParallelism(3))
var num = 0
(1..100).map {
scope.launch {
num ++
println("started $it")
delay(Long.MAX_VALUE)
}
}
delay(500)
assertEquals(3,num)
}
You can use the limitedParallelism-function on a Dispatcher (experimental in v1.6.0), and use the returned dispatcher to call your asynchronous functions. The function returns a view over the original dispatcher which limits the parallelism to a limit you provide. You can use it like this:
val limit = 2 // Or some other number
val dispatcher = Dispatchers.Default
val limitedDispatcher = dispatcher.limitedParallelism(limit)
for (n in 0..100) {
scope.launch(limitedDispatcher) {
executeTask(n)
}
}
Your question, as asked, calls for #marstran's answer. If what you want is that no more than N coroutines are being actively executed at any given time (in parallel), then limitedParallelism is the way to go:
val maxThreads: Int = TODO("some max number of threads")
val limitedDispatcher = Dispatchers.Default.limitedParallelism(maxThreads)
elements.forEach { elt ->
scope.launch(limitedDispatcher) {
doSomething(elt)
}
}
Now, if what you want is to even limit concurrency, so that at most N coroutines are run concurrently (potentially interlacing), regardless of threads, you could use a Semaphore instead:
val maxConcurrency: Int = TODO("some max number of concurrency coroutines")
val semaphore = Semaphore(maxConcurrency)
elements.forEach { elt ->
scope.async {
semaphore.withPermit {
doSomething(elt)
}
}
}
You can also combine both approaches.
Other answers already explained that it depends whether you need to limit parallelism or concurrency. If you need to limit concurrency, then you can do this similarly to your original solution, but with only a single channel:
val channel = Channel<T>()
repeat(n) {
launch {
for(t in channel){
fn(t)
}
}
}
Also note that offer() in your example does not guarantee that the task will be ever executed. If the next consumer in the round robin is still occupied with the previous task, the new task is simply ignored.

Is it impossible to combine `repeat()` and `while()` to loop 3 times?

I am new to Kotlin. How do I combine repeat() and while() to loop? I know how to loop by using following 2 methods:
// Method 1
for (i in 1..3) {
fortune = getFortune(getBirthday())
println("\nYour fortune is: $fortune")
if (fortune.contains("Take it easy")) break;
}
// Method 2
var fortune: String = ""
var i: Int = 0 //i = 0
while (!fortune.contains("Take it easy") && i<3 ) {
fortune = getFortune(getBirthday())
println("\nYour fortune is: $fortune")
i += 1 // i++
}
But I don't know how to combine repeat() and while() to loop. What I wrote below does NOT exit the loop at the 3rd times but will exit ONLY when the fortune string contains "Take it easy".
What I want is to exit the loop at either the 3rd time of entering your birthday or the fortune string contains "Take it easy". What I wrote below doesn't exit the loop at the 3rd time of entering your birthday. Is it impossible to combine repeat() and while() to loop 3 times?
fun main(args: Array<String>) {
var fortune: String = ""
repeat(3) {
while (!fortune.contains("Take it easy")) {
fortune = getFortune(getBirthday())
println("\nYour fortune is: $fortune")
}
}
}
fun getBirthday(): Int {
print("\nEnter your birthday: ")
return readLine()?.toIntOrNull() ?: 1
}
fun getFortune(birthday: Int): String {
val fortunes = listOf("You will have a great day!",
"Things will go well for you today.",
"Enjoy a wonderful day of success.",
"Be humble and all will turn out well.",
"Today is a good day for exercising restraint.",
"Take it easy and enjoy life!",
"Treasure your friends, because they are your greatest fortune.")
val index = when (birthday) {
in 1..7 -> 4
28, 31 -> 2
else -> birthday.rem(fortunes.size)
}
return fortunes[index]
}
It doesn't make sense to combine both to make it loop 3 times. You would use one or the other. When you nest loops like this, it's like multiplying them. The outer repeat happens 3 times, so the inner while loop will loop all the way through from the beginning 3 times. However, in your example, your criterion for exiting the loop will be immediately satisfied the second two times so there will be no output from those subsequent runs.
Basically, if you have a repeating action but you have multiple conditions for when to finish it, the solution is not to add another loop, but instead you must add the additional logic for exiting the loop.
Since repeat is not a keyword, but rather it is a higher-order function, it is more complicated to exit the loop and doing so makes the code hard to read. So I don't recommend using repeat if you have any conditions for exiting the loop early. To exit it early, you have to do something like wrapping it in the run scope function so you can return from that scope function to break out of the loop:
fun main(args: Array<String>) {
var fortune: String = ""
run loop#{
repeat(3) {
if (fortune.contains("Take it easy")) {
return#loop
}
fortune = getFortune(getBirthday())
println("\nYour fortune is: $fortune")
}
}
}
Much simpler to use a for loop than using repeat if you want to break out early, as shown in #evadeflow's answer, since you can use the break keyword.
for(i in 0..2){
println(i);
}
I don't entirely understand what you're trying to accomplish, but I think you're saying you want to execute the loop until either:
The fortune string contains "Take it easy", or:
The loop has executed three times
I'm no Kotlin expert, but I don't believe it's possible to use a break statement inside a repeat loop, so you'll need to do something like this instead:
fun main(args: Array<String>) {
var fortune: String
for (i in 1..3) {
fortune = getFortune(getBirthday())
println("\nYour fortune is: $fortune")
if (fortune.contains("Take it easy")) {
break
}
}
}
This will execute the loop a maximum of three times, but it may exit earlier if fortune contains "Take it easy".

How to "Catch" both a non-numeric and Incomplete ArrayList capacity

I created an ArrayList that has a capacity of 5 Ints. I can get the if statement to run if its less than 5 but I can't seem to get the else statement to "Catch" Non-Numerics. For example if I enter 1,2,3,Hello; it will print "Wrong number of sales provided."
fun main(){
val stats = ArrayList<Int>(5)
println("Enter your numbers: ")
try {
while (stats.size < 5) {
stats.add(readLine()!!.toInt())
}
}catch (e: NumberFormatException){
if (stats.size != 5){
println("The wrong number of sales provided.")
}else{
println("All inputs should be numeric.")
}
exitProcess(status = 1)
}
calStats(stats)
}
fun calStats(sales: Collection<Int>){
val min = sales.minOrNull()
val max = sales.maxOrNull()
println("Min: $min\nMax: $max\nRange: ${(max!! - min!!)}\nAverage: ${(BigDecimal(sales.average()).setScale(0, RoundingMode.FLOOR))} ")
}
The problem is how you are handling your exception, in fact since you are checking the size of your array first, if you enter 1,2,3,'Hello' and there are 4 elements in this list it will output the wrong message.
You should nest your try ... catch block inside the while loop.
Actually the if (stats.size != 5) control is reduntant since the while loop will execute until stats has a size of 5, unless the NumberFormatException is thrown.
Try to edit your code like this:
fun main() {
val stats = ArrayList<Int>(5)
println("Enter your numbers: ")
while (stats.size < 5) {
try {
stats.add(readLine()!!.toInt())
} catch (e: NumberFormatException) {
println("All inputs should be numeric.")
exitProcess(status = 1)
}
}
calStats(stats)
}
Your logic loops through, reading lines and adding them until you've collected 5 values. As soon as it fails at parsing one of those lines as an Int, it throws a NumberFormatException and you hit the catch block.
The first thing the catch block does is check how many values you've successfully added to the stats list. If it's not exactly 5, it prints the "wrong number" error instead of the "inputs need to be numeric" one.
But if you think about it, the size is never going to be 5 when you hit the catch block - if you've added 5 items successfully, the while loop ends and there's no way it's going to throw. If you have 4 items and the 5th one fails, it doesn't get added, so you have 4 items when you hit the catch block.
If you need to do it this way, you probably want to keep a counter of how many lines you've read and refer to that. But you'll still throw once you hit the first non-numeric value (even if there's 5 of them to be read in total) and the counter will show how far you've got, not how many there are.
Probably the easiest way is to read in 5 lines to a list, and then transform them to Ints and add those to your collection. That way you can check if you have less than 5 before you start, and handle that case separately.
Something like
// create a list by calling readline() 5 times - produces null at EOF
val lines = List(5) { readLine() }
if (lines.contains(null)) { // handle your "not enough items" here }
// parse all lines as Ints - any that fail will be null
val stats = lines.map { it.toIntOrNull() } // or map(String::toIntOrNull)
if (stats.contains(null)) { // handle bad values here }
Kotlin's style tries to avoid exceptions, which is why you have functions like toIntOrNull alongside toInt - it lets you use nulls as a "failure value" that you can handle in normal code. But you can always throw an exception if you want (e.g. when you get a null line) and handle it in your catch block.

How to test a function's output (stdout/stderr) in unit tests

I have a simple function I want to test:
func (t *Thing) print(min_verbosity int, message string) {
if t.verbosity >= minv {
fmt.Print(message)
}
}
But how can I test what the function actually sends to standard output? Test::Output does what I want in Perl. I know I could write all my own boilerplate to do the same in Go (as described here):
orig = os.Stdout
r,w,_ = os.Pipe()
thing.print("Some message")
var buf bytes.Buffer
io.Copy(&buf, r)
w.Close()
os.Stdout = orig
if(buf.String() != "Some message") {
t.Error("Failure!")
}
But that's a lot of extra work for every single test. I'm hoping there's a more standard way, or perhaps an abstraction library to handle this.
One thing to also remember, there's nothing stopping you from writing functions to avoid the boilerplate.
For example I have a command line app that uses log and I wrote this function:
func captureOutput(f func()) string {
var buf bytes.Buffer
log.SetOutput(&buf)
f()
log.SetOutput(os.Stderr)
return buf.String()
}
Then used it like this:
output := captureOutput(func() {
client.RemoveCertificate("www.example.com")
})
assert.Equal(t, "removed certificate www.example.com\n", output)
Using this assert library: http://godoc.org/github.com/stretchr/testify/assert.
You can do one of three things. The first is to use Examples.
The package also runs and verifies example code. Example functions may include a concluding line comment that begins with "Output:" and is compared with the standard output of the function when the tests are run. (The comparison ignores leading and trailing space.) These are examples of an example:
func ExampleHello() {
fmt.Println("hello")
// Output: hello
}
The second (and more appropriate, IMO) is to use fake functions for your IO. In your code you do:
var myPrint = fmt.Print
func (t *Thing) print(min_verbosity int, message string) {
if t.verbosity >= minv {
myPrint(message) // N.B.
}
}
And in your tests:
func init() {
myPrint = fakePrint // fakePrint records everything it's supposed to print.
}
func Test...
The third is to use fmt.Fprintf with an io.Writer that is os.Stdout in production code, but bytes.Buffer in tests.
You could consider adding a return statement to your function to return the string that is actually printed out.
func (t *Thing) print(min_verbosity int, message string) string {
if t.verbosity >= minv {
fmt.Print(message)
return message
}
return ""
}
Now, your test could just check the returned string against an expected string (rather than the print out). Maybe a bit more in-line with Test Driven Development (TDD).
And, in your production code, nothing would need to change, since you don't have to assign the return value of a function if you don't need it.