How to limit the number of Mono waiting for completion? - java

In Project Reactor I have a set number of requests to be made in the format of Mono. In order to not overwhelm the target service, I need to limit the number of requests to at most X per second and more than that ensure that I have a maximum of N pending requests waiting for completion.
The time limiting part was easy tranforming a list of Monos in a Flux and using the function Flux#delayElement but I can't figure the second requisite.
How can I limit the number of Mono waiting for completion?

Related

How does producer process data in a queue in AWS SQS

I've a doubt, let's assume in consumer I've set the receive messages limit to 10 for queue, delay of 5 seconds and polling rate as 20 sec.
I want to know how my consumer processes the data, whether it process the total 10 data at once and then picks up another 10 or it process one data and simultaneously picks one from the queue?
I want to know how my consumer processes the data, whether it process the total 10 data at once and then picks up another 10 or it process one data and simultaneously picks one from the queue?
If you're asking about how batches get processed, the former is correct. If your consumer is a lambda function for example, the function would recieve a single "batch" of 10 messages per invocation. It would then have to process or fail to process all 10 before starting another batch.
If you are in fact using lambda, I'd suggest reading this guide for more basic information.

Spring batch integration remote chunking timeout for manager

In spring batch remote chunking, consider a situation where the worker consumers are not available. In that case the manager publishes number of items equal to the throttle limit and waits for the reply from the workers. I have the receiveTimeout for the MessagingTemplate set to 5000. This loop from ChunkMessageChannelItemWriter write() is running infinitely because after every 5 seconds, the MessagingTemplate receive() timeout and returns null here. It doesn't perform anything if the result is null and thus falls into infinite loop.
In this scenario, the Job remains in an UNKNOWN state. Is there any way to perform the timeout from the manager end if it doesn't get any reply from the workers for some amount of time and mark the job as failed?
Is there any way to perform the timeout from the manager end if it doesn't get any reply from the workers for some quite an amount of time and mark the job as failed?
You can set the maxWaitTimeouts on the ChunkMessageChannelItemWriter parameters for that.

Automatic rate adjustment in Reactor

TL;DR;
Is there a way to automatically adjust delay between elements in Project Reactor based on downstream health?
More details
I have an application that reads records from Kafka topic, sends an HTTP request for each one of them and writes the result to another Kafka topic. Reading and writing from/to Kafka is fast and easy, but the third party HTTP service is easily overwhelmed, so I use delayElements() with a value from a property file, which means that this value does not change during application runtime. Here's a code sample:
kafkaReceiver.receiveAutoAck()
.concatMap(identity())
.delayElements(ofMillis(delayElement))
.flatMap(message -> recordProcessingFunction.process(message.value()), messageRate)
.onErrorContinue(handleError())
.map(this::getSenderRecord)
.flatMap(kafkaSender::send)
However, the third party service might perform differently overtime and I'd like to be able to adjust this delay accordingly. Let's say, if I see that over 5% of requests fail over 10 second period, I would increase the delay. If it gets lower than 5% for over 10 sec, then I would reduce the delay again.
Is there an existing mechanism for that in Reactor? I can think of some creative solutions from my side, but was wondering if they (or someone else) already implemented that.
I don't think there is backpressure provided by any HTTP client, including netty. One option is to switch to RSocket, but if you are calling a third-party service, that may not be an option, I guess. You could tune a rate that works during most of the day and send the errored out message to another topic using doOnError or similar. Another receiver can process those messages with even higher delays, put the message back on the same topic with a retry count if it errors out again so that you can finally stop processing them.
If you are looking for delaying elements depends on the elements processing speed, you could use delayUntil.
Flux.range(1, 100)
.doOnNext(i -> System.out.println("Kafka Receive :: " + i))
.delayUntil(i -> Mono.fromSupplier(() -> i)
.map(k -> {
// msg processing
return k * 2;
})
.delayElement(Duration.ofSeconds(1)) // msg processing simulation
.doOnNext(k -> System.out.println("Kafka send :: " + k)))
.subscribe();
You can add a retry with exponential backoff. Somethign like this:
influx()
.flatMap(x -> Mono.just(x)
.map(data -> apiCall(data))
.retryWhen(
Retry.backoff(Integet.MAX_VALUE, Duration.ofSeconds(30))
.filter(err -> err instanceof RuntimeException)
.doBeforeRetry(
s -> log.warn("Retrying for err {}", s.failure().getMessage()))
.onRetryExhaustedThrow((spec, sig) -> new RuntimeException("ex")))
.onErrorResume(err -> Mono.empty()),
concurrency_val,
prefetch_val)
This will retry the failed request Integet.MAX_VALUE times with minimum time of 30s between each retry. The subsequent retries are actually offset by a configurable jitter factor (default value = 0.5) causing the duration to increase between successive retries.
The documentation on Retry.backoff says that:
A RetryBackoffSpec preconfigured for exponential backoff strategy with jitter, given a maximum number of retry attempts and a minimum Duration for the backoff.
Also, since the whole operation is mapped in flatMap, you can vary the default concurrency and prefetch values for it in order to account for the maximum number of requests that can fail at any given time while the whole pipeline waits for the RetryBackOffSpec to complete successfully.
Worst case scenario, your concurrency_val number of requests have failed and waiting for 30+ seconds for the retry to happen. The whole operation might halt down (still waiting for success from downstream) which may not be desirable if the downstream system don't recover in time. Better to replace backOff limit from Integer.MAX_VALUE to something managable beyond which it would just log the error and proceed with next event.

Jmeter multiple concurrent users in single thread group

I have 1 thread group and I have defined 100 threads and 1 Iteration with a single HttpSampler. Basically I am testing a single GET API.
Now, Jmeter should start 100 threads and then they should fire request to my server which have the API. The server can respond to 100 requests concurrently. So, basically at any point in time I should have 100 concurrency.
But that is not what is happening when I checked through Blazemeter. I get a max users of 37 and total users as 100 which means max concurrency during the test was 37.
This can be possible only if Jmeter did not executed the threads parallel. So where am I wrong ?
I want all threads to execute parallel after they all are created and fire requests at once so that maximum concurrency is 100 for 1 iteration.
If you need more control and accuracy use Ultimate Thread Group JMeter plugin (instead of regular Thread Group)
Set Start Thread Count as 100, with 0 initial delay and 0 Startup Time, with positive Hold time, your thread will hold 100 max users
General example:
If your computer can't handle generating load, you may need distributed testing setup
It is not suggested to use Ramp-Up period as 0.
I think you are making confusion between concurrency (Related to virtual users) and simultaneous (Related to requests or samplers).
To hit requests simultaneously, use the Synchronizing Timer as a child of your requests. It will pause X number of threads and then released at once. And before that, to maintain the concurrency at 100 users, try to use the ramp-up time accordingly (i.e 10 seconds). So it will take 10 seconds for 100 users alive on the server and then hit the requests for 100 users simultaneously.
It doesn't matter which thread group you use but if you maintain the concurrency for more period of time (hold that concurrency), then use Ultimate Thread Group or you can use the loop count accordingly.
If you want to perform spike testing, then the regular Thread group is fine. But you have to remember that, some of your threads might already finish their work and were shut down so you won't see the expected number of the concurrent user.
Here are the example screenshots for 1 minute Test duration (100 users ramp-up time 30 sec + hold load time 20 sec + 10 sec for ramp downtime)
Ultimate Thread Group Config:
Test Results (100 requests at once):
Test Results (100 Concurrent users):
Hope it helps you to understand.
To achieve this, you can use Synchronizing_Timer. Add Synchronization Timer as a child of your GET Request.
The purpose of the SyncTimer is to block threads until X number of
threads have been blocked, and then they are all released at once. A
SyncTimer can thus create large instant loads at various points of the
test plan.
Secondly, to keep a constant load of 100 Request Per Second/Hit Per Second for a time duration, you can use Throughput Shaping Timer. Make sure, you add the Loop Count to Forever and Duration accordingly in Thread Group.
JMeter acts as follows:
The number of threads specified in the Thread Group is being kicked off during the ramp-up period
Each thread starts executing Samplers upside down (or according to the Logic Controllers)
When thread doesn't have any more Samplers to execute or loops to iterate it's being shut down
Assuming all above you may run into the situation when some threads have already finished their work and were shut down and some haven't yet been started. Check out JMeter Test Results: Why the Actual Users Number is Lower than Expected article for more comprehensive explanation if needed
Therefore the solutions are in:
Provide more "iterations" on Thread Group level to let your users loop over, this way you will have 100 concurrent users
If you need to perform some form of Spike Testing and don't want/cannot increase the number of loops just use Synchronizing Timer, this way JMeter will pause the threads until the desired amount is reached and release them at exactly the same moment

How to create Java concurrent queue from which we can blocking-take more than 1 element in single call?

Background: I need to send many small-size messages to WebSocket clients in asynchronous way. Messages are usually sent in peak, so after some pause I need to send ~5000 messages fast. So the problem is:
I don't want to start 5000 async's in single thread
I don't want to loop "start async"-"wait for complete" 5000 times in serial
I don't want to use 5000 threads, with single "start async"-"wait for complete" per thread
The best way would be to group ~20 asyncs per thread, so I need very specific queue:
lot of means concurrent push/poll in queue
small-sized asynchronous means I want to poll in bundles, like 1 to 20 messages per queue take() (so I can start 1...20 async I/O and wait for completness in single thread)
immediately means that I dont want to wait until 20 messages will be polled, bundle-poll should be used only if queue has lot of messages. Single message should be polled and sent immediately.
So basically: I need structure like queue that has blocking take(1 to X) waiting elements in single blocking call. Pseudocode:
[each of ~50 processing threads]:
messages = queue.blockingTake( max 10 or at least 1 if less than 10 available );
for each message: message.startAsync()
for each message: message.waitToComplete()
repeat
I wouldn't implement a Queue from scratch if it's not really necessary. A few ideas if you're interested:
Queue> if you have only 1 thread doing the offers. If you have more, the collection has to be sync'd. Like, one offerer peek()-s into the queue, sees that the last collection has too many elements so it creates a new one and offers it.
or
A number of running threads where the runnables take elements one by one from the queue.
or
1 queue per sending thread, if you keep the queue references you can then add elements to each of them in a round robin fashion.
or
subclass a BlockingQueue of your choice and create a "Collection take(int i)" method with a rewritten version of the normal take().

Categories

Resources