In RateLimiter example, why is `ask` being called twice?

I am learning about GenStage and trying to understand the example for managing code with rate limits here: gen_stage/rate_limiter.exs at main · elixir-lang/gen_stage · GitHub

In those two lines, it calls ask twice and I couldn’t understand why. I understand the purpose for L70, however. Perhaps my issue is that I don’t understand the point of the ask method. I tried re-reading the documentation a few times, but it doesn’t really give much insight.

https://hexdocs.pm/gen_stage/GenStage.html#ask/3

GenStage works with backpressure using a “demand” system. Consumers need to demand being sent events by communicating demand to their producer. Usually this “requesting events” happens automatically, but the example posted does setup the consumer to manually handle that.

Given a single producer and 10 events per 5s it works like this:

- Consumer: Start with 10 pending events
- Consumer: Communicate number of pending events to producer (10, using `GenStage.ask`)
- Consumer: Queue up the next interval after 5s
- Consumer: Set pending events to 0
- Producer: Produce 10 events
- Consumer: Handles 10 events and increments the number of pending events by the number of events
# After 5s interval timeout elapses
- Consumer: Communicate number of pending events to producer (10, using `GenStage.ask`)
- Consumer: Queue up the next interval after 5s
- Consumer: Set pending events to 0
- …

Given the consumer only ever communicates its demand in the interval configured at startup you get the rate limiting aspired. You can also see that GenStage.ask is not called twice. It’s called once per interval and the other line is just queuing up the next interval.

2 Likes

Okay that made things a lot more clear, thanks again!

Another point of confusion that I figured out was this line:

Process.send_after(self(), {:ask, from}, interval)

I wrongly thought this was saying, call the ask method on itself (presuming that use GenStage decorated an ask method into the current instance). However, that ask symbol is just arbitrary and can be anything and it’s the handle_info that triggers the ask again.