Skip to content

Bug: rate limiter blocks tokio executor thread via thread::sleep #50

Description

@ZialeHub

Problem

RateLimiter::sleep() in src/rate_limiter.rs uses std::thread::sleep:

fn sleep(&self) {
    match self.period {
        TimePeriod::Second => sleep(Duration::from_secs(1)),
        TimePeriod::Minute => sleep(Duration::from_secs(60)),
        // ...
    }
}

This is called from request() inside Request::send(), which is an async context running on a tokio executor thread. std::thread::sleep blocks the OS thread entirely, preventing tokio from scheduling any other task on that thread for the duration of the sleep. Under a default multi-thread tokio runtime this stalls one worker thread; under a single-thread runtime it stalls the entire application.

Expected behaviour

The rate limiter should yield the executor thread back to tokio while waiting, allowing other tasks to make progress.

Fix

Replace std::thread::sleep with tokio::time::sleep (async) and make request() an async function:

pub async fn request(&mut self) {
    loop {
        // ...
        if self.remaining > 0 {
            self.remaining -= 1;
            return;
        } else {
            self.is_asleep = true;
            log::info!("Rate limit exceeded, sleeping for {:?}", self.period);
            tokio::time::sleep(self.period.clone().into()).await;
            self.is_asleep = false;
        }
    }
}

TimePeriod::into::<std::time::Duration> already exists; tokio::time::sleep accepts std::time::Duration directly.

Impact

Every user of reqt in an async context is affected. Under high concurrency or long rate-limit periods (minute/hour/day), this bug can starve the entire tokio runtime.

Metadata

Metadata

Assignees

No one assigned

    Labels

    HOTFIXFix bug in productionrustPull requests that update rust code

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions