All Downloads are FREE. Search and download functionalities are using the official Maven repository.

discord4j.rest.request.GlobalRateLimiter Maven / Gradle / Ivy

/*
 * This file is part of Discord4J.
 *
 * Discord4J is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Discord4J is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Discord4J.  If not, see .
 */
package discord4j.rest.request;

import reactor.core.Exceptions;
import reactor.core.publisher.Mono;

import java.time.Duration;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Supplier;

/**
 * Used to prevent requests from being sent while the bot is
 * globally rate limited.
 * 

* Provides a single resource that can be acquired through the use of {@link #withLimiter(Supplier)}, blocking all other * attempts until the {@link Mono} supplier completes or terminates with an error. *

* This rate limiter can have their delay directly indicated through {@link #rateLimitFor(Duration)}, determining the * duration a resource holder must wait before processing starts. *

* When subscribing to the rate limiter, the only guarantee is that the subscription will be completed at some point in * the future. If a global ratelimit is in effect, it will be completed when the cooldown ends. Otherwise, it is * completed immediately. */ public class GlobalRateLimiter { private final Semaphore outer = new Semaphore(8, true); private final Semaphore inner = new Semaphore(1, true); private final AtomicLong limitedUntil = new AtomicLong(0L); /** * Sets a new rate limit that will be applied to every new resource acquired. * * @param duration the {@link Duration} every new acquired resource should wait before being used */ public void rateLimitFor(Duration duration) { limitedUntil.set(System.nanoTime() + duration.toNanos()); } /** * Returns a {@link Mono} indicating that the rate limit has ended. * * @return a {@link Mono} that completes when the currently set limit has completed */ Mono onComplete() { return Mono.defer(this::notifier); } private Mono notifier() { long delayNanos = delayNanos(); if (delayNanos > 0) { return Mono.delay(Duration.ofNanos(delayNanos)).then(); } return Mono.empty(); } private long delayNanos() { return limitedUntil.get() - System.nanoTime(); } /** * Provides a scope to perform reactive operations under this limiter resources. Resources are acquired on * subscription and released when the given stage has completed or terminated with an error. * * @param stage a supplier containing a {@link Mono} that will manage this limiter resources * @param the type of the stage supplier * @return a {@link Mono} where each subscription represents acquiring a rate limiter resource */ public Mono withLimiter(Supplier> stage) { return Mono.usingWhen( acquire(), resource -> stage.get(), this::release, this::release); } private Mono acquire() { return Mono .fromCallable(() -> { try { outer.acquire(); if (delayNanos() > 0) { try { inner.acquire(); return new Resource(outer, inner); } catch (InterruptedException e) { throw Exceptions.propagate(e); } } return new Resource(outer, null); } catch (InterruptedException e) { throw Exceptions.propagate(e); } }) .delayUntil(resource -> onComplete()); } private Mono release(Resource resource) { return Mono.fromRunnable(() -> { if (resource.inner != null) { resource.inner.release(); } if (resource.outer != null) { resource.outer.release(); } }); } static class Resource { private final Semaphore outer; private final Semaphore inner; Resource(Semaphore outer, Semaphore inner) { this.outer = outer; this.inner = inner; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy