Implement throttle for Source(Flow)WithContext (#29107)
This commit is contained in:
parent
34b9a26b98
commit
6328e0a6d6
6 changed files with 449 additions and 0 deletions
|
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2018-2020 Lightbend Inc. <https://www.lightbend.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
package akka.stream.javadsl;
|
||||||
|
|
||||||
|
import akka.NotUsed;
|
||||||
|
import akka.japi.Pair;
|
||||||
|
import akka.stream.StreamTest;
|
||||||
|
import akka.stream.ThrottleMode;
|
||||||
|
import akka.testkit.AkkaJUnitActorSystemResource;
|
||||||
|
import akka.testkit.AkkaSpec;
|
||||||
|
import org.junit.ClassRule;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CompletionStage;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
public class FlowWithContextThrottleTest extends StreamTest {
|
||||||
|
|
||||||
|
public FlowWithContextThrottleTest() {
|
||||||
|
super(actorSystemResource);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ClassRule
|
||||||
|
public static AkkaJUnitActorSystemResource actorSystemResource =
|
||||||
|
new AkkaJUnitActorSystemResource("ThrottleTest", AkkaSpec.testConf());
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void mustWorksForTwoStreams() throws Exception {
|
||||||
|
final FlowWithContext<Integer, String, Integer, String, NotUsed> sharedThrottle =
|
||||||
|
FlowWithContext.<Integer, String>create()
|
||||||
|
.throttle(1, java.time.Duration.ofDays(1), 1, (a) -> 1, ThrottleMode.enforcing());
|
||||||
|
|
||||||
|
CompletionStage<List<Pair<Integer, String>>> result1 =
|
||||||
|
Source.single(new Pair<>(1, "context-a"))
|
||||||
|
.via(sharedThrottle.asFlow())
|
||||||
|
.via(sharedThrottle.asFlow())
|
||||||
|
.runWith(Sink.seq(), system);
|
||||||
|
|
||||||
|
// If there is accidental shared state then we would not be able to pass through the single
|
||||||
|
// element
|
||||||
|
List<Pair<Integer, String>> pairs1 = result1.toCompletableFuture().get(3, TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
assertEquals(1, pairs1.size());
|
||||||
|
assertEquals(Integer.valueOf(1), pairs1.get(0).first());
|
||||||
|
assertEquals("context-a", pairs1.get(0).second());
|
||||||
|
|
||||||
|
// It works with a new stream, too
|
||||||
|
CompletionStage<List<Pair<Integer, String>>> result2 =
|
||||||
|
Source.single(new Pair<>(2, "context-b"))
|
||||||
|
.via(sharedThrottle.asFlow())
|
||||||
|
.via(sharedThrottle.asFlow())
|
||||||
|
.runWith(Sink.seq(), system);
|
||||||
|
|
||||||
|
List<Pair<Integer, String>> pairs2 = result2.toCompletableFuture().get(3, TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
assertEquals(1, pairs2.size());
|
||||||
|
assertEquals(Integer.valueOf(2), pairs2.get(0).first());
|
||||||
|
assertEquals("context-b", pairs2.get(0).second());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2018-2020 Lightbend Inc. <https://www.lightbend.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
package akka.stream.javadsl;
|
||||||
|
|
||||||
|
import akka.japi.Pair;
|
||||||
|
import akka.stream.StreamTest;
|
||||||
|
import akka.stream.ThrottleMode;
|
||||||
|
import akka.testkit.AkkaJUnitActorSystemResource;
|
||||||
|
import akka.testkit.AkkaSpec;
|
||||||
|
import org.junit.ClassRule;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
public class SourceWithContextThrottleTest extends StreamTest {
|
||||||
|
|
||||||
|
public SourceWithContextThrottleTest() {
|
||||||
|
super(actorSystemResource);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ClassRule
|
||||||
|
public static AkkaJUnitActorSystemResource actorSystemResource =
|
||||||
|
new AkkaJUnitActorSystemResource("ThrottleTest", AkkaSpec.testConf());
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void mustBeAbleToUseThrottle() throws Exception {
|
||||||
|
List<Pair<Integer, String>> list =
|
||||||
|
Arrays.asList(
|
||||||
|
new Pair<>(0, "context-a"), new Pair<>(1, "context-b"), new Pair<>(2, "context-c"));
|
||||||
|
Pair<Integer, String> result =
|
||||||
|
SourceWithContext.fromPairs(Source.from(list))
|
||||||
|
.throttle(10, Duration.ofSeconds(1), 10, ThrottleMode.shaping())
|
||||||
|
.throttle(10, Duration.ofSeconds(1), 10, ThrottleMode.enforcing())
|
||||||
|
.runWith(Sink.head(), system)
|
||||||
|
.toCompletableFuture()
|
||||||
|
.get(3, TimeUnit.SECONDS);
|
||||||
|
|
||||||
|
assertEquals(list.get(0), result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,208 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2015-2020 Lightbend Inc. <https://www.lightbend.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
package akka.stream.scaladsl
|
||||||
|
|
||||||
|
import akka.stream.ThrottleMode.Shaping
|
||||||
|
import akka.stream.testkit._
|
||||||
|
import akka.stream.testkit.scaladsl.StreamTestKit._
|
||||||
|
import akka.stream.testkit.scaladsl.TestSink
|
||||||
|
|
||||||
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
|
class FlowWithContextThrottleSpec extends StreamSpec("""
|
||||||
|
akka.stream.materializer.initial-input-buffer-size = 2
|
||||||
|
akka.stream.materializer.max-input-buffer-size = 2
|
||||||
|
""") {
|
||||||
|
|
||||||
|
private def toMessage(i: Int) = Message(s"data-$i", i.toLong)
|
||||||
|
|
||||||
|
private def genMessage(length: Int, i: Int) = Message("a" * length, i.toLong)
|
||||||
|
|
||||||
|
"throttle() on FlowWithContextOps" must {
|
||||||
|
"on FlowWithContext" must {
|
||||||
|
"work for the happy case" in assertAllStagesStopped {
|
||||||
|
val throttle = FlowWithContext[Message, Long].throttle(19, 1000.millis, -1, Shaping)
|
||||||
|
val input = (1 to 5).map(toMessage)
|
||||||
|
val expected = input.map(message => (message, message.offset))
|
||||||
|
|
||||||
|
Source(input)
|
||||||
|
.asSourceWithContext(m => m.offset)
|
||||||
|
.via(throttle)
|
||||||
|
.asSource
|
||||||
|
.runWith(TestSink.probe[(Message, Long)])
|
||||||
|
.request(5)
|
||||||
|
.expectNextN(expected)
|
||||||
|
.expectComplete()
|
||||||
|
}
|
||||||
|
|
||||||
|
"accept very high rates" in assertAllStagesStopped {
|
||||||
|
val throttle = FlowWithContext[Message, Long].throttle(1, 1.nanos, 0, Shaping)
|
||||||
|
val input = (1 to 5).map(toMessage)
|
||||||
|
val expected = input.map(message => (message, message.offset))
|
||||||
|
|
||||||
|
Source(input)
|
||||||
|
.asSourceWithContext(m => m.offset)
|
||||||
|
.via(throttle)
|
||||||
|
.asSource
|
||||||
|
.runWith(TestSink.probe[(Message, Long)])
|
||||||
|
.request(5)
|
||||||
|
.expectNextN(expected)
|
||||||
|
.expectComplete()
|
||||||
|
}
|
||||||
|
|
||||||
|
"accept very low rates" in assertAllStagesStopped {
|
||||||
|
val throttle = FlowWithContext[Message, Long].throttle(1, 100.days, 1, Shaping)
|
||||||
|
val input = (1 to 5).map(toMessage)
|
||||||
|
val expected = (input.head, input.head.offset)
|
||||||
|
|
||||||
|
Source(input)
|
||||||
|
.asSourceWithContext(m => m.offset)
|
||||||
|
.via(throttle)
|
||||||
|
.asSource
|
||||||
|
.runWith(TestSink.probe[(Message, Long)])
|
||||||
|
.request(5)
|
||||||
|
.expectNext(expected)
|
||||||
|
.expectNoMessage(100.millis)
|
||||||
|
.cancel() // We won't wait 100 days, sorry
|
||||||
|
}
|
||||||
|
|
||||||
|
"emit single element per tick" in assertAllStagesStopped {
|
||||||
|
val upstream = TestPublisher.probe[Message]()
|
||||||
|
val downstream = TestSubscriber.probe[(Message, Long)]()
|
||||||
|
val throttle = FlowWithContext[Message, Long].throttle(1, 300.millis, 0, Shaping)
|
||||||
|
|
||||||
|
Source
|
||||||
|
.fromPublisher(upstream)
|
||||||
|
.asSourceWithContext(m => m.offset)
|
||||||
|
.via(throttle)
|
||||||
|
.asSource
|
||||||
|
.runWith(Sink.fromSubscriber(downstream))
|
||||||
|
|
||||||
|
downstream.request(20)
|
||||||
|
upstream.sendNext(Message("a", 1L))
|
||||||
|
downstream.expectNoMessage(150.millis)
|
||||||
|
downstream.expectNext((Message("a", 1L), 1L))
|
||||||
|
|
||||||
|
upstream.sendNext(Message("b", 2L))
|
||||||
|
downstream.expectNoMessage(150.millis)
|
||||||
|
downstream.expectNext((Message("b", 2L), 2L))
|
||||||
|
|
||||||
|
upstream.sendComplete()
|
||||||
|
downstream.expectComplete()
|
||||||
|
}
|
||||||
|
|
||||||
|
"emit elements according to cost" in assertAllStagesStopped {
|
||||||
|
val list = (1 to 4).map(i => genMessage(i * 2, i))
|
||||||
|
val throttle = FlowWithContext[Message, Long].throttle(2, 200.millis, 0, _.data.length, Shaping)
|
||||||
|
|
||||||
|
Source(list)
|
||||||
|
.asSourceWithContext(m => m.offset)
|
||||||
|
.via(throttle)
|
||||||
|
.asSource
|
||||||
|
.map(_._1)
|
||||||
|
.runWith(TestSink.probe[Message])
|
||||||
|
.request(4)
|
||||||
|
.expectNext(list(0))
|
||||||
|
.expectNoMessage(300.millis)
|
||||||
|
.expectNext(list(1))
|
||||||
|
.expectNoMessage(500.millis)
|
||||||
|
.expectNext(list(2))
|
||||||
|
.expectNoMessage(700.millis)
|
||||||
|
.expectNext(list(3))
|
||||||
|
.expectComplete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"on SourceWithContext" must {
|
||||||
|
"work for the happy case" in assertAllStagesStopped {
|
||||||
|
val input = (1 to 5).map(toMessage)
|
||||||
|
val expected = input.map(message => (message, message.offset))
|
||||||
|
|
||||||
|
Source(input)
|
||||||
|
.asSourceWithContext(m => m.offset)
|
||||||
|
.throttle(19, 1000.millis, -1, Shaping)
|
||||||
|
.asSource
|
||||||
|
.runWith(TestSink.probe[(Message, Long)])
|
||||||
|
.request(5)
|
||||||
|
.expectNextN(expected)
|
||||||
|
.expectComplete()
|
||||||
|
}
|
||||||
|
|
||||||
|
"accept very high rates" in assertAllStagesStopped {
|
||||||
|
val input = (1 to 5).map(toMessage)
|
||||||
|
val expected = input.map(message => (message, message.offset))
|
||||||
|
|
||||||
|
Source(input)
|
||||||
|
.asSourceWithContext(m => m.offset)
|
||||||
|
.throttle(1, 1.nanos, 0, Shaping)
|
||||||
|
.asSource
|
||||||
|
.runWith(TestSink.probe[(Message, Long)])
|
||||||
|
.request(5)
|
||||||
|
.expectNextN(expected)
|
||||||
|
.expectComplete()
|
||||||
|
}
|
||||||
|
|
||||||
|
"accept very low rates" in assertAllStagesStopped {
|
||||||
|
val input = (1 to 5).map(toMessage)
|
||||||
|
val expected = (input.head, input.head.offset)
|
||||||
|
|
||||||
|
Source(input)
|
||||||
|
.asSourceWithContext(m => m.offset)
|
||||||
|
.throttle(1, 100.days, 1, Shaping)
|
||||||
|
.asSource
|
||||||
|
.runWith(TestSink.probe[(Message, Long)])
|
||||||
|
.request(5)
|
||||||
|
.expectNext(expected)
|
||||||
|
.expectNoMessage(100.millis)
|
||||||
|
.cancel() // We won't wait 100 days, sorry
|
||||||
|
}
|
||||||
|
|
||||||
|
"emit single element per tick" in assertAllStagesStopped {
|
||||||
|
val upstream = TestPublisher.probe[Message]()
|
||||||
|
val downstream = TestSubscriber.probe[(Message, Long)]()
|
||||||
|
|
||||||
|
Source
|
||||||
|
.fromPublisher(upstream)
|
||||||
|
.asSourceWithContext(m => m.offset)
|
||||||
|
.throttle(1, 300.millis, 0, Shaping)
|
||||||
|
.asSource
|
||||||
|
.runWith(Sink.fromSubscriber(downstream))
|
||||||
|
|
||||||
|
downstream.request(20)
|
||||||
|
upstream.sendNext(Message("a", 1L))
|
||||||
|
downstream.expectNoMessage(150.millis)
|
||||||
|
downstream.expectNext((Message("a", 1L), 1L))
|
||||||
|
|
||||||
|
upstream.sendNext(Message("b", 2L))
|
||||||
|
downstream.expectNoMessage(150.millis)
|
||||||
|
downstream.expectNext((Message("b", 2L), 2L))
|
||||||
|
|
||||||
|
upstream.sendComplete()
|
||||||
|
downstream.expectComplete()
|
||||||
|
}
|
||||||
|
|
||||||
|
"emit elements according to cost" in assertAllStagesStopped {
|
||||||
|
val list = (1 to 4).map(i => genMessage(i * 2, i))
|
||||||
|
|
||||||
|
Source(list)
|
||||||
|
.asSourceWithContext(m => m.offset)
|
||||||
|
.throttle(2, 200.millis, 0, _.data.length, Shaping)
|
||||||
|
.asSource
|
||||||
|
.map(_._1)
|
||||||
|
.runWith(TestSink.probe[Message])
|
||||||
|
.request(4)
|
||||||
|
.expectNext(list(0))
|
||||||
|
.expectNoMessage(300.millis)
|
||||||
|
.expectNext(list(1))
|
||||||
|
.expectNoMessage(500.millis)
|
||||||
|
.expectNext(list(2))
|
||||||
|
.expectNoMessage(700.millis)
|
||||||
|
.expectNext(list(3))
|
||||||
|
.expectComplete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -14,6 +14,7 @@ import akka.japi.{ function, Pair, Util }
|
||||||
import akka.stream._
|
import akka.stream._
|
||||||
import akka.util.ConstantFun
|
import akka.util.ConstantFun
|
||||||
import akka.util.ccompat.JavaConverters._
|
import akka.util.ccompat.JavaConverters._
|
||||||
|
import akka.util.JavaDurationConverters._
|
||||||
|
|
||||||
object FlowWithContext {
|
object FlowWithContext {
|
||||||
|
|
||||||
|
|
@ -286,6 +287,50 @@ final class FlowWithContext[In, CtxIn, Out, CtxOut, +Mat](
|
||||||
marker: function.Function2[Out, CtxOut, LogMarker]): FlowWithContext[In, CtxIn, Out, CtxOut, Mat] =
|
marker: function.Function2[Out, CtxOut, LogMarker]): FlowWithContext[In, CtxIn, Out, CtxOut, Mat] =
|
||||||
this.logWithMarker(name, marker, ConstantFun.javaIdentityFunction[Out], null)
|
this.logWithMarker(name, marker, ConstantFun.javaIdentityFunction[Out], null)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Context-preserving variant of [[akka.stream.javadsl.Flow.throttle]].
|
||||||
|
*
|
||||||
|
* @see [[akka.stream.javadsl.Flow.throttle]]
|
||||||
|
*/
|
||||||
|
def throttle(elements: Int, per: java.time.Duration): FlowWithContext[In, CtxIn, Out, CtxOut, Mat] =
|
||||||
|
viaScala(_.throttle(elements, per.asScala))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Context-preserving variant of [[akka.stream.javadsl.Flow.throttle]].
|
||||||
|
*
|
||||||
|
* @see [[akka.stream.javadsl.Flow.throttle]]
|
||||||
|
*/
|
||||||
|
def throttle(
|
||||||
|
elements: Int,
|
||||||
|
per: java.time.Duration,
|
||||||
|
maximumBurst: Int,
|
||||||
|
mode: ThrottleMode): FlowWithContext[In, CtxIn, Out, CtxOut, Mat] =
|
||||||
|
viaScala(_.throttle(elements, per.asScala, maximumBurst, mode))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Context-preserving variant of [[akka.stream.javadsl.Flow.throttle]].
|
||||||
|
*
|
||||||
|
* @see [[akka.stream.javadsl.Flow.throttle]]
|
||||||
|
*/
|
||||||
|
def throttle(
|
||||||
|
cost: Int,
|
||||||
|
per: java.time.Duration,
|
||||||
|
costCalculation: function.Function[Out, Integer]): FlowWithContext[In, CtxIn, Out, CtxOut, Mat] =
|
||||||
|
viaScala(_.throttle(cost, per.asScala, costCalculation.apply))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Context-preserving variant of [[akka.stream.javadsl.Flow.throttle]].
|
||||||
|
*
|
||||||
|
* @see [[akka.stream.javadsl.Flow.throttle]]
|
||||||
|
*/
|
||||||
|
def throttle(
|
||||||
|
cost: Int,
|
||||||
|
per: java.time.Duration,
|
||||||
|
maximumBurst: Int,
|
||||||
|
costCalculation: function.Function[Out, Integer],
|
||||||
|
mode: ThrottleMode): FlowWithContext[In, CtxIn, Out, CtxOut, Mat] =
|
||||||
|
viaScala(_.throttle(cost, per.asScala, maximumBurst, costCalculation.apply, mode))
|
||||||
|
|
||||||
def asScala: scaladsl.FlowWithContext[In, CtxIn, Out, CtxOut, Mat] =
|
def asScala: scaladsl.FlowWithContext[In, CtxIn, Out, CtxOut, Mat] =
|
||||||
scaladsl.FlowWithContext.fromTuples(
|
scaladsl.FlowWithContext.fromTuples(
|
||||||
scaladsl
|
scaladsl
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import akka.japi.function
|
||||||
import akka.stream._
|
import akka.stream._
|
||||||
import akka.util.ConstantFun
|
import akka.util.ConstantFun
|
||||||
import akka.util.ccompat.JavaConverters._
|
import akka.util.ccompat.JavaConverters._
|
||||||
|
import akka.util.JavaDurationConverters._
|
||||||
|
|
||||||
object SourceWithContext {
|
object SourceWithContext {
|
||||||
|
|
||||||
|
|
@ -266,6 +267,50 @@ final class SourceWithContext[+Out, +Ctx, +Mat](delegate: scaladsl.SourceWithCon
|
||||||
def logWithMarker(name: String, marker: function.Function2[Out, Ctx, LogMarker]): SourceWithContext[Out, Ctx, Mat] =
|
def logWithMarker(name: String, marker: function.Function2[Out, Ctx, LogMarker]): SourceWithContext[Out, Ctx, Mat] =
|
||||||
this.logWithMarker(name, marker, ConstantFun.javaIdentityFunction[Out], null)
|
this.logWithMarker(name, marker, ConstantFun.javaIdentityFunction[Out], null)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Context-preserving variant of [[akka.stream.javadsl.Source.throttle]].
|
||||||
|
*
|
||||||
|
* @see [[akka.stream.javadsl.Source.throttle]]
|
||||||
|
*/
|
||||||
|
def throttle(elements: Int, per: java.time.Duration): SourceWithContext[Out, Ctx, Mat] =
|
||||||
|
viaScala(_.throttle(elements, per.asScala))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Context-preserving variant of [[akka.stream.javadsl.Source.throttle]].
|
||||||
|
*
|
||||||
|
* @see [[akka.stream.javadsl.Source.throttle]]
|
||||||
|
*/
|
||||||
|
def throttle(
|
||||||
|
elements: Int,
|
||||||
|
per: java.time.Duration,
|
||||||
|
maximumBurst: Int,
|
||||||
|
mode: ThrottleMode): SourceWithContext[Out, Ctx, Mat] =
|
||||||
|
viaScala(_.throttle(elements, per.asScala, maximumBurst, mode))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Context-preserving variant of [[akka.stream.javadsl.Source.throttle]].
|
||||||
|
*
|
||||||
|
* @see [[akka.stream.javadsl.Source.throttle]]
|
||||||
|
*/
|
||||||
|
def throttle(
|
||||||
|
cost: Int,
|
||||||
|
per: java.time.Duration,
|
||||||
|
costCalculation: function.Function[Out, Integer]): SourceWithContext[Out, Ctx, Mat] =
|
||||||
|
viaScala(_.throttle(cost, per.asScala, costCalculation.apply))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Context-preserving variant of [[akka.stream.javadsl.Source.throttle]].
|
||||||
|
*
|
||||||
|
* @see [[akka.stream.javadsl.Source.throttle]]
|
||||||
|
*/
|
||||||
|
def throttle(
|
||||||
|
cost: Int,
|
||||||
|
per: java.time.Duration,
|
||||||
|
maximumBurst: Int,
|
||||||
|
costCalculation: function.Function[Out, Integer],
|
||||||
|
mode: ThrottleMode): SourceWithContext[Out, Ctx, Mat] =
|
||||||
|
viaScala(_.throttle(cost, per.asScala, maximumBurst, costCalculation.apply, mode))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connect this [[akka.stream.javadsl.SourceWithContext]] to a [[akka.stream.javadsl.Sink]],
|
* Connect this [[akka.stream.javadsl.SourceWithContext]] to a [[akka.stream.javadsl.Sink]],
|
||||||
* concatenating the processing steps of both.
|
* concatenating the processing steps of both.
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,13 @@ package akka.stream.scaladsl
|
||||||
import scala.annotation.unchecked.uncheckedVariance
|
import scala.annotation.unchecked.uncheckedVariance
|
||||||
import scala.collection.immutable
|
import scala.collection.immutable
|
||||||
import scala.concurrent.Future
|
import scala.concurrent.Future
|
||||||
|
import scala.concurrent.duration.FiniteDuration
|
||||||
|
|
||||||
import akka.NotUsed
|
import akka.NotUsed
|
||||||
import akka.dispatch.ExecutionContexts
|
import akka.dispatch.ExecutionContexts
|
||||||
import akka.event.{ LogMarker, LoggingAdapter, MarkerLoggingAdapter }
|
import akka.event.{ LogMarker, LoggingAdapter, MarkerLoggingAdapter }
|
||||||
import akka.stream._
|
import akka.stream._
|
||||||
|
import akka.stream.impl.Throttle
|
||||||
import akka.util.ConstantFun
|
import akka.util.ConstantFun
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -207,5 +209,42 @@ trait FlowWithContextOps[+Out, +Ctx, +Mat] {
|
||||||
via(flow.logWithMarker(name, marker.tupled, extractWithContext)(log))
|
via(flow.logWithMarker(name, marker.tupled, extractWithContext)(log))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Context-preserving variant of [[akka.stream.scaladsl.FlowOps.throttle]].
|
||||||
|
*
|
||||||
|
* @see [[akka.stream.scaladsl.FlowOps.throttle]]
|
||||||
|
*/
|
||||||
|
def throttle(elements: Int, per: FiniteDuration): Repr[Out, Ctx] =
|
||||||
|
throttle(elements, per, Throttle.AutomaticMaximumBurst, ConstantFun.oneInt, ThrottleMode.Shaping)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Context-preserving variant of [[akka.stream.scaladsl.FlowOps.throttle]].
|
||||||
|
*
|
||||||
|
* @see [[akka.stream.scaladsl.FlowOps.throttle]]
|
||||||
|
*/
|
||||||
|
def throttle(elements: Int, per: FiniteDuration, maximumBurst: Int, mode: ThrottleMode): Repr[Out, Ctx] =
|
||||||
|
throttle(elements, per, maximumBurst, ConstantFun.oneInt, mode)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Context-preserving variant of [[akka.stream.scaladsl.FlowOps.throttle]].
|
||||||
|
*
|
||||||
|
* @see [[akka.stream.scaladsl.FlowOps.throttle]]
|
||||||
|
*/
|
||||||
|
def throttle(cost: Int, per: FiniteDuration, costCalculation: (Out) => Int): Repr[Out, Ctx] =
|
||||||
|
throttle(cost, per, Throttle.AutomaticMaximumBurst, costCalculation, ThrottleMode.Shaping)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Context-preserving variant of [[akka.stream.scaladsl.FlowOps.throttle]].
|
||||||
|
*
|
||||||
|
* @see [[akka.stream.scaladsl.FlowOps.throttle]]
|
||||||
|
*/
|
||||||
|
def throttle(
|
||||||
|
cost: Int,
|
||||||
|
per: FiniteDuration,
|
||||||
|
maximumBurst: Int,
|
||||||
|
costCalculation: (Out) => Int,
|
||||||
|
mode: ThrottleMode): Repr[Out, Ctx] =
|
||||||
|
via(flow.throttle(cost, per, maximumBurst, a => costCalculation(a._1), mode))
|
||||||
|
|
||||||
private[akka] def flow[T, C]: Flow[(T, C), (T, C), NotUsed] = Flow[(T, C)]
|
private[akka] def flow[T, C]: Flow[(T, C), (T, C), NotUsed] = Flow[(T, C)]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue