Adding delay between requests
liketoeatcheese opened this issue · comments
Hi,
Is there a way to add a delay between requests? Like 200 requests for one worker thread, wait for 5s between each request?
Sorry, you can do similar but there are limitations.
You can put -q
option to set query per second e.g. if you set -q 1
, oha
will do 1 request per second.
But it's not a delay, the actual delay would be shorter than 1 second.
And -q
option only accepts an integer, you cannot specify less than 1 query per second.
Thanks for clarifying :)
Hey hatoo,
Based on your comment, I looked into the code and want to see if I can contribute anything, as I use this quite often. One thing I noticed, and maybe a question on why it's being set up that way:
Looking at -q
, which is qps
, the function for processing that option is Client::work_until_with_qps
. Hypothetically, assuming there are 7 qps, and 5 workers. In that function, you create a queue with the same size as qps, and in the sender, you do a tokio sleep of an amount of time relative to qps and send it to the queue. And in the receiver end, 5 workers will constantly waiting to consume the data. However, isn't query per second
means every second, 7 requests are sent for processing? While this looks like one request is sent at a time with some time delayed between it, and all available workers consume it right away.
And in terms of delay
, I assume that every work*() related function should accept a delay: Option<human::Duration>
, and be processed by each worker before sending to the queue, and within the validation process with n_tasks
or deadline
with something like this?:
match delay {
Some(duration) => {
tokio::time::sleep(duration.into()).await;
return ();
}
None => (),
};
This way all workers will respect the delay. Let me know what you think :)
While this looks like one request is sent at a time with some time delayed between it, and all available workers consume it right away.
Sorry, I don't get that. I think this explanation seems right and it means we are sending 7 queries per second.
And in terms of delay, I assume that every work*() related function should accept a delay: Optionhuman::Duration, and be processed by each worker before sending to the queue, and within the validation process with n_tasks or deadline with something like this?:
Looks good, but I have some opinions
human::Duration
only be used in command line parsing.std::time::Duration
should be sent to work*() functionsdelay
should be processed in work*() functions because I want the client module to keep slim.
But at first, I want to know why you want to introduce delay rather than just setting query per second
:)
Reason why I want to introduce delay
I'm a data engineer at work, and often we do need to test some memory leak issues where we run a certain amount of loads but spread out, so for example, 200 requests
, 10 requests per second
(correct me if I'm wrong but I use 10 workers for this, as I assume 10 workers represent 10 different clients hitting the API at once which portrait better than qps
as qps
is more of a rate-limiting functionality - e.g: 10 qps means you can only do 10 querys per second max, no matter how many workers you have) and with a delay of 10 seconds
. Meaning every 10 seconds, send 10 requests until it reaches 200 requests. And I'd normally use SOAPUI for this as it's a feature, but I prefer to use oha
as it's normally my go to for load testing now.
QPS questionnaire
For some reason, probably it's because it was towards the end of the day, I misread the bounded queue (channel) of 7, your code is unbounded (flume::unbounded()
). But my question is still the same, so the code which I'm reading in client::work_with_qps
:
tokio::spawn(async move {
let start = std::time::Instant::now();
for i in 0..n_tasks {
tokio::time::sleep_until(
(start + i as u32 * std::time::Duration::from_secs(1) / qps as u32).into(),
)
.await;
tx.send_async(()).await.unwrap();
}
// tx gone
});
From my understanding, a green thread is spawned, and you loop through the number of tasks, tokio::time::sleep_until
will sleep to a point in time of: (start + i as u32 * std::time::Duration::from_secs(1) / qps as u32).into(), send to the queue (channel), and then go to the next task. Doesn't that mean you send one task every few seconds instead of sending a few tasks every one second? My thoughts would be you have a number of sender workers (e.g 7 qps would be 7 sender workers), send to the channel at once, wait till the next second, and send it again until you have finished all
n_tasks. And
n_workers` will be on the receiver end, so no matter how many workers there are, you will only send 7 requests per second, and 7 requests will be consumed.
Yours:
Reply on your opinions
- human::Duration only be used in command line parsing. std::time::Duration should be sent to work*() functions
human::Duration
do implement From<std::time::Duration>, so we can always use .into()
to convert. So that's fine
- delay should be processed in work*() functions because I want the client module to keep slim.
So do you mean you want to refactor work*() functions to a separate mod? As these functions are currently in client mod.
Ohhhhh, you spread one second to the number of smaller microseconds based on qps
. Ok, that makes sense. That's really smart! I finally understand the (start + i as u32 * std::time::Duration::from_secs(1) / qps as u32).into()
part
For that, yes you're right
So do you want the delay
feature still?
I think you can use qps
instead.
Sounds good.
Here is my rough idea.
- Add a command line argument, somewhat
--burst [delay] [requests]
- it's not compatible with
-q
- Extend
work*qps*
functions to receive an enum like the below instead of justqps
integer
enum QueryLimit {
Qps(usize),
Burst(Duration, usize)
}
- Update those function's sender part
I'm happy if you do or I'll do it this weekend.
I will do it and make a pull request when I'm done. I will update the comments if I have any questions
Hi @hatoo, I finished the code. I wrote it in a separate branch, how do I push this to remote and make a pull request?
Roughly, you should follow these steps.
Please add comments if any steps of these are unclear.
- Fork this repo in GitHub
- Add your ssh key to your GitHub account
- Run
git remote set-url origin ${YOUR_FORKED_REPO_URL}
in local - Push the branch to your forked repo
Making pull request button
should be appeared on GitHub
I created a pull request just now. Let me know if you have any questions or input