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

net.pincette.mongo.streams.Throttle Maven / Gradle / Ivy

The newest version!
package net.pincette.mongo.streams;

import static java.time.Duration.between;
import static java.time.Instant.now;
import static java.time.temporal.ChronoUnit.SECONDS;
import static java.util.concurrent.CompletableFuture.completedFuture;
import static net.pincette.json.JsonUtil.isObject;
import static net.pincette.rs.Async.mapAsyncSequential;
import static net.pincette.util.ScheduledCompletionStage.supplyAsyncAfter;
import static net.pincette.util.Util.must;

import java.time.Instant;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.Flow.Processor;
import javax.json.JsonObject;
import javax.json.JsonValue;
import net.pincette.rs.streams.Message;

/**
 * The $throttle operator.
 *
 * @author Werner Donné
 * @since 3.0
 */
class Throttle {
  private static final String MAX_PER_SECOND = "maxPerSecond";

  private Throttle() {}

  static Processor, Message> stage(
      final JsonValue expression) {
    must(isObject(expression));

    final Running running = new Running(expression.asJsonObject().getInt(MAX_PER_SECOND));

    return mapAsyncSequential(m -> updateRunning(running).thenApply(r -> m));
  }

  private static CompletionStage updateRunning(final Running running) {
    final Instant now = now().truncatedTo(SECONDS);

    if (between(running.second, now).toMillis() > 999) {
      running.count = 0;
      running.second = now;
    }

    return ++running.count >= running.maxPerSecond
        ? supplyAsyncAfter(() -> true, between(now(), now.plusSeconds(1)))
        : completedFuture(true);
  }

  private static class Running {
    private final int maxPerSecond;
    private long count = 0;
    private Instant second = now().truncatedTo(SECONDS);

    private Running(final int maxPerSecond) {
      this.maxPerSecond = maxPerSecond;
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy