Concurrency & Returnable Limits Try it

Sometimes you don't care exactly what the rate-limit is, but you want to ensure that something is only happening N times at once.

You can achieve this with Ratlim.it in much the same way as a rate limit, but with the additional tweak of declaring that your limit is returnable.

When you mark your LimitDefinition as returnable, the burst value is used as the total number of concurrent tokens and the limit and limitPolicy are used to define the token expiration.

limiter = RateLimit::Limiter.new(apikey: "ACCT_ID|APIKEY")

# only need to do this on startup
# allow 2 tokens to be taken out at once
# allow tokens to be returned
# if processes don't return their token in 30 sec, they will become available again
limiter.upsert_concurrency_limit("concurrent", 2, 30)

5.times do |i|
  Thread.new do
    begin
      limit_result = limiter.acquire_or_wait(key: "concurrent", acquire_amount: 1, wait_in_secs: 10)
      sleep(2)
      limiter.return(limit_result)
    rescue RateLimIt::WaitExceeded => e
      puts "never ran #{i}"
    end
  end
end
apiClient.limitCreate(RateLimitProtos.LimitDefinition.newBuilder()
    .setGroup("oneAtATime")
    .setLimit(2)  // 2 per minute = 30 sec = timeout on token leases
    .setPolicyName(RateLimitProtos.LimitResponse.LimitPolicyNames.MINUTELY_ROLLING)
    .setBurst(1) // one concurrent
    .build()
);

Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder()
        .retryIfResult(Predicates.alwaysFalse())
        .withWaitStrategy(WaitStrategies.exponentialWait(100, 1, TimeUnit.MINUTES))
        .withStopStrategy(StopStrategies.stopAfterAttempt(3))
        .build();


Callable<Boolean> callable = () -> {
  final RateLimitProtos.LimitResponse limitResponse = apiClient.limitCheck(RateLimitProtos.LimitRequest.newBuilder()
      .addGroups("oneAtATime:my_concurrent_process")
      .build());
  if(limitResponse.getPassed()){
    System.out.println("I was able to do the thing!");
    apiClient.limitReturn(limitResponse); //give the token back
    return true;
  }else{
    return false;
  }
};
retryer.call(callable);

Read about Batches & Acquiring More Than One Token