Merge branch 'master' into scalatest310
This commit is contained in:
commit
ec208cad08
34 changed files with 497 additions and 137 deletions
|
|
@ -21,11 +21,21 @@ object DeadLetterSuspensionSpec {
|
||||||
context.system.eventStream.publish(Dropped(n, "Don't like numbers", self))
|
context.system.eventStream.publish(Dropped(n, "Don't like numbers", self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object Unandled {
|
||||||
|
def props(): Props = Props(new Unandled)
|
||||||
|
}
|
||||||
|
|
||||||
|
class Unandled extends Actor {
|
||||||
|
override def receive: Receive = {
|
||||||
|
case n: Int => unhandled(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class DeadLetterSuspensionSpec extends AkkaSpec("""
|
class DeadLetterSuspensionSpec extends AkkaSpec("""
|
||||||
akka.loglevel = INFO
|
akka.loglevel = INFO
|
||||||
akka.log-dead-letters = 3
|
akka.log-dead-letters = 4
|
||||||
akka.log-dead-letters-suspend-duration = 2s
|
akka.log-dead-letters-suspend-duration = 2s
|
||||||
""") with ImplicitSender {
|
""") with ImplicitSender {
|
||||||
import DeadLetterSuspensionSpec._
|
import DeadLetterSuspensionSpec._
|
||||||
|
|
@ -36,6 +46,7 @@ class DeadLetterSuspensionSpec extends AkkaSpec("""
|
||||||
expectTerminated(deadActor)
|
expectTerminated(deadActor)
|
||||||
|
|
||||||
private val droppingActor = system.actorOf(Dropping.props(), "droppingActor")
|
private val droppingActor = system.actorOf(Dropping.props(), "droppingActor")
|
||||||
|
private val unhandledActor = system.actorOf(Unandled.props(), "unhandledActor")
|
||||||
|
|
||||||
private def expectedDeadLettersLogMessage(count: Int): String =
|
private def expectedDeadLettersLogMessage(count: Int): String =
|
||||||
s"Message [java.lang.Integer] from $testActor to $deadActor was not delivered. [$count] dead letters encountered"
|
s"Message [java.lang.Integer] from $testActor to $deadActor was not delivered. [$count] dead letters encountered"
|
||||||
|
|
@ -43,6 +54,9 @@ class DeadLetterSuspensionSpec extends AkkaSpec("""
|
||||||
private def expectedDroppedLogMessage(count: Int): String =
|
private def expectedDroppedLogMessage(count: Int): String =
|
||||||
s"Message [java.lang.Integer] to $droppingActor was dropped. Don't like numbers. [$count] dead letters encountered"
|
s"Message [java.lang.Integer] to $droppingActor was dropped. Don't like numbers. [$count] dead letters encountered"
|
||||||
|
|
||||||
|
private def expectedUnhandledLogMessage(count: Int): String =
|
||||||
|
s"Message [java.lang.Integer] from $testActor to $unhandledActor was unhandled. [$count] dead letters encountered"
|
||||||
|
|
||||||
"must suspend dead-letters logging when reaching 'akka.log-dead-letters', and then re-enable" in {
|
"must suspend dead-letters logging when reaching 'akka.log-dead-letters', and then re-enable" in {
|
||||||
EventFilter.info(start = expectedDeadLettersLogMessage(1), occurrences = 1).intercept {
|
EventFilter.info(start = expectedDeadLettersLogMessage(1), occurrences = 1).intercept {
|
||||||
deadActor ! 1
|
deadActor ! 1
|
||||||
|
|
@ -50,27 +64,30 @@ class DeadLetterSuspensionSpec extends AkkaSpec("""
|
||||||
EventFilter.info(start = expectedDroppedLogMessage(2), occurrences = 1).intercept {
|
EventFilter.info(start = expectedDroppedLogMessage(2), occurrences = 1).intercept {
|
||||||
droppingActor ! 2
|
droppingActor ! 2
|
||||||
}
|
}
|
||||||
EventFilter
|
EventFilter.info(start = expectedUnhandledLogMessage(3), occurrences = 1).intercept {
|
||||||
.info(start = expectedDeadLettersLogMessage(3) + ", no more dead letters will be logged in next", occurrences = 1)
|
unhandledActor ! 3
|
||||||
.intercept {
|
|
||||||
deadActor ! 3
|
|
||||||
}
|
}
|
||||||
|
EventFilter
|
||||||
|
.info(start = expectedDeadLettersLogMessage(4) + ", no more dead letters will be logged in next", occurrences = 1)
|
||||||
|
.intercept {
|
||||||
deadActor ! 4
|
deadActor ! 4
|
||||||
droppingActor ! 5
|
}
|
||||||
|
deadActor ! 5
|
||||||
|
droppingActor ! 6
|
||||||
|
|
||||||
// let suspend-duration elapse
|
// let suspend-duration elapse
|
||||||
Thread.sleep(2050)
|
Thread.sleep(2050)
|
||||||
|
|
||||||
// re-enabled
|
// re-enabled
|
||||||
EventFilter
|
EventFilter
|
||||||
.info(start = expectedDeadLettersLogMessage(6) + ", of which 2 were not logged", occurrences = 1)
|
.info(start = expectedDeadLettersLogMessage(7) + ", of which 2 were not logged", occurrences = 1)
|
||||||
.intercept {
|
.intercept {
|
||||||
deadActor ! 6
|
deadActor ! 7
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset count
|
// reset count
|
||||||
EventFilter.info(start = expectedDeadLettersLogMessage(1), occurrences = 1).intercept {
|
EventFilter.info(start = expectedDeadLettersLogMessage(1), occurrences = 1).intercept {
|
||||||
deadActor ! 7
|
deadActor ! 8
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ object EventStreamSpec {
|
||||||
stdout-loglevel = WARNING
|
stdout-loglevel = WARNING
|
||||||
loglevel = WARNING
|
loglevel = WARNING
|
||||||
actor.debug.unhandled = on
|
actor.debug.unhandled = on
|
||||||
|
log-dead-letters = off
|
||||||
}
|
}
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -150,7 +150,13 @@ class ByteStringSpec extends AnyWordSpec with Matchers with Checkers {
|
||||||
|
|
||||||
def likeVector(bs: ByteString)(body: IndexedSeq[Byte] => Any): Boolean = {
|
def likeVector(bs: ByteString)(body: IndexedSeq[Byte] => Any): Boolean = {
|
||||||
val vec = Vector(bs: _*)
|
val vec = Vector(bs: _*)
|
||||||
body(bs) == body(vec)
|
val a = body(bs)
|
||||||
|
val b = body(vec)
|
||||||
|
val result = a == b
|
||||||
|
if (!result) {
|
||||||
|
println(s"$bs => $a != $vec => $b")
|
||||||
|
}
|
||||||
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
def likeVectors(bsA: ByteString, bsB: ByteString)(body: (IndexedSeq[Byte], IndexedSeq[Byte]) => Any): Boolean = {
|
def likeVectors(bsA: ByteString, bsB: ByteString)(body: (IndexedSeq[Byte], IndexedSeq[Byte]) => Any): Boolean = {
|
||||||
|
|
@ -384,6 +390,27 @@ class ByteStringSpec extends AnyWordSpec with Matchers with Checkers {
|
||||||
ByteString1.fromString("0123456789").take(3).drop(1) should ===(ByteString("12"))
|
ByteString1.fromString("0123456789").take(3).drop(1) should ===(ByteString("12"))
|
||||||
ByteString1.fromString("0123456789").take(10).take(8).drop(3).take(5) should ===(ByteString("34567"))
|
ByteString1.fromString("0123456789").take(10).take(8).drop(3).take(5) should ===(ByteString("34567"))
|
||||||
}
|
}
|
||||||
|
"copyToArray" in {
|
||||||
|
val byteString = ByteString1(Array[Byte](1, 2, 3, 4, 5), startIndex = 1, length = 3)
|
||||||
|
def verify(f: Array[Byte] => Unit)(expected: Byte*): Unit = {
|
||||||
|
val array = Array.fill[Byte](3)(0)
|
||||||
|
f(array)
|
||||||
|
array should ===(expected.toArray)
|
||||||
|
}
|
||||||
|
|
||||||
|
verify(byteString.copyToArray(_, 0, 1))(2, 0, 0)
|
||||||
|
verify(byteString.copyToArray(_, 1, 1))(0, 2, 0)
|
||||||
|
verify(byteString.copyToArray(_, 2, 1))(0, 0, 2)
|
||||||
|
verify(byteString.copyToArray(_, 3, 1))(0, 0, 0)
|
||||||
|
verify(byteString.copyToArray(_, 0, 2))(2, 3, 0)
|
||||||
|
verify(byteString.copyToArray(_, 1, 2))(0, 2, 3)
|
||||||
|
verify(byteString.copyToArray(_, 2, 2))(0, 0, 2)
|
||||||
|
verify(byteString.copyToArray(_, 3, 2))(0, 0, 0)
|
||||||
|
verify(byteString.copyToArray(_, 0, 3))(2, 3, 4)
|
||||||
|
verify(byteString.copyToArray(_, 1, 3))(0, 2, 3)
|
||||||
|
verify(byteString.copyToArray(_, 2, 3))(0, 0, 2)
|
||||||
|
verify(byteString.copyToArray(_, 3, 3))(0, 0, 0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"ByteString1C" must {
|
"ByteString1C" must {
|
||||||
"drop" in {
|
"drop" in {
|
||||||
|
|
@ -427,6 +454,27 @@ class ByteStringSpec extends AnyWordSpec with Matchers with Checkers {
|
||||||
ByteString1.fromString("abcdefg").drop(2) should ===(ByteString("cdefg"))
|
ByteString1.fromString("abcdefg").drop(2) should ===(ByteString("cdefg"))
|
||||||
ByteString1.fromString("abcdefg").drop(2).take(1) should ===(ByteString("c"))
|
ByteString1.fromString("abcdefg").drop(2).take(1) should ===(ByteString("c"))
|
||||||
}
|
}
|
||||||
|
"copyToArray" in {
|
||||||
|
val byteString = ByteString1C(Array[Byte](1, 2, 3))
|
||||||
|
def verify(f: Array[Byte] => Unit)(expected: Byte*): Unit = {
|
||||||
|
val array = Array.fill[Byte](3)(0)
|
||||||
|
f(array)
|
||||||
|
array should ===(expected.toArray)
|
||||||
|
}
|
||||||
|
|
||||||
|
verify(byteString.copyToArray(_, 0, 1))(1, 0, 0)
|
||||||
|
verify(byteString.copyToArray(_, 1, 1))(0, 1, 0)
|
||||||
|
verify(byteString.copyToArray(_, 2, 1))(0, 0, 1)
|
||||||
|
verify(byteString.copyToArray(_, 3, 1))(0, 0, 0)
|
||||||
|
verify(byteString.copyToArray(_, 0, 2))(1, 2, 0)
|
||||||
|
verify(byteString.copyToArray(_, 1, 2))(0, 1, 2)
|
||||||
|
verify(byteString.copyToArray(_, 2, 2))(0, 0, 1)
|
||||||
|
verify(byteString.copyToArray(_, 3, 2))(0, 0, 0)
|
||||||
|
verify(byteString.copyToArray(_, 0, 3))(1, 2, 3)
|
||||||
|
verify(byteString.copyToArray(_, 1, 3))(0, 1, 2)
|
||||||
|
verify(byteString.copyToArray(_, 2, 3))(0, 0, 1)
|
||||||
|
verify(byteString.copyToArray(_, 3, 3))(0, 0, 0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"ByteStrings" must {
|
"ByteStrings" must {
|
||||||
"drop" in {
|
"drop" in {
|
||||||
|
|
@ -656,6 +704,28 @@ class ByteStringSpec extends AnyWordSpec with Matchers with Checkers {
|
||||||
compact.indexOf('g', 5) should ===(5)
|
compact.indexOf('g', 5) should ===(5)
|
||||||
compact.indexOf('g', 6) should ===(-1)
|
compact.indexOf('g', 6) should ===(-1)
|
||||||
}
|
}
|
||||||
|
"copyToArray" in {
|
||||||
|
val byteString = ByteString(1, 2) ++ ByteString(3) ++ ByteString(4)
|
||||||
|
|
||||||
|
def verify(f: Array[Byte] => Unit)(expected: Byte*): Unit = {
|
||||||
|
val array = Array.fill[Byte](3)(0)
|
||||||
|
f(array)
|
||||||
|
array should ===(expected.toArray)
|
||||||
|
}
|
||||||
|
|
||||||
|
verify(byteString.copyToArray(_, 0, 1))(1, 0, 0)
|
||||||
|
verify(byteString.copyToArray(_, 1, 1))(0, 1, 0)
|
||||||
|
verify(byteString.copyToArray(_, 2, 1))(0, 0, 1)
|
||||||
|
verify(byteString.copyToArray(_, 3, 1))(0, 0, 0)
|
||||||
|
verify(byteString.copyToArray(_, 0, 2))(1, 2, 0)
|
||||||
|
verify(byteString.copyToArray(_, 1, 2))(0, 1, 2)
|
||||||
|
verify(byteString.copyToArray(_, 2, 2))(0, 0, 1)
|
||||||
|
verify(byteString.copyToArray(_, 3, 2))(0, 0, 0)
|
||||||
|
verify(byteString.copyToArray(_, 0, 3))(1, 2, 3)
|
||||||
|
verify(byteString.copyToArray(_, 1, 3))(0, 1, 2)
|
||||||
|
verify(byteString.copyToArray(_, 2, 3))(0, 0, 1)
|
||||||
|
verify(byteString.copyToArray(_, 3, 3))(0, 0, 0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"A ByteString" must {
|
"A ByteString" must {
|
||||||
|
|
@ -886,7 +956,7 @@ class ByteStringSpec extends AnyWordSpec with Matchers with Checkers {
|
||||||
case (xs, from, until) =>
|
case (xs, from, until) =>
|
||||||
likeVector(xs)({ it =>
|
likeVector(xs)({ it =>
|
||||||
val array = new Array[Byte](xs.length)
|
val array = new Array[Byte](xs.length)
|
||||||
it.slice(from, until).copyToArray(array, from, until)
|
it.copyToArray(array, from, until)
|
||||||
array.toSeq
|
array.toSeq
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -1125,6 +1195,14 @@ class ByteStringSpec extends AnyWordSpec with Matchers with Checkers {
|
||||||
iterator.copyToArray(array, 4, 2)
|
iterator.copyToArray(array, 4, 2)
|
||||||
assert(new String(array) === "123456")
|
assert(new String(array) === "123456")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"calling copyToArray with length passing end of destination" in {
|
||||||
|
// Pre fix len passing the end of the destination would cause never ending loop inside iterator copyToArray
|
||||||
|
val iterator = (ByteString(1, 2) ++ ByteString(3) ++ ByteString(4)).iterator
|
||||||
|
val array = Array.fill[Byte](3)(0)
|
||||||
|
iterator.copyToArray(array, 2, 2)
|
||||||
|
array.toSeq should ===(Seq(0, 0, 1))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"decode data correctly" when {
|
"decode data correctly" when {
|
||||||
|
|
|
||||||
|
|
@ -106,36 +106,36 @@ public interface IntroTest {
|
||||||
|
|
||||||
// #hello-world-main
|
// #hello-world-main
|
||||||
// #hello-world-main-setup
|
// #hello-world-main-setup
|
||||||
public class HelloWorldMain extends AbstractBehavior<HelloWorldMain.Start> {
|
public class HelloWorldMain extends AbstractBehavior<HelloWorldMain.SayHello> {
|
||||||
// #hello-world-main-setup
|
// #hello-world-main-setup
|
||||||
|
|
||||||
public static class Start {
|
public static class SayHello {
|
||||||
public final String name;
|
public final String name;
|
||||||
|
|
||||||
public Start(String name) {
|
public SayHello(String name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// #hello-world-main-setup
|
// #hello-world-main-setup
|
||||||
public static Behavior<Start> create() {
|
public static Behavior<SayHello> create() {
|
||||||
return Behaviors.setup(HelloWorldMain::new);
|
return Behaviors.setup(HelloWorldMain::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final ActorRef<HelloWorld.Greet> greeter;
|
private final ActorRef<HelloWorld.Greet> greeter;
|
||||||
|
|
||||||
private HelloWorldMain(ActorContext<Start> context) {
|
private HelloWorldMain(ActorContext<SayHello> context) {
|
||||||
super(context);
|
super(context);
|
||||||
greeter = context.spawn(HelloWorld.create(), "greeter");
|
greeter = context.spawn(HelloWorld.create(), "greeter");
|
||||||
}
|
}
|
||||||
// #hello-world-main-setup
|
// #hello-world-main-setup
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Receive<Start> createReceive() {
|
public Receive<SayHello> createReceive() {
|
||||||
return newReceiveBuilder().onMessage(Start.class, this::onStart).build();
|
return newReceiveBuilder().onMessage(SayHello.class, this::onStart).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Behavior<Start> onStart(Start command) {
|
private Behavior<SayHello> onStart(SayHello command) {
|
||||||
ActorRef<HelloWorld.Greeted> replyTo =
|
ActorRef<HelloWorld.Greeted> replyTo =
|
||||||
getContext().spawn(HelloWorldBot.create(3), command.name);
|
getContext().spawn(HelloWorldBot.create(3), command.name);
|
||||||
greeter.tell(new HelloWorld.Greet(command.name, replyTo));
|
greeter.tell(new HelloWorld.Greet(command.name, replyTo));
|
||||||
|
|
@ -148,35 +148,35 @@ public interface IntroTest {
|
||||||
|
|
||||||
interface CustomDispatchersExample {
|
interface CustomDispatchersExample {
|
||||||
|
|
||||||
public static class Start {
|
public static class SayHello {
|
||||||
public final String name;
|
public final String name;
|
||||||
|
|
||||||
public Start(String name) {
|
public SayHello(String name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// #hello-world-main-with-dispatchers
|
// #hello-world-main-with-dispatchers
|
||||||
public class HelloWorldMain extends AbstractBehavior<HelloWorldMain.Start> {
|
public class HelloWorldMain extends AbstractBehavior<HelloWorldMain.SayHello> {
|
||||||
|
|
||||||
// Start message...
|
// Start message...
|
||||||
// #hello-world-main-with-dispatchers
|
// #hello-world-main-with-dispatchers
|
||||||
public static class Start {
|
public static class SayHello {
|
||||||
public final String name;
|
public final String name;
|
||||||
|
|
||||||
public Start(String name) {
|
public SayHello(String name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// #hello-world-main-with-dispatchers
|
// #hello-world-main-with-dispatchers
|
||||||
|
|
||||||
public static Behavior<Start> create() {
|
public static Behavior<SayHello> create() {
|
||||||
return Behaviors.setup(HelloWorldMain::new);
|
return Behaviors.setup(HelloWorldMain::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final ActorRef<HelloWorld.Greet> greeter;
|
private final ActorRef<HelloWorld.Greet> greeter;
|
||||||
|
|
||||||
private HelloWorldMain(ActorContext<Start> context) {
|
private HelloWorldMain(ActorContext<SayHello> context) {
|
||||||
super(context);
|
super(context);
|
||||||
|
|
||||||
final String dispatcherPath = "akka.actor.default-blocking-io-dispatcher";
|
final String dispatcherPath = "akka.actor.default-blocking-io-dispatcher";
|
||||||
|
|
@ -187,7 +187,7 @@ public interface IntroTest {
|
||||||
// createReceive ...
|
// createReceive ...
|
||||||
// #hello-world-main-with-dispatchers
|
// #hello-world-main-with-dispatchers
|
||||||
@Override
|
@Override
|
||||||
public Receive<HelloWorldMain.Start> createReceive() {
|
public Receive<SayHello> createReceive() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// #hello-world-main-with-dispatchers
|
// #hello-world-main-with-dispatchers
|
||||||
|
|
@ -197,11 +197,11 @@ public interface IntroTest {
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
// #hello-world
|
// #hello-world
|
||||||
final ActorSystem<HelloWorldMain.Start> system =
|
final ActorSystem<HelloWorldMain.SayHello> system =
|
||||||
ActorSystem.create(HelloWorldMain.create(), "hello");
|
ActorSystem.create(HelloWorldMain.create(), "hello");
|
||||||
|
|
||||||
system.tell(new HelloWorldMain.Start("World"));
|
system.tell(new HelloWorldMain.SayHello("World"));
|
||||||
system.tell(new HelloWorldMain.Start("Akka"));
|
system.tell(new HelloWorldMain.SayHello("Akka"));
|
||||||
// #hello-world
|
// #hello-world
|
||||||
|
|
||||||
Thread.sleep(3000);
|
Thread.sleep(3000);
|
||||||
|
|
|
||||||
|
|
@ -74,9 +74,9 @@ object IntroSpec {
|
||||||
//#hello-world-main
|
//#hello-world-main
|
||||||
object HelloWorldMain {
|
object HelloWorldMain {
|
||||||
|
|
||||||
final case class Start(name: String)
|
final case class SayHello(name: String)
|
||||||
|
|
||||||
def apply(): Behavior[Start] =
|
def apply(): Behavior[SayHello] =
|
||||||
Behaviors.setup { context =>
|
Behaviors.setup { context =>
|
||||||
val greeter = context.spawn(HelloWorld(), "greeter")
|
val greeter = context.spawn(HelloWorld(), "greeter")
|
||||||
|
|
||||||
|
|
@ -94,10 +94,10 @@ object IntroSpec {
|
||||||
object CustomDispatchersExample {
|
object CustomDispatchersExample {
|
||||||
object HelloWorldMain {
|
object HelloWorldMain {
|
||||||
|
|
||||||
final case class Start(name: String)
|
final case class SayHello(name: String)
|
||||||
|
|
||||||
//#hello-world-main-with-dispatchers
|
//#hello-world-main-with-dispatchers
|
||||||
def apply(): Behavior[Start] =
|
def apply(): Behavior[SayHello] =
|
||||||
Behaviors.setup { context =>
|
Behaviors.setup { context =>
|
||||||
val dispatcherPath = "akka.actor.default-blocking-io-dispatcher"
|
val dispatcherPath = "akka.actor.default-blocking-io-dispatcher"
|
||||||
|
|
||||||
|
|
@ -232,11 +232,11 @@ class IntroSpec extends ScalaTestWithActorTestKit with AnyWordSpecLike with LogC
|
||||||
//#fiddle_code
|
//#fiddle_code
|
||||||
//#hello-world
|
//#hello-world
|
||||||
|
|
||||||
val system: ActorSystem[HelloWorldMain.Start] =
|
val system: ActorSystem[HelloWorldMain.SayHello] =
|
||||||
ActorSystem(HelloWorldMain(), "hello")
|
ActorSystem(HelloWorldMain(), "hello")
|
||||||
|
|
||||||
system ! HelloWorldMain.Start("World")
|
system ! HelloWorldMain.SayHello("World")
|
||||||
system ! HelloWorldMain.Start("Akka")
|
system ! HelloWorldMain.SayHello("Akka")
|
||||||
|
|
||||||
//#hello-world
|
//#hello-world
|
||||||
//#fiddle_code
|
//#fiddle_code
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,8 @@ akka {
|
||||||
# This is useful when you are uncertain of what configuration is used.
|
# This is useful when you are uncertain of what configuration is used.
|
||||||
log-config-on-start = off
|
log-config-on-start = off
|
||||||
|
|
||||||
# Log at info level when messages are sent to dead letters.
|
# Log at info level when messages are sent to dead letters, or published to
|
||||||
|
# eventStream as `DeadLetter`, `Dropped` or `UnhandledMessage`.
|
||||||
# Possible values:
|
# Possible values:
|
||||||
# on: all dead letters are logged
|
# on: all dead letters are logged
|
||||||
# off: no logging of dead letters
|
# off: no logging of dead letters
|
||||||
|
|
|
||||||
|
|
@ -300,7 +300,7 @@ object ByteIterator {
|
||||||
final override def copyToArray[B >: Byte](xs: Array[B], start: Int, len: Int): Unit = {
|
final override def copyToArray[B >: Byte](xs: Array[B], start: Int, len: Int): Unit = {
|
||||||
var pos = start
|
var pos = start
|
||||||
var rest = len
|
var rest = len
|
||||||
while ((rest > 0) && !iterators.isEmpty) {
|
while ((rest > 0) && !iterators.isEmpty && pos < xs.length) {
|
||||||
val n = 0 max ((xs.length - pos) min current.len min rest)
|
val n = 0 max ((xs.length - pos) min current.len min rest)
|
||||||
current.copyToArray(xs, pos, n)
|
current.copyToArray(xs, pos, n)
|
||||||
pos += n
|
pos += n
|
||||||
|
|
|
||||||
|
|
@ -309,7 +309,7 @@ object ByteIterator {
|
||||||
final override def copyToArray[B >: Byte](xs: Array[B], start: Int, len: Int): Int = {
|
final override def copyToArray[B >: Byte](xs: Array[B], start: Int, len: Int): Int = {
|
||||||
var pos = start
|
var pos = start
|
||||||
var rest = len
|
var rest = len
|
||||||
while ((rest > 0) && !iterators.isEmpty) {
|
while ((rest > 0) && !iterators.isEmpty && pos < xs.length) {
|
||||||
val n = 0 max ((xs.length - pos) min current.len min rest)
|
val n = 0 max ((xs.length - pos) min current.len min rest)
|
||||||
current.copyToArray(xs, pos, n)
|
current.copyToArray(xs, pos, n)
|
||||||
pos += n
|
pos += n
|
||||||
|
|
|
||||||
|
|
@ -253,11 +253,11 @@ object ByteString {
|
||||||
}
|
}
|
||||||
|
|
||||||
override def copyToArray[B >: Byte](dest: Array[B], start: Int, len: Int): Int = {
|
override def copyToArray[B >: Byte](dest: Array[B], start: Int, len: Int): Int = {
|
||||||
val copied = math.min(math.min(len, bytes.length), dest.length - start)
|
val toCopy = math.min(math.min(len, bytes.length), dest.length - start)
|
||||||
if (copied > 0) {
|
if (toCopy > 0) {
|
||||||
System.arraycopy(bytes, 0, dest, start, copied)
|
Array.copy(bytes, 0, dest, start, toCopy)
|
||||||
}
|
}
|
||||||
copied
|
toCopy
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -395,11 +395,11 @@ object ByteString {
|
||||||
|
|
||||||
override def copyToArray[B >: Byte](dest: Array[B], start: Int, len: Int): Int = {
|
override def copyToArray[B >: Byte](dest: Array[B], start: Int, len: Int): Int = {
|
||||||
// min of the bytes available to copy, bytes there is room for in dest and the requested number of bytes
|
// min of the bytes available to copy, bytes there is room for in dest and the requested number of bytes
|
||||||
val copied = math.max(math.min(math.min(len, length), dest.length - start), 0)
|
val toCopy = math.min(math.min(len, length), dest.length - start)
|
||||||
if (copied > 0) {
|
if (toCopy > 0) {
|
||||||
System.arraycopy(bytes, 0, dest, start, copied)
|
Array.copy(bytes, startIndex, dest, start, toCopy)
|
||||||
}
|
}
|
||||||
copied
|
toCopy
|
||||||
}
|
}
|
||||||
|
|
||||||
protected def writeReplace(): AnyRef = new SerializationProxy(this)
|
protected def writeReplace(): AnyRef = new SerializationProxy(this)
|
||||||
|
|
@ -645,20 +645,19 @@ object ByteString {
|
||||||
}
|
}
|
||||||
|
|
||||||
override def copyToArray[B >: Byte](dest: Array[B], start: Int, len: Int): Int = {
|
override def copyToArray[B >: Byte](dest: Array[B], start: Int, len: Int): Int = {
|
||||||
if (isCompact) bytestrings.head.copyToArray(dest, start, len)
|
if (bytestrings.size == 1) bytestrings.head.copyToArray(dest, start, len)
|
||||||
else {
|
else {
|
||||||
// min of the bytes available top copy, bytes there is room for in dest and the requested number of bytes
|
// min of the bytes available to copy, bytes there is room for in dest and the requested number of bytes
|
||||||
val copied = math.min(math.min(len, length), dest.length - start)
|
val totalToCopy = math.min(math.min(len, length), dest.length - start)
|
||||||
if (copied > 0) {
|
if (totalToCopy > 0) {
|
||||||
val bsIterator = bytestrings.iterator
|
val bsIterator = bytestrings.iterator
|
||||||
var pos = 0
|
var copied = 0
|
||||||
while (pos < copied) {
|
while (copied < totalToCopy) {
|
||||||
val current = bsIterator.next()
|
val current = bsIterator.next()
|
||||||
val copied = current.copyToArray(dest, pos, len - pos)
|
copied += current.copyToArray(dest, start + copied, totalToCopy - copied)
|
||||||
pos += copied
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
copied
|
totalToCopy
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -289,6 +289,7 @@ final case class UnhandledMessage(
|
||||||
@BeanProperty recipient: ActorRef)
|
@BeanProperty recipient: ActorRef)
|
||||||
extends NoSerializationVerificationNeeded
|
extends NoSerializationVerificationNeeded
|
||||||
with WrappedMessage
|
with WrappedMessage
|
||||||
|
with AllDeadLetters
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Classes for passing status back to the sender.
|
* Classes for passing status back to the sender.
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,11 @@ package akka.actor
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
import akka.annotation.InternalApi
|
import akka.annotation.InternalApi
|
||||||
|
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
import scala.collection.immutable
|
import scala.collection.immutable
|
||||||
import scala.util.control.NonFatal
|
import scala.util.control.NonFatal
|
||||||
|
|
||||||
|
import akka.annotation.DoNotInherit
|
||||||
import akka.dispatch._
|
import akka.dispatch._
|
||||||
import akka.dispatch.sysmsg._
|
import akka.dispatch.sysmsg._
|
||||||
import akka.event.AddressTerminatedTopic
|
import akka.event.AddressTerminatedTopic
|
||||||
|
|
@ -478,8 +479,11 @@ private[akka] trait MinimalActorRef extends InternalActorRef with LocalRef {
|
||||||
/**
|
/**
|
||||||
* Subscribe to this class to be notified about all [[DeadLetter]] (also the suppressed ones)
|
* Subscribe to this class to be notified about all [[DeadLetter]] (also the suppressed ones)
|
||||||
* and [[Dropped]].
|
* and [[Dropped]].
|
||||||
|
*
|
||||||
|
* Not for user extension
|
||||||
*/
|
*/
|
||||||
sealed trait AllDeadLetters extends WrappedMessage {
|
@DoNotInherit
|
||||||
|
trait AllDeadLetters extends WrappedMessage {
|
||||||
def message: Any
|
def message: Any
|
||||||
def sender: ActorRef
|
def sender: ActorRef
|
||||||
def recipient: ActorRef
|
def recipient: ActorRef
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import akka.actor.DeadLetter
|
||||||
import akka.actor.DeadLetterActorRef
|
import akka.actor.DeadLetterActorRef
|
||||||
import akka.actor.DeadLetterSuppression
|
import akka.actor.DeadLetterSuppression
|
||||||
import akka.actor.Dropped
|
import akka.actor.Dropped
|
||||||
|
import akka.actor.UnhandledMessage
|
||||||
import akka.actor.WrappedMessage
|
import akka.actor.WrappedMessage
|
||||||
import akka.event.Logging.Info
|
import akka.event.Logging.Info
|
||||||
import akka.util.PrettyDuration._
|
import akka.util.PrettyDuration._
|
||||||
|
|
@ -29,6 +30,7 @@ class DeadLetterListener extends Actor {
|
||||||
override def preStart(): Unit = {
|
override def preStart(): Unit = {
|
||||||
eventStream.subscribe(self, classOf[DeadLetter])
|
eventStream.subscribe(self, classOf[DeadLetter])
|
||||||
eventStream.subscribe(self, classOf[Dropped])
|
eventStream.subscribe(self, classOf[Dropped])
|
||||||
|
eventStream.subscribe(self, classOf[UnhandledMessage])
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't re-subscribe, skip call to preStart
|
// don't re-subscribe, skip call to preStart
|
||||||
|
|
@ -115,6 +117,10 @@ class DeadLetterListener extends Actor {
|
||||||
val destination = if (isReal(d.recipient)) s" to ${d.recipient}" else ""
|
val destination = if (isReal(d.recipient)) s" to ${d.recipient}" else ""
|
||||||
s"Message [$messageStr]$wrappedIn$origin$destination was dropped. ${dropped.reason}. " +
|
s"Message [$messageStr]$wrappedIn$origin$destination was dropped. ${dropped.reason}. " +
|
||||||
s"[$count] dead letters encountered$doneMsg. "
|
s"[$count] dead letters encountered$doneMsg. "
|
||||||
|
case _: UnhandledMessage =>
|
||||||
|
val destination = if (isReal(d.recipient)) s" to ${d.recipient}" else ""
|
||||||
|
s"Message [$messageStr]$wrappedIn$origin$destination was unhandled. " +
|
||||||
|
s"[$count] dead letters encountered$doneMsg. "
|
||||||
case _ =>
|
case _ =>
|
||||||
s"Message [$messageStr]$wrappedIn$origin to ${d.recipient} was not delivered. " +
|
s"Message [$messageStr]$wrappedIn$origin to ${d.recipient} was not delivered. " +
|
||||||
s"[$count] dead letters encountered$doneMsg. " +
|
s"[$count] dead letters encountered$doneMsg. " +
|
||||||
|
|
|
||||||
|
|
@ -1637,6 +1637,8 @@ class LogMarker(val name: String, val properties: Map[String, Any]) {
|
||||||
import akka.util.ccompat.JavaConverters._
|
import akka.util.ccompat.JavaConverters._
|
||||||
properties.map { case (k, v) => (k, v.asInstanceOf[AnyRef]) }.asJava
|
properties.map { case (k, v) => (k, v.asInstanceOf[AnyRef]) }.asJava
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override def toString: String = s"LogMarker($name,$properties)"
|
||||||
}
|
}
|
||||||
|
|
||||||
object LogMarker {
|
object LogMarker {
|
||||||
|
|
|
||||||
|
|
@ -116,8 +116,8 @@ object CircuitBreaker {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides circuit breaker functionality to provide stability when working with "dangerous" operations, e.g. calls to
|
* Provides circuit breaker functionality for stability when working with "dangerous" operations, e.g. calls to
|
||||||
* remote systems
|
* remote systems.
|
||||||
*
|
*
|
||||||
* Transitions through three states:
|
* Transitions through three states:
|
||||||
* - In *Closed* state, calls pass through until the `maxFailures` count is reached. This causes the circuit breaker
|
* - In *Closed* state, calls pass through until the `maxFailures` count is reached. This causes the circuit breaker
|
||||||
|
|
@ -244,7 +244,7 @@ class CircuitBreaker(
|
||||||
Unsafe.instance.getObjectVolatile(this, AbstractCircuitBreaker.resetTimeoutOffset).asInstanceOf[FiniteDuration]
|
Unsafe.instance.getObjectVolatile(this, AbstractCircuitBreaker.resetTimeoutOffset).asInstanceOf[FiniteDuration]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wraps invocations of asynchronous calls that need to be protected
|
* Wraps invocations of asynchronous calls that need to be protected.
|
||||||
*
|
*
|
||||||
* @param body Call needing protected
|
* @param body Call needing protected
|
||||||
* @param defineFailureFn function that define what should be consider failure and thus increase failure count
|
* @param defineFailureFn function that define what should be consider failure and thus increase failure count
|
||||||
|
|
@ -255,7 +255,7 @@ class CircuitBreaker(
|
||||||
currentState.invoke(body, defineFailureFn)
|
currentState.invoke(body, defineFailureFn)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wraps invocations of asynchronous calls that need to be protected
|
* Wraps invocations of asynchronous calls that need to be protected.
|
||||||
*
|
*
|
||||||
* @param body Call needing protected
|
* @param body Call needing protected
|
||||||
* @return [[scala.concurrent.Future]] containing the call result or a
|
* @return [[scala.concurrent.Future]] containing the call result or a
|
||||||
|
|
@ -266,7 +266,7 @@ class CircuitBreaker(
|
||||||
currentState.invoke(body, CircuitBreaker.exceptionAsFailure)
|
currentState.invoke(body, CircuitBreaker.exceptionAsFailure)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Java API for [[#withCircuitBreaker]]
|
* Java API for [[#withCircuitBreaker]].
|
||||||
*
|
*
|
||||||
* @param body Call needing protected
|
* @param body Call needing protected
|
||||||
* @return [[scala.concurrent.Future]] containing the call result or a
|
* @return [[scala.concurrent.Future]] containing the call result or a
|
||||||
|
|
@ -276,7 +276,7 @@ class CircuitBreaker(
|
||||||
callWithCircuitBreaker(body, CircuitBreaker.exceptionAsFailureJava[T])
|
callWithCircuitBreaker(body, CircuitBreaker.exceptionAsFailureJava[T])
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Java API for [[#withCircuitBreaker]]
|
* Java API for [[#withCircuitBreaker]].
|
||||||
*
|
*
|
||||||
* @param body Call needing protected
|
* @param body Call needing protected
|
||||||
* @param defineFailureFn function that define what should be consider failure and thus increase failure count
|
* @param defineFailureFn function that define what should be consider failure and thus increase failure count
|
||||||
|
|
@ -292,7 +292,7 @@ class CircuitBreaker(
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Java API (8) for [[#withCircuitBreaker]]
|
* Java API (8) for [[#withCircuitBreaker]].
|
||||||
*
|
*
|
||||||
* @param body Call needing protected
|
* @param body Call needing protected
|
||||||
* @return [[java.util.concurrent.CompletionStage]] containing the call result or a
|
* @return [[java.util.concurrent.CompletionStage]] containing the call result or a
|
||||||
|
|
@ -302,7 +302,7 @@ class CircuitBreaker(
|
||||||
callWithCircuitBreakerCS(body, CircuitBreaker.exceptionAsFailureJava)
|
callWithCircuitBreakerCS(body, CircuitBreaker.exceptionAsFailureJava)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Java API (8) for [[#withCircuitBreaker]]
|
* Java API (8) for [[#withCircuitBreaker]].
|
||||||
*
|
*
|
||||||
* @param body Call needing protected
|
* @param body Call needing protected
|
||||||
* @param defineFailureFn function that define what should be consider failure and thus increase failure count
|
* @param defineFailureFn function that define what should be consider failure and thus increase failure count
|
||||||
|
|
@ -317,10 +317,10 @@ class CircuitBreaker(
|
||||||
}, defineFailureFn))
|
}, defineFailureFn))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wraps invocations of synchronous calls that need to be protected
|
* Wraps invocations of synchronous calls that need to be protected.
|
||||||
*
|
*
|
||||||
* Calls are run in caller's thread. Because of the synchronous nature of
|
* Calls are run in the caller's thread. Because of the synchronous nature of
|
||||||
* this call the `scala.concurrent.TimeoutException` will only be thrown
|
* this call, the `scala.concurrent.TimeoutException` will only be thrown
|
||||||
* after the body has completed.
|
* after the body has completed.
|
||||||
*
|
*
|
||||||
* Throws java.util.concurrent.TimeoutException if the call timed out.
|
* Throws java.util.concurrent.TimeoutException if the call timed out.
|
||||||
|
|
@ -332,7 +332,7 @@ class CircuitBreaker(
|
||||||
withSyncCircuitBreaker(body, CircuitBreaker.exceptionAsFailure)
|
withSyncCircuitBreaker(body, CircuitBreaker.exceptionAsFailure)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wraps invocations of synchronous calls that need to be protected
|
* Wraps invocations of synchronous calls that need to be protected.
|
||||||
*
|
*
|
||||||
* Calls are run in caller's thread. Because of the synchronous nature of
|
* Calls are run in caller's thread. Because of the synchronous nature of
|
||||||
* this call the `scala.concurrent.TimeoutException` will only be thrown
|
* this call the `scala.concurrent.TimeoutException` will only be thrown
|
||||||
|
|
@ -510,7 +510,7 @@ class CircuitBreaker(
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a callback to execute when call finished with failure.
|
* Adds a callback to execute if the call finished with failure.
|
||||||
*
|
*
|
||||||
* The callback is run in the [[scala.concurrent.ExecutionContext]] supplied in the constructor.
|
* The callback is run in the [[scala.concurrent.ExecutionContext]] supplied in the constructor.
|
||||||
*
|
*
|
||||||
|
|
@ -523,7 +523,7 @@ class CircuitBreaker(
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JavaAPI for onCallFailure
|
* JavaAPI for onCallFailure.
|
||||||
*
|
*
|
||||||
* @param callback Handler to be invoked on failed call, where passed value is elapsed time in nanoseconds.
|
* @param callback Handler to be invoked on failed call, where passed value is elapsed time in nanoseconds.
|
||||||
* @return CircuitBreaker for fluent usage
|
* @return CircuitBreaker for fluent usage
|
||||||
|
|
@ -534,7 +534,7 @@ class CircuitBreaker(
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a callback to execute when call finished with timeout.
|
* Adds a callback to execute if a call finished with timeout.
|
||||||
*
|
*
|
||||||
* The callback is run in the [[scala.concurrent.ExecutionContext]] supplied in the constructor.
|
* The callback is run in the [[scala.concurrent.ExecutionContext]] supplied in the constructor.
|
||||||
*
|
*
|
||||||
|
|
@ -547,7 +547,7 @@ class CircuitBreaker(
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* JavaAPI for onCallTimeout
|
* JavaAPI for onCallTimeout.
|
||||||
*
|
*
|
||||||
* @param callback Handler to be invoked on call finished with timeout, where passed value is elapsed time in nanoseconds.
|
* @param callback Handler to be invoked on call finished with timeout, where passed value is elapsed time in nanoseconds.
|
||||||
* @return CircuitBreaker for fluent usage
|
* @return CircuitBreaker for fluent usage
|
||||||
|
|
@ -558,7 +558,7 @@ class CircuitBreaker(
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a callback to execute when call was failed due to open breaker.
|
* Adds a callback to execute if call was failed due to open breaker.
|
||||||
*
|
*
|
||||||
* The callback is run in the [[scala.concurrent.ExecutionContext]] supplied in the constructor.
|
* The callback is run in the [[scala.concurrent.ExecutionContext]] supplied in the constructor.
|
||||||
*
|
*
|
||||||
|
|
@ -607,7 +607,6 @@ class CircuitBreaker(
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resets breaker to a closed state. This is valid from an Half-Open state only.
|
* Resets breaker to a closed state. This is valid from an Half-Open state only.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
private def resetBreaker(): Unit = transition(HalfOpen, Closed)
|
private def resetBreaker(): Unit = transition(HalfOpen, Closed)
|
||||||
|
|
||||||
|
|
@ -672,7 +671,6 @@ class CircuitBreaker(
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempts to reset breaker by transitioning to a half-open state. This is valid from an Open state only.
|
* Attempts to reset breaker by transitioning to a half-open state. This is valid from an Open state only.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
private def attemptReset(): Unit = transition(Open, HalfOpen)
|
private def attemptReset(): Unit = transition(Open, HalfOpen)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ Java
|
||||||
|
|
||||||
@@@ note
|
@@@ note
|
||||||
|
|
||||||
Using the `CircuitBreaker` companion object's @scala[*apply*]@java[*create*] method
|
Using the `CircuitBreaker`'s companion object @scala[*apply*]@java[*create*] method
|
||||||
will return a `CircuitBreaker` where callbacks are executed in the caller's thread.
|
will return a `CircuitBreaker` where callbacks are executed in the caller's thread.
|
||||||
This can be useful if the asynchronous `Future` behavior is unnecessary, for
|
This can be useful if the asynchronous `Future` behavior is unnecessary, for
|
||||||
example invoking a synchronous-only API.
|
example invoking a synchronous-only API.
|
||||||
|
|
@ -101,11 +101,11 @@ example invoking a synchronous-only API.
|
||||||
|
|
||||||
### Control failure count explicitly
|
### Control failure count explicitly
|
||||||
|
|
||||||
By default, the circuit breaker treat `Exception` as failure in synchronized API, or failed `Future` as failure in future based API.
|
By default, the circuit breaker treats `Exception` as failure in synchronized API, or failed `Future` as failure in future based API.
|
||||||
Failure will increment failure count, when failure count reach the *maxFailures*, circuit breaker will be opened.
|
On failure, the failure count will increment. If the failure count reaches the *maxFailures*, the circuit breaker will be opened.
|
||||||
However, some applications might requires certain exception to not increase failure count, or vice versa,
|
However, some applications may require certain exceptions to not increase the failure count.
|
||||||
sometime we want to increase the failure count even if the call succeeded.
|
In other cases one may want to increase the failure count even if the call succeeded.
|
||||||
Akka circuit breaker provides a way to achieve such use case:
|
Akka circuit breaker provides a way to achieve such use cases:
|
||||||
|
|
||||||
* `withCircuitBreaker`
|
* `withCircuitBreaker`
|
||||||
* `withSyncCircuitBreaker`
|
* `withSyncCircuitBreaker`
|
||||||
|
|
@ -113,7 +113,7 @@ Akka circuit breaker provides a way to achieve such use case:
|
||||||
* `callWithCircuitBreakerCS`
|
* `callWithCircuitBreakerCS`
|
||||||
* `callWithSyncCircuitBreaker`
|
* `callWithSyncCircuitBreaker`
|
||||||
|
|
||||||
All methods above accepts an argument `defineFailureFn`
|
All methods above accept an argument `defineFailureFn`
|
||||||
|
|
||||||
Type of `defineFailureFn`: @scala[`Try[T] => Boolean`]@java[`BiFunction[Optional[T], Optional[Throwable], java.lang.Boolean]`]
|
Type of `defineFailureFn`: @scala[`Try[T] => Boolean`]@java[`BiFunction[Optional[T], Optional[Throwable], java.lang.Boolean]`]
|
||||||
|
|
||||||
|
|
@ -128,9 +128,14 @@ Java
|
||||||
|
|
||||||
### Low level API
|
### Low level API
|
||||||
|
|
||||||
The low-level API allows you to describe the behavior of the CircuitBreaker in detail, including deciding what to return to the calling `Actor` in case of success or failure. This is especially useful when expecting the remote call to send a reply. CircuitBreaker doesn't support `Tell Protection` (protecting against calls that expect a reply) natively at the moment, so you need to use the low-level power-user APIs, `succeed` and `fail` methods, as well as `isClose`, `isOpen`, `isHalfOpen` to implement it.
|
The low-level API allows you to describe the behavior of the CircuitBreaker in detail, including deciding what to return to the calling `Actor` in case of success or failure. This is especially useful when expecting the remote call to send a reply.
|
||||||
|
CircuitBreaker doesn't support `Tell Protection` (protecting against calls that expect a reply) natively at the moment.
|
||||||
|
Thus you need to use the low-level power-user APIs, `succeed` and `fail` methods, as well as `isClose`, `isOpen`, `isHalfOpen` to implement it.
|
||||||
|
|
||||||
As can be seen in the examples below, a `Tell Protection` pattern could be implemented by using the `succeed` and `fail` methods, which would count towards the `CircuitBreaker` counts. In the example, a call is made to the remote service if the `breaker.isClosed`, and once a response is received, the `succeed` method is invoked, which tells the `CircuitBreaker` to keep the breaker closed. If on the other hand an error or timeout is received, we trigger a `fail` and the breaker accrues this failure towards its count for opening the breaker.
|
As can be seen in the examples below, a `Tell Protection` pattern could be implemented by using the `succeed` and `fail` methods, which would count towards the `CircuitBreaker` counts.
|
||||||
|
In the example, a call is made to the remote service if the `breaker.isClosed`.
|
||||||
|
Once a response is received, the `succeed` method is invoked, which tells the `CircuitBreaker` to keep the breaker closed.
|
||||||
|
On the other hand, if an error or timeout is received we trigger a `fail`, and the breaker accrues this failure towards its count for opening the breaker.
|
||||||
|
|
||||||
@@@ note
|
@@@ note
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ Another example that uses the "thread-pool-executor":
|
||||||
|
|
||||||
@@@ note
|
@@@ note
|
||||||
|
|
||||||
The thread pool executor dispatcher is implemented using by a `java.util.concurrent.ThreadPoolExecutor`.
|
The thread pool executor dispatcher is implemented using a `java.util.concurrent.ThreadPoolExecutor`.
|
||||||
You can read more about it in the JDK's [ThreadPoolExecutor documentation](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ThreadPoolExecutor.html).
|
You can read more about it in the JDK's [ThreadPoolExecutor documentation](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ThreadPoolExecutor.html).
|
||||||
|
|
||||||
@@@
|
@@@
|
||||||
|
|
|
||||||
|
|
@ -50,18 +50,18 @@ class MyActor extends Actor with akka.actor.ActorLogging {
|
||||||
@@@
|
@@@
|
||||||
|
|
||||||
The first parameter to @scala[`Logging`] @java[`Logging.getLogger`] could also be any
|
The first parameter to @scala[`Logging`] @java[`Logging.getLogger`] could also be any
|
||||||
`LoggingBus`, specifically @scala[`system.eventStream`] @scala[`system.eventStream()`]; in the demonstrated
|
`LoggingBus`, specifically @scala[`system.eventStream`] @scala[`system.eventStream()`].
|
||||||
case, the actor system's address is included in the `akkaSource`
|
In the demonstrated case, the actor system's address is included in the `akkaSource`
|
||||||
representation of the log source (see @ref:[Logging Thread, Akka Source and Actor System in MDC](#logging-thread-akka-source-and-actor-system-in-mdc))
|
representation of the log source (see @ref:[Logging Thread, Akka Source and Actor System in MDC](#logging-thread-akka-source-and-actor-system-in-mdc)),
|
||||||
while in the second case this is not automatically done.
|
while in the second case this is not automatically done.
|
||||||
The second parameter to @scala[`Logging`] @java[`Logging.getLogger`] is the source of this logging channel.
|
The second parameter to @scala[`Logging`] @java[`Logging.getLogger`] is the source of this logging channel.
|
||||||
The source object is translated to a String according to the following rules:
|
The source object is translated to a String according to the following rules:
|
||||||
|
|
||||||
* if it is an Actor or ActorRef, its path is used
|
* if it is an Actor or ActorRef, its path is used
|
||||||
* in case of a String it is used as is
|
* in case of a String it is used as is
|
||||||
* in case of a class an approximation of its simpleName
|
* in case of a Class an approximation of its `simpleName` is used
|
||||||
* and in all other cases @scala[a compile error occurs unless an implicit
|
* in all other cases @scala[a compile error occurs unless an implicit
|
||||||
`LogSource[T]` is in scope for the type in question] @java[the simpleName of its class]
|
`LogSource[T]` is in scope for the type in question] @java[the `simpleName` of its class] is used
|
||||||
|
|
||||||
The log message may contain argument placeholders `{}`, which will be
|
The log message may contain argument placeholders `{}`, which will be
|
||||||
substituted if the log level is enabled. Giving more arguments than
|
substituted if the log level is enabled. Giving more arguments than
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,9 @@ project.description: Migrating to Akka 2.6.
|
||||||
---
|
---
|
||||||
# Migration Guide 2.5.x to 2.6.x
|
# Migration Guide 2.5.x to 2.6.x
|
||||||
|
|
||||||
|
An overview of the changes in Akka 2.6 is presented in the [What's new in Akka 2.6 video](https://akka.io/blog/news/2019/12/12/akka-26-intro)
|
||||||
|
and the [release announcement](https://akka.io/blog/news/2019/11/06/akka-2.6.0-released).
|
||||||
|
|
||||||
Akka 2.6.x is binary backwards compatible with 2.5.x with the ordinary exceptions listed in the
|
Akka 2.6.x is binary backwards compatible with 2.5.x with the ordinary exceptions listed in the
|
||||||
@ref:[Binary Compatibility Rules](../common/circuitbreaker.md).
|
@ref:[Binary Compatibility Rules](../common/circuitbreaker.md).
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,19 @@ End the current substream whenever a predicate returns `true`, starting a new su
|
||||||
|
|
||||||
End the current substream whenever a predicate returns `true`, starting a new substream for the next element.
|
End the current substream whenever a predicate returns `true`, starting a new substream for the next element.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
Given some time series data source we would like to split the stream into sub-streams for each second.
|
||||||
|
By using `sliding` we can compare the timestamp of the current and next element to decide when to split.
|
||||||
|
|
||||||
|
Scala
|
||||||
|
: @@snip [Scan.scala](/akka-docs/src/test/scala/docs/stream/operators/sourceorflow/Split.scala) { #splitAfter }
|
||||||
|
|
||||||
|
Java
|
||||||
|
: @@snip [SourceOrFlow.java](/akka-docs/src/test/java/jdocs/stream/operators/sourceorflow/Split.java) { #splitAfter }
|
||||||
|
|
||||||
|
An alternative way of implementing this is shown in @ref:[splitWhen example](splitWhen.md#example).
|
||||||
|
|
||||||
## Reactive Streams semantics
|
## Reactive Streams semantics
|
||||||
|
|
||||||
@@@div { .callout }
|
@@@div { .callout }
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,20 @@ Split off elements into a new substream whenever a predicate function return `tr
|
||||||
|
|
||||||
Split off elements into a new substream whenever a predicate function return `true`.
|
Split off elements into a new substream whenever a predicate function return `true`.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
Given some time series data source we would like to split the stream into sub-streams for each second.
|
||||||
|
We need to compare the timestamp of the previous and current element to decide when to split. This
|
||||||
|
decision can be implemented in a `statefulMapConcat` operator preceding the `splitWhen`.
|
||||||
|
|
||||||
|
Scala
|
||||||
|
: @@snip [Scan.scala](/akka-docs/src/test/scala/docs/stream/operators/sourceorflow/Split.scala) { #splitWhen }
|
||||||
|
|
||||||
|
Java
|
||||||
|
: @@snip [SourceOrFlow.java](/akka-docs/src/test/java/jdocs/stream/operators/sourceorflow/Split.java) { #splitWhen }
|
||||||
|
|
||||||
|
An alternative way of implementing this is shown in @ref:[splitAfter example](splitAfter.md#example).
|
||||||
|
|
||||||
## Reactive Streams semantics
|
## Reactive Streams semantics
|
||||||
|
|
||||||
@@@div { .callout }
|
@@@div { .callout }
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,8 @@ systems. The API of Akka’s Actors has borrowed some of its syntax from Erlang.
|
||||||
## First example
|
## First example
|
||||||
|
|
||||||
If you are new to Akka you might want to start with reading the @ref:[Getting Started Guide](guide/introduction.md)
|
If you are new to Akka you might want to start with reading the @ref:[Getting Started Guide](guide/introduction.md)
|
||||||
and then come back here to learn more.
|
and then come back here to learn more. We also recommend watching the short
|
||||||
|
[introduction video to Akka actors](https://akka.io/blog/news/2019/12/03/akka-typed-actor-intro-video).
|
||||||
|
|
||||||
It is helpful to become familiar with the foundational, external and internal
|
It is helpful to become familiar with the foundational, external and internal
|
||||||
ecosystem of your Actors, to see what you can leverage and customize as needed, see
|
ecosystem of your Actors, to see what you can leverage and customize as needed, see
|
||||||
|
|
@ -132,7 +133,7 @@ Scala
|
||||||
Java
|
Java
|
||||||
: @@snip [IntroSpec.scala](/akka-actor-typed-tests/src/test/java/jdocs/akka/typed/IntroTest.java) { #hello-world }
|
: @@snip [IntroSpec.scala](/akka-actor-typed-tests/src/test/java/jdocs/akka/typed/IntroTest.java) { #hello-world }
|
||||||
|
|
||||||
We start an Actor system from the defined `HelloWorldMain` behavior and send two `Start` messages that
|
We start an Actor system from the defined `HelloWorldMain` behavior and send two `SayHello` messages that
|
||||||
will kick-off the interaction between two separate `HelloWorldBot` actors and the single `Greeter` actor.
|
will kick-off the interaction between two separate `HelloWorldBot` actors and the single `Greeter` actor.
|
||||||
|
|
||||||
An application normally consists of a single `ActorSystem`, running many actors, per JVM.
|
An application normally consists of a single `ActorSystem`, running many actors, per JVM.
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,9 @@ It could for example be actors representing Aggregate Roots in Domain-Driven Des
|
||||||
Here we call these actors "entities". These actors typically have persistent (durable) state,
|
Here we call these actors "entities". These actors typically have persistent (durable) state,
|
||||||
but this feature is not limited to actors with persistent state.
|
but this feature is not limited to actors with persistent state.
|
||||||
|
|
||||||
|
The [Introduction to Akka Cluster Sharding video](https://akka.io/blog/news/2019/12/16/akka-cluster-sharding-intro-video)
|
||||||
|
is a good starting point for learning Cluster Sharding.
|
||||||
|
|
||||||
Cluster sharding is typically used when you have many stateful actors that together consume
|
Cluster sharding is typically used when you have many stateful actors that together consume
|
||||||
more resources (e.g. memory) than fit on one machine. If you only have a few stateful actors
|
more resources (e.g. memory) than fit on one machine. If you only have a few stateful actors
|
||||||
it might be easier to run them on a @ref:[Cluster Singleton](cluster-singleton.md) node.
|
it might be easier to run them on a @ref:[Cluster Singleton](cluster-singleton.md) node.
|
||||||
|
|
|
||||||
|
|
@ -209,8 +209,8 @@ The thread information was recorded using the YourKit profiler, however any good
|
||||||
has this feature (including the free and bundled with the Oracle JDK [VisualVM](https://visualvm.github.io/), as well as [Java Mission Control](https://openjdk.java.net/projects/jmc/)).
|
has this feature (including the free and bundled with the Oracle JDK [VisualVM](https://visualvm.github.io/), as well as [Java Mission Control](https://openjdk.java.net/projects/jmc/)).
|
||||||
|
|
||||||
The orange portion of the thread shows that it is idle. Idle threads are fine -
|
The orange portion of the thread shows that it is idle. Idle threads are fine -
|
||||||
they're ready to accept new work. However, large amount of turquoise (blocked, or sleeping as in our example) threads
|
they're ready to accept new work. However, a large number of turquoise (blocked, or sleeping as in our example) threads
|
||||||
is very bad and leads to thread starvation.
|
leads to thread starvation.
|
||||||
|
|
||||||
@@@ note
|
@@@ note
|
||||||
|
|
||||||
|
|
@ -267,7 +267,7 @@ unless you @ref:[set up a separate dispatcher for the actor](../dispatchers.md#s
|
||||||
|
|
||||||
### Solution: Dedicated dispatcher for blocking operations
|
### Solution: Dedicated dispatcher for blocking operations
|
||||||
|
|
||||||
One of the most efficient methods of isolating the blocking behavior such that it does not impact the rest of the system
|
One of the most efficient methods of isolating the blocking behavior, such that it does not impact the rest of the system,
|
||||||
is to prepare and use a dedicated dispatcher for all those blocking operations.
|
is to prepare and use a dedicated dispatcher for all those blocking operations.
|
||||||
This technique is often referred to as "bulk-heading" or simply "isolating blocking".
|
This technique is often referred to as "bulk-heading" or simply "isolating blocking".
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,9 @@ allows for very high transaction rates and efficient replication. A stateful act
|
||||||
events to the actor, allowing it to rebuild its state. This can be either the full history of changes
|
events to the actor, allowing it to rebuild its state. This can be either the full history of changes
|
||||||
or starting from a checkpoint in a snapshot which can dramatically reduce recovery times.
|
or starting from a checkpoint in a snapshot which can dramatically reduce recovery times.
|
||||||
|
|
||||||
|
The [Event Sourcing with Akka 2.6 video](https://akka.io/blog/news/2020/01/07/akka-event-sourcing-video)
|
||||||
|
is a good starting point for learning Event Sourcing.
|
||||||
|
|
||||||
@@@ note
|
@@@ note
|
||||||
|
|
||||||
The General Data Protection Regulation (GDPR) requires that personal information must be deleted at the request of users.
|
The General Data Protection Regulation (GDPR) requires that personal information must be deleted at the request of users.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,108 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Lightbend Inc. <https://www.lightbend.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
package jdocs.stream.operators.sourceorflow;
|
||||||
|
|
||||||
|
import akka.actor.ActorSystem;
|
||||||
|
import akka.japi.Pair;
|
||||||
|
import akka.japi.function.Creator;
|
||||||
|
import akka.japi.function.Function;
|
||||||
|
import akka.stream.javadsl.Sink;
|
||||||
|
import akka.stream.javadsl.Source;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
public class Split {
|
||||||
|
public static void splitWhenExample(String[] args) {
|
||||||
|
ActorSystem system = ActorSystem.create();
|
||||||
|
|
||||||
|
// #splitWhen
|
||||||
|
Source.range(1, 100)
|
||||||
|
.throttle(1, Duration.ofMillis(100))
|
||||||
|
.map(elem -> new Pair<>(elem, Instant.now()))
|
||||||
|
.statefulMapConcat(
|
||||||
|
() -> {
|
||||||
|
return new Function<Pair<Integer, Instant>, Iterable<Pair<Integer, Boolean>>>() {
|
||||||
|
// stateful decision in statefulMapConcat
|
||||||
|
// keep track of time bucket (one per second)
|
||||||
|
LocalDateTime currentTimeBucket =
|
||||||
|
LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneOffset.UTC);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterable<Pair<Integer, Boolean>> apply(
|
||||||
|
Pair<Integer, Instant> elemTimestamp) {
|
||||||
|
LocalDateTime time =
|
||||||
|
LocalDateTime.ofInstant(elemTimestamp.second(), ZoneOffset.UTC);
|
||||||
|
LocalDateTime bucket = time.withNano(0);
|
||||||
|
boolean newBucket = !bucket.equals(currentTimeBucket);
|
||||||
|
if (newBucket) currentTimeBucket = bucket;
|
||||||
|
return Collections.singleton(new Pair<>(elemTimestamp.first(), newBucket));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.splitWhen(elemDecision -> elemDecision.second()) // split when time bucket changes
|
||||||
|
.map(elemDecision -> elemDecision.first())
|
||||||
|
.fold(0, (acc, notUsed) -> acc + 1) // sum
|
||||||
|
.to(Sink.foreach(System.out::println))
|
||||||
|
.run(system);
|
||||||
|
// 3
|
||||||
|
// 10
|
||||||
|
// 10
|
||||||
|
// 10
|
||||||
|
// 10
|
||||||
|
// 10
|
||||||
|
// 10
|
||||||
|
// 10
|
||||||
|
// 10
|
||||||
|
// 10
|
||||||
|
// 7
|
||||||
|
// #splitWhen
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void splitAfterExample(String[] args) {
|
||||||
|
ActorSystem system = ActorSystem.create();
|
||||||
|
|
||||||
|
// #splitAfter
|
||||||
|
Source.range(1, 100)
|
||||||
|
.throttle(1, Duration.ofMillis(100))
|
||||||
|
.map(elem -> new Pair<>(elem, Instant.now()))
|
||||||
|
.sliding(2, 1)
|
||||||
|
.splitAfter(
|
||||||
|
slidingElements -> {
|
||||||
|
if (slidingElements.size() == 2) {
|
||||||
|
Pair<Integer, Instant> current = slidingElements.get(0);
|
||||||
|
Pair<Integer, Instant> next = slidingElements.get(1);
|
||||||
|
LocalDateTime currentBucket =
|
||||||
|
LocalDateTime.ofInstant(current.second(), ZoneOffset.UTC).withNano(0);
|
||||||
|
LocalDateTime nextBucket =
|
||||||
|
LocalDateTime.ofInstant(next.second(), ZoneOffset.UTC).withNano(0);
|
||||||
|
return !currentBucket.equals(nextBucket);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map(slidingElements -> slidingElements.get(0).first())
|
||||||
|
.fold(0, (acc, notUsed) -> acc + 1) // sum
|
||||||
|
.to(Sink.foreach(System.out::println))
|
||||||
|
.run(system);
|
||||||
|
// 3
|
||||||
|
// 10
|
||||||
|
// 10
|
||||||
|
// 10
|
||||||
|
// 10
|
||||||
|
// 10
|
||||||
|
// 10
|
||||||
|
// 10
|
||||||
|
// 10
|
||||||
|
// 10
|
||||||
|
// 6
|
||||||
|
// note that the very last element is never included due to sliding,
|
||||||
|
// but that would not be problem for an infinite stream
|
||||||
|
// #splitAfter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2019-2020 Lightbend Inc. <https://www.lightbend.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
package docs.stream.operators.sourceorflow
|
||||||
|
|
||||||
|
import java.time.Instant
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
import java.time.ZoneOffset
|
||||||
|
|
||||||
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
|
import akka.stream.scaladsl.Sink
|
||||||
|
import akka.stream.scaladsl.Source
|
||||||
|
|
||||||
|
object Split {
|
||||||
|
def splitWhenExample(args: Array[String]): Unit = {
|
||||||
|
import akka.actor.ActorSystem
|
||||||
|
|
||||||
|
implicit val system: ActorSystem = ActorSystem()
|
||||||
|
|
||||||
|
//#splitWhen
|
||||||
|
Source(1 to 100)
|
||||||
|
.throttle(1, 100.millis)
|
||||||
|
.map(elem => (elem, Instant.now()))
|
||||||
|
.statefulMapConcat(() => {
|
||||||
|
// stateful decision in statefulMapConcat
|
||||||
|
// keep track of time bucket (one per second)
|
||||||
|
var currentTimeBucket = LocalDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneOffset.UTC)
|
||||||
|
|
||||||
|
{
|
||||||
|
case (elem, timestamp) =>
|
||||||
|
val time = LocalDateTime.ofInstant(timestamp, ZoneOffset.UTC)
|
||||||
|
val bucket = time.withNano(0)
|
||||||
|
val newBucket = bucket != currentTimeBucket
|
||||||
|
if (newBucket)
|
||||||
|
currentTimeBucket = bucket
|
||||||
|
List((elem, newBucket))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.splitWhen(_._2) // split when time bucket changes
|
||||||
|
.map(_._1)
|
||||||
|
.fold(0)((acc, _) => acc + 1) // sum
|
||||||
|
.to(Sink.foreach(println))
|
||||||
|
.run()
|
||||||
|
// 3
|
||||||
|
// 10
|
||||||
|
// 10
|
||||||
|
// 10
|
||||||
|
// 10
|
||||||
|
// 10
|
||||||
|
// 10
|
||||||
|
// 10
|
||||||
|
// 10
|
||||||
|
// 10
|
||||||
|
// 7
|
||||||
|
//#splitWhen
|
||||||
|
}
|
||||||
|
|
||||||
|
def splitAfterExample(args: Array[String]): Unit = {
|
||||||
|
import akka.actor.ActorSystem
|
||||||
|
|
||||||
|
implicit val system: ActorSystem = ActorSystem()
|
||||||
|
|
||||||
|
//#splitAfter
|
||||||
|
Source(1 to 100)
|
||||||
|
.throttle(1, 100.millis)
|
||||||
|
.map(elem => (elem, Instant.now()))
|
||||||
|
.sliding(2)
|
||||||
|
.splitAfter { slidingElements =>
|
||||||
|
if (slidingElements.size == 2) {
|
||||||
|
val current = slidingElements.head
|
||||||
|
val next = slidingElements.tail.head
|
||||||
|
val currentBucket = LocalDateTime.ofInstant(current._2, ZoneOffset.UTC).withNano(0)
|
||||||
|
val nextBucket = LocalDateTime.ofInstant(next._2, ZoneOffset.UTC).withNano(0)
|
||||||
|
currentBucket != nextBucket
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.map(_.head._1)
|
||||||
|
.fold(0)((acc, _) => acc + 1) // sum
|
||||||
|
.to(Sink.foreach(println))
|
||||||
|
.run()
|
||||||
|
// 3
|
||||||
|
// 10
|
||||||
|
// 10
|
||||||
|
// 10
|
||||||
|
// 10
|
||||||
|
// 10
|
||||||
|
// 10
|
||||||
|
// 10
|
||||||
|
// 10
|
||||||
|
// 10
|
||||||
|
// 6
|
||||||
|
// note that the very last element is never included due to sliding,
|
||||||
|
// but that would not be problem for an infinite stream
|
||||||
|
//#splitAfter
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -13,6 +13,7 @@ object EventEnvelope extends AbstractFunction4[Offset, String, Long, Any, EventE
|
||||||
def apply(offset: Offset, persistenceId: String, sequenceNr: Long, event: Any, timestamp: Long): EventEnvelope =
|
def apply(offset: Offset, persistenceId: String, sequenceNr: Long, event: Any, timestamp: Long): EventEnvelope =
|
||||||
new EventEnvelope(offset, persistenceId, sequenceNr, event, timestamp)
|
new EventEnvelope(offset, persistenceId, sequenceNr, event, timestamp)
|
||||||
|
|
||||||
|
@deprecated("for binary compatibility", "2.6.2")
|
||||||
override def apply(offset: Offset, persistenceId: String, sequenceNr: Long, event: Any): EventEnvelope =
|
override def apply(offset: Offset, persistenceId: String, sequenceNr: Long, event: Any): EventEnvelope =
|
||||||
new EventEnvelope(offset, persistenceId, sequenceNr, event)
|
new EventEnvelope(offset, persistenceId, sequenceNr, event)
|
||||||
|
|
||||||
|
|
@ -37,7 +38,7 @@ final class EventEnvelope(
|
||||||
extends Product4[Offset, String, Long, Any]
|
extends Product4[Offset, String, Long, Any]
|
||||||
with Serializable {
|
with Serializable {
|
||||||
|
|
||||||
// for binary compatibility
|
@deprecated("for binary compatibility", "2.6.2")
|
||||||
def this(offset: Offset, persistenceId: String, sequenceNr: Long, event: Any) =
|
def this(offset: Offset, persistenceId: String, sequenceNr: Long, event: Any) =
|
||||||
this(offset, persistenceId, sequenceNr, event, 0L)
|
this(offset, persistenceId, sequenceNr, event, 0L)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -86,18 +86,18 @@ class EventsByTagSpec extends AkkaSpec(EventsByTagSpec.config) with Cleanup with
|
||||||
greenSrc
|
greenSrc
|
||||||
.runWith(TestSink.probe[Any])
|
.runWith(TestSink.probe[Any])
|
||||||
.request(2)
|
.request(2)
|
||||||
.expectNext(EventEnvelope(Sequence(1L), "a", 2L, "a green apple"))
|
.expectNext(EventEnvelope(Sequence(1L), "a", 2L, "a green apple", 0L))
|
||||||
.expectNext(EventEnvelope(Sequence(2L), "a", 3L, "a green banana"))
|
.expectNext(EventEnvelope(Sequence(2L), "a", 3L, "a green banana", 0L))
|
||||||
.expectNoMessage(500.millis)
|
.expectNoMessage(500.millis)
|
||||||
.request(2)
|
.request(2)
|
||||||
.expectNext(EventEnvelope(Sequence(3L), "b", 2L, "a green leaf"))
|
.expectNext(EventEnvelope(Sequence(3L), "b", 2L, "a green leaf", 0L))
|
||||||
.expectComplete()
|
.expectComplete()
|
||||||
|
|
||||||
val blackSrc = queries.currentEventsByTag(tag = "black", offset = Sequence(0L))
|
val blackSrc = queries.currentEventsByTag(tag = "black", offset = Sequence(0L))
|
||||||
blackSrc
|
blackSrc
|
||||||
.runWith(TestSink.probe[Any])
|
.runWith(TestSink.probe[Any])
|
||||||
.request(5)
|
.request(5)
|
||||||
.expectNext(EventEnvelope(Sequence(1L), "b", 1L, "a black car"))
|
.expectNext(EventEnvelope(Sequence(1L), "b", 1L, "a black car", 0L))
|
||||||
.expectComplete()
|
.expectComplete()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -108,8 +108,8 @@ class EventsByTagSpec extends AkkaSpec(EventsByTagSpec.config) with Cleanup with
|
||||||
val probe = greenSrc
|
val probe = greenSrc
|
||||||
.runWith(TestSink.probe[Any])
|
.runWith(TestSink.probe[Any])
|
||||||
.request(2)
|
.request(2)
|
||||||
.expectNext(EventEnvelope(Sequence(1L), "a", 2L, "a green apple"))
|
.expectNext(EventEnvelope(Sequence(1L), "a", 2L, "a green apple", 0L))
|
||||||
.expectNext(EventEnvelope(Sequence(2L), "a", 3L, "a green banana"))
|
.expectNext(EventEnvelope(Sequence(2L), "a", 3L, "a green banana", 0L))
|
||||||
.expectNoMessage(100.millis)
|
.expectNoMessage(100.millis)
|
||||||
|
|
||||||
c ! "a green cucumber"
|
c ! "a green cucumber"
|
||||||
|
|
@ -118,7 +118,7 @@ class EventsByTagSpec extends AkkaSpec(EventsByTagSpec.config) with Cleanup with
|
||||||
probe
|
probe
|
||||||
.expectNoMessage(100.millis)
|
.expectNoMessage(100.millis)
|
||||||
.request(5)
|
.request(5)
|
||||||
.expectNext(EventEnvelope(Sequence(3L), "b", 2L, "a green leaf"))
|
.expectNext(EventEnvelope(Sequence(3L), "b", 2L, "a green leaf", 0L))
|
||||||
.expectComplete() // green cucumber not seen
|
.expectComplete() // green cucumber not seen
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -128,8 +128,8 @@ class EventsByTagSpec extends AkkaSpec(EventsByTagSpec.config) with Cleanup with
|
||||||
.runWith(TestSink.probe[Any])
|
.runWith(TestSink.probe[Any])
|
||||||
.request(10)
|
.request(10)
|
||||||
// note that banana is not included, since exclusive offset
|
// note that banana is not included, since exclusive offset
|
||||||
.expectNext(EventEnvelope(Sequence(3L), "b", 2L, "a green leaf"))
|
.expectNext(EventEnvelope(Sequence(3L), "b", 2L, "a green leaf", 0L))
|
||||||
.expectNext(EventEnvelope(Sequence(4L), "c", 1L, "a green cucumber"))
|
.expectNext(EventEnvelope(Sequence(4L), "c", 1L, "a green cucumber", 0L))
|
||||||
.expectComplete()
|
.expectComplete()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -145,15 +145,15 @@ class EventsByTagSpec extends AkkaSpec(EventsByTagSpec.config) with Cleanup with
|
||||||
val pinkSrc = queries.currentEventsByTag(tag = "pink")
|
val pinkSrc = queries.currentEventsByTag(tag = "pink")
|
||||||
val probe = pinkSrc.runWith(TestSink.probe[Any])
|
val probe = pinkSrc.runWith(TestSink.probe[Any])
|
||||||
|
|
||||||
probe.request(1).expectNext(EventEnvelope(Sequence(1L), "z", 1L, "a pink apple"))
|
probe.request(1).expectNext(EventEnvelope(Sequence(1L), "z", 1L, "a pink apple", 0L))
|
||||||
|
|
||||||
system.log.info("delay before demand")
|
system.log.info("delay before demand")
|
||||||
|
|
||||||
probe
|
probe
|
||||||
.expectNoMessage(200.millis)
|
.expectNoMessage(200.millis)
|
||||||
.request(3)
|
.request(3)
|
||||||
.expectNext(EventEnvelope(Sequence(2L), "z", 2L, "a pink banana"))
|
.expectNext(EventEnvelope(Sequence(2L), "z", 2L, "a pink banana", 0L))
|
||||||
.expectNext(EventEnvelope(Sequence(3L), "z", 3L, "a pink orange"))
|
.expectNext(EventEnvelope(Sequence(3L), "z", 3L, "a pink orange", 0L))
|
||||||
.expectComplete()
|
.expectComplete()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -179,7 +179,7 @@ class EventsByTagSpec extends AkkaSpec(EventsByTagSpec.config) with Cleanup with
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
probe.request(2).expectNext(EventEnvelope(Sequence(1L), "b", 1L, "a black car")).expectNoMessage(100.millis)
|
probe.request(2).expectNext(EventEnvelope(Sequence(1L), "b", 1L, "a black car", 0L)).expectNoMessage(100.millis)
|
||||||
|
|
||||||
d ! "a black dog"
|
d ! "a black dog"
|
||||||
expectMsg(s"a black dog-done")
|
expectMsg(s"a black dog-done")
|
||||||
|
|
@ -187,10 +187,10 @@ class EventsByTagSpec extends AkkaSpec(EventsByTagSpec.config) with Cleanup with
|
||||||
expectMsg(s"a black night-done")
|
expectMsg(s"a black night-done")
|
||||||
|
|
||||||
probe
|
probe
|
||||||
.expectNext(EventEnvelope(Sequence(2L), "d", 1L, "a black dog"))
|
.expectNext(EventEnvelope(Sequence(2L), "d", 1L, "a black dog", 0L))
|
||||||
.expectNoMessage(100.millis)
|
.expectNoMessage(100.millis)
|
||||||
.request(10)
|
.request(10)
|
||||||
.expectNext(EventEnvelope(Sequence(3L), "d", 2L, "a black night"))
|
.expectNext(EventEnvelope(Sequence(3L), "d", 2L, "a black night", 0L))
|
||||||
} finally {
|
} finally {
|
||||||
probe.cancel()
|
probe.cancel()
|
||||||
}
|
}
|
||||||
|
|
@ -203,8 +203,8 @@ class EventsByTagSpec extends AkkaSpec(EventsByTagSpec.config) with Cleanup with
|
||||||
probe
|
probe
|
||||||
.request(10)
|
.request(10)
|
||||||
// note that banana is not included, since exclusive offset
|
// note that banana is not included, since exclusive offset
|
||||||
.expectNext(EventEnvelope(Sequence(3L), "b", 2L, "a green leaf"))
|
.expectNext(EventEnvelope(Sequence(3L), "b", 2L, "a green leaf", 0L))
|
||||||
.expectNext(EventEnvelope(Sequence(4L), "c", 1L, "a green cucumber"))
|
.expectNext(EventEnvelope(Sequence(4L), "c", 1L, "a green cucumber", 0L))
|
||||||
.expectNoMessage(100.millis)
|
.expectNoMessage(100.millis)
|
||||||
} finally {
|
} finally {
|
||||||
probe.cancel()
|
probe.cancel()
|
||||||
|
|
@ -222,7 +222,7 @@ class EventsByTagSpec extends AkkaSpec(EventsByTagSpec.config) with Cleanup with
|
||||||
val probe = yellowSrc
|
val probe = yellowSrc
|
||||||
.runWith(TestSink.probe[Any])
|
.runWith(TestSink.probe[Any])
|
||||||
.request(2)
|
.request(2)
|
||||||
.expectNext(EventEnvelope(Sequence(1L), "y", 1L, "a yellow car"))
|
.expectNext(EventEnvelope(Sequence(1L), "y", 1L, "a yellow car", 0L))
|
||||||
.expectNoMessage(100.millis)
|
.expectNoMessage(100.millis)
|
||||||
|
|
||||||
d ! "a yellow dog"
|
d ! "a yellow dog"
|
||||||
|
|
@ -231,10 +231,10 @@ class EventsByTagSpec extends AkkaSpec(EventsByTagSpec.config) with Cleanup with
|
||||||
expectMsg(s"a yellow night-done")
|
expectMsg(s"a yellow night-done")
|
||||||
|
|
||||||
probe
|
probe
|
||||||
.expectNext(EventEnvelope(Sequence(2L), "y", 2L, "a yellow dog"))
|
.expectNext(EventEnvelope(Sequence(2L), "y", 2L, "a yellow dog", 0L))
|
||||||
.expectNoMessage(100.millis)
|
.expectNoMessage(100.millis)
|
||||||
.request(10)
|
.request(10)
|
||||||
.expectNext(EventEnvelope(Sequence(3L), "y", 3L, "a yellow night"))
|
.expectNext(EventEnvelope(Sequence(3L), "y", 3L, "a yellow night", 0L))
|
||||||
|
|
||||||
probe.cancel()
|
probe.cancel()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -461,7 +461,7 @@ class EventSourcedBehaviorSpec
|
||||||
replyProbe.expectMessage(State(1, Vector(0)))
|
replyProbe.expectMessage(State(1, Vector(0)))
|
||||||
|
|
||||||
val events = queries.currentEventsByTag("tag1").runWith(Sink.seq).futureValue
|
val events = queries.currentEventsByTag("tag1").runWith(Sink.seq).futureValue
|
||||||
events shouldEqual List(EventEnvelope(Sequence(1), pid.id, 1, Incremented(1)))
|
events shouldEqual List(EventEnvelope(Sequence(1), pid.id, 1, Incremented(1), 0L))
|
||||||
}
|
}
|
||||||
|
|
||||||
"handle scheduled message arriving before recovery completed " in {
|
"handle scheduled message arriving before recovery completed " in {
|
||||||
|
|
|
||||||
|
|
@ -191,7 +191,7 @@ class EventSourcedEventAdapterSpec
|
||||||
replyProbe.expectMessage(State(1, Vector(0)))
|
replyProbe.expectMessage(State(1, Vector(0)))
|
||||||
|
|
||||||
val events = queries.currentEventsByPersistenceId(pid.id).runWith(Sink.seq).futureValue
|
val events = queries.currentEventsByPersistenceId(pid.id).runWith(Sink.seq).futureValue
|
||||||
events shouldEqual List(EventEnvelope(Sequence(1), pid.id, 1, GenericWrapper(Incremented(1))))
|
events shouldEqual List(EventEnvelope(Sequence(1), pid.id, 1, GenericWrapper(Incremented(1)), 0L))
|
||||||
|
|
||||||
val c2 =
|
val c2 =
|
||||||
spawn(Behaviors.setup[Command](ctx => counter(ctx, pid).eventAdapter(new GenericWrapperEventAdapter[Event])))
|
spawn(Behaviors.setup[Command](ctx => counter(ctx, pid).eventAdapter(new GenericWrapperEventAdapter[Event])))
|
||||||
|
|
@ -212,8 +212,8 @@ class EventSourcedEventAdapterSpec
|
||||||
|
|
||||||
val events = queries.currentEventsByPersistenceId(pid.id).runWith(Sink.seq).futureValue
|
val events = queries.currentEventsByPersistenceId(pid.id).runWith(Sink.seq).futureValue
|
||||||
events shouldEqual List(
|
events shouldEqual List(
|
||||||
EventEnvelope(Sequence(1), pid.id, 1, GenericWrapper(Incremented(1))),
|
EventEnvelope(Sequence(1), pid.id, 1, GenericWrapper(Incremented(1)), 0L),
|
||||||
EventEnvelope(Sequence(2), pid.id, 2, GenericWrapper(Incremented(1))))
|
EventEnvelope(Sequence(2), pid.id, 2, GenericWrapper(Incremented(1)), 0L))
|
||||||
|
|
||||||
val c2 =
|
val c2 =
|
||||||
spawn(Behaviors.setup[Command](ctx => counter(ctx, pid).eventAdapter(new GenericWrapperEventAdapter[Event])))
|
spawn(Behaviors.setup[Command](ctx => counter(ctx, pid).eventAdapter(new GenericWrapperEventAdapter[Event])))
|
||||||
|
|
@ -232,7 +232,7 @@ class EventSourcedEventAdapterSpec
|
||||||
replyProbe.expectMessage(State(1, Vector(0)))
|
replyProbe.expectMessage(State(1, Vector(0)))
|
||||||
|
|
||||||
val events = queries.currentEventsByPersistenceId(pid.id).runWith(Sink.seq).futureValue
|
val events = queries.currentEventsByPersistenceId(pid.id).runWith(Sink.seq).futureValue
|
||||||
events shouldEqual List(EventEnvelope(Sequence(1), pid.id, 1, GenericWrapper(Incremented(1))))
|
events shouldEqual List(EventEnvelope(Sequence(1), pid.id, 1, GenericWrapper(Incremented(1)), 0L))
|
||||||
|
|
||||||
val c2 =
|
val c2 =
|
||||||
spawn(Behaviors.setup[Command](ctx => counter(ctx, pid).eventAdapter(new GenericWrapperEventAdapter[Event])))
|
spawn(Behaviors.setup[Command](ctx => counter(ctx, pid).eventAdapter(new GenericWrapperEventAdapter[Event])))
|
||||||
|
|
@ -240,7 +240,7 @@ class EventSourcedEventAdapterSpec
|
||||||
replyProbe.expectMessage(State(1, Vector(0)))
|
replyProbe.expectMessage(State(1, Vector(0)))
|
||||||
|
|
||||||
val taggedEvents = queries.currentEventsByTag("tag99").runWith(Sink.seq).futureValue
|
val taggedEvents = queries.currentEventsByTag("tag99").runWith(Sink.seq).futureValue
|
||||||
taggedEvents shouldEqual List(EventEnvelope(Sequence(1), pid.id, 1, GenericWrapper(Incremented(1))))
|
taggedEvents shouldEqual List(EventEnvelope(Sequence(1), pid.id, 1, GenericWrapper(Incremented(1)), 0L))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2017-2019 Lightbend Inc. <https://www.lightbend.com>
|
* Copyright (C) 2017-2020 Lightbend Inc. <https://www.lightbend.com>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package akka.persistence.journal.inmem
|
package akka.persistence.journal.inmem
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,8 @@ object AkkaDisciplinePlugin extends AutoPlugin with ScalafixSupport {
|
||||||
val silencerVersion = "1.4.4"
|
val silencerVersion = "1.4.4"
|
||||||
Seq(
|
Seq(
|
||||||
libraryDependencies ++= Seq(
|
libraryDependencies ++= Seq(
|
||||||
compilerPlugin("com.github.ghik" %% "silencer-plugin" % silencerVersion cross CrossVersion.patch),
|
compilerPlugin(("com.github.ghik" %% "silencer-plugin" % silencerVersion).cross(CrossVersion.patch)),
|
||||||
"com.github.ghik" %% "silencer-lib" % silencerVersion % Provided cross CrossVersion.patch))
|
("com.github.ghik" %% "silencer-lib" % silencerVersion % Provided).cross(CrossVersion.patch)))
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy val disciplineSettings =
|
lazy val disciplineSettings =
|
||||||
|
|
@ -39,7 +39,6 @@ object AkkaDisciplinePlugin extends AutoPlugin with ScalafixSupport {
|
||||||
else Seq.empty
|
else Seq.empty
|
||||||
),
|
),
|
||||||
Test / scalacOptions --= testUndicipline,
|
Test / scalacOptions --= testUndicipline,
|
||||||
Compile / console / scalacOptions --= Seq("-deprecation", "-Xfatal-warnings", "-Xlint", "-Ywarn-unused:imports"),
|
|
||||||
Compile / scalacOptions ++= (CrossVersion.partialVersion(scalaVersion.value) match {
|
Compile / scalacOptions ++= (CrossVersion.partialVersion(scalaVersion.value) match {
|
||||||
case Some((2, 13)) =>
|
case Some((2, 13)) =>
|
||||||
disciplineScalacOptions -- Set(
|
disciplineScalacOptions -- Set(
|
||||||
|
|
@ -61,7 +60,9 @@ object AkkaDisciplinePlugin extends AutoPlugin with ScalafixSupport {
|
||||||
// different compiler phases from the regular run), and in particular
|
// different compiler phases from the regular run), and in particular
|
||||||
// '-Ywarn-unused:explicits' breaks 'sbt ++2.13.0-M5 akka-actor/doc'
|
// '-Ywarn-unused:explicits' breaks 'sbt ++2.13.0-M5 akka-actor/doc'
|
||||||
// https://github.com/akka/akka/issues/26119
|
// https://github.com/akka/akka/issues/26119
|
||||||
Compile / doc / scalacOptions --= disciplineScalacOptions.toSeq :+ "-Xfatal-warnings")
|
Compile / doc / scalacOptions --= disciplineScalacOptions.toSeq :+ "-Xfatal-warnings",
|
||||||
|
// having discipline warnings in console is just an annoyance
|
||||||
|
Compile / console / scalacOptions --= disciplineScalacOptions.toSeq)
|
||||||
|
|
||||||
val testUndicipline = Seq(
|
val testUndicipline = Seq(
|
||||||
"-Ywarn-dead-code", // ??? used in compile only specs
|
"-Ywarn-dead-code", // ??? used in compile only specs
|
||||||
|
|
|
||||||
|
|
@ -16,12 +16,12 @@ object Dependencies {
|
||||||
lazy val java8CompatVersion = settingKey[String]("The version of scala-java8-compat to use.")
|
lazy val java8CompatVersion = settingKey[String]("The version of scala-java8-compat to use.")
|
||||||
|
|
||||||
val junitVersion = "4.13"
|
val junitVersion = "4.13"
|
||||||
val slf4jVersion = "1.7.29"
|
val slf4jVersion = "1.7.30"
|
||||||
// check agrona version when updating this
|
// check agrona version when updating this
|
||||||
val aeronVersion = "1.24.0"
|
val aeronVersion = "1.25.0"
|
||||||
// needs to be inline with the aeron version, check
|
// needs to be inline with the aeron version, check
|
||||||
// https://github.com/real-logic/aeron/blob/1.x.y/build.gradle
|
// https://github.com/real-logic/aeron/blob/1.x.y/build.gradle
|
||||||
val agronaVersion = "1.1.0"
|
val agronaVersion = "1.2.0"
|
||||||
val nettyVersion = "3.10.6.Final"
|
val nettyVersion = "3.10.6.Final"
|
||||||
val jacksonVersion = "2.10.2"
|
val jacksonVersion = "2.10.2"
|
||||||
val protobufJavaVersion = "3.10.0"
|
val protobufJavaVersion = "3.10.0"
|
||||||
|
|
@ -37,7 +37,7 @@ object Dependencies {
|
||||||
val Versions = Seq(
|
val Versions = Seq(
|
||||||
crossScalaVersions := Seq(scala212Version, scala213Version),
|
crossScalaVersions := Seq(scala212Version, scala213Version),
|
||||||
scalaVersion := System.getProperty("akka.build.scalaVersion", crossScalaVersions.value.head),
|
scalaVersion := System.getProperty("akka.build.scalaVersion", crossScalaVersions.value.head),
|
||||||
scalaCheckVersion := sys.props.get("akka.build.scalaCheckVersion").getOrElse("1.14.2"),
|
scalaCheckVersion := sys.props.get("akka.build.scalaCheckVersion").getOrElse("1.14.3"),
|
||||||
scalaTestVersion := "3.1.0",
|
scalaTestVersion := "3.1.0",
|
||||||
scalaTestPlusVersion := "3.1.0.0",
|
scalaTestPlusVersion := "3.1.0.0",
|
||||||
java8CompatVersion := {
|
java8CompatVersion := {
|
||||||
|
|
@ -128,8 +128,8 @@ object Dependencies {
|
||||||
val dockerClient = "com.spotify" % "docker-client" % "8.16.0" % "test" // ApacheV2
|
val dockerClient = "com.spotify" % "docker-client" % "8.16.0" % "test" // ApacheV2
|
||||||
|
|
||||||
// metrics, measurements, perf testing
|
// metrics, measurements, perf testing
|
||||||
val metrics = "io.dropwizard.metrics" % "metrics-core" % "4.1.1" % "test" // ApacheV2
|
val metrics = "io.dropwizard.metrics" % "metrics-core" % "4.1.2" % "test" // ApacheV2
|
||||||
val metricsJvm = "io.dropwizard.metrics" % "metrics-jvm" % "4.1.1" % "test" // ApacheV2
|
val metricsJvm = "io.dropwizard.metrics" % "metrics-jvm" % "4.1.2" % "test" // ApacheV2
|
||||||
val latencyUtils = "org.latencyutils" % "LatencyUtils" % "2.0.3" % "test" // Free BSD
|
val latencyUtils = "org.latencyutils" % "LatencyUtils" % "2.0.3" % "test" // Free BSD
|
||||||
val hdrHistogram = "org.hdrhistogram" % "HdrHistogram" % "2.1.12" % "test" // CC0
|
val hdrHistogram = "org.hdrhistogram" % "HdrHistogram" % "2.1.12" % "test" // CC0
|
||||||
val metricsAll = Seq(metrics, metricsJvm, latencyUtils, hdrHistogram)
|
val metricsAll = Seq(metrics, metricsJvm, latencyUtils, hdrHistogram)
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.11")
|
||||||
// sbt-osgi 0.9.5 is available but breaks including jdk9-only classes
|
// sbt-osgi 0.9.5 is available but breaks including jdk9-only classes
|
||||||
addSbtPlugin("com.typesafe.sbt" % "sbt-osgi" % "0.9.4")
|
addSbtPlugin("com.typesafe.sbt" % "sbt-osgi" % "0.9.4")
|
||||||
addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "0.6.1")
|
addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "0.6.1")
|
||||||
addSbtPlugin("com.jsuereth" % "sbt-pgp" % "2.0.0")
|
addSbtPlugin("com.jsuereth" % "sbt-pgp" % "2.0.1")
|
||||||
addSbtPlugin("com.eed3si9n" % "sbt-unidoc" % "0.4.2")
|
addSbtPlugin("com.eed3si9n" % "sbt-unidoc" % "0.4.2")
|
||||||
addSbtPlugin("com.thoughtworks.sbt-api-mappings" % "sbt-api-mappings" % "3.0.0")
|
addSbtPlugin("com.thoughtworks.sbt-api-mappings" % "sbt-api-mappings" % "3.0.0")
|
||||||
addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.3.7")
|
addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.3.7")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue