From 57fd8895486e21ff703426e92ec03a9dd8c352ed Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Thu, 13 Jun 2019 21:53:21 +0200 Subject: [PATCH] remove Jackson afterburner, since no visible improvement #24155 (#27064) * remove Jackson afterburner, since no visible improvement #24155 * add back timeMessage benchmark * improve custom mapper test --- .../serialization/jackson/JavaMessages.java | 97 +++++++++++++++++++ .../jackson/JacksonSerializationBench.scala | 83 +++++++++++++++- .../src/main/resources/reference.conf | 3 - .../jackson/JavaTestMessages.java | 34 +++++++ .../jackson/JacksonSerializerSpec.scala | 29 ++++-- build.sbt | 1 + project/Dependencies.scala | 2 - 7 files changed, 231 insertions(+), 18 deletions(-) create mode 100644 akka-bench-jmh/src/main/java/akka/serialization/jackson/JavaMessages.java diff --git a/akka-bench-jmh/src/main/java/akka/serialization/jackson/JavaMessages.java b/akka-bench-jmh/src/main/java/akka/serialization/jackson/JavaMessages.java new file mode 100644 index 0000000000..8d5ce060e1 --- /dev/null +++ b/akka-bench-jmh/src/main/java/akka/serialization/jackson/JavaMessages.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2019 Lightbend Inc. + */ + +package akka.serialization.jackson; + +import com.fasterxml.jackson.annotation.JsonCreator; + +import java.time.Duration; +import java.time.Instant; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; + +public class JavaMessages { + interface JTestMessage {} + + public static class JSmall implements JTestMessage { + public final String name; + public final int num; + + public JSmall(String name, int num) { + this.name = name; + this.num = num; + } + } + + public static class JMedium implements JTestMessage { + public final String field1; + public final String field2; + public final String field3; + public final int num1; + public final int num2; + public final int num3; + public final boolean flag1; + public final boolean flag2; + public final Duration duration; + + public final LocalDateTime date; + public final Instant instant; + public final JSmall nested1; + public final JSmall nested2; + public final JSmall nested3; + + public JMedium( + String field1, + String field2, + String field3, + int num1, + int num2, + int num3, + boolean flag1, + boolean flag2, + Duration duration, + LocalDateTime date, + Instant instant, + JSmall nested1, + JSmall nested2, + JSmall nested3) { + this.field1 = field1; + this.field2 = field2; + this.field3 = field3; + this.num1 = num1; + this.num2 = num2; + this.num3 = num3; + this.flag1 = flag1; + this.flag2 = flag2; + this.duration = duration; + this.date = date; + this.instant = instant; + this.nested1 = nested1; + this.nested2 = nested2; + this.nested3 = nested3; + } + } + + public static class JLarge implements JTestMessage { + public final JMedium nested1; + public final JMedium nested2; + public final JMedium nested3; + public final List list; + public final Map map; + + public JLarge( + JMedium nested1, + JMedium nested2, + JMedium nested3, + List list, + Map map) { + this.nested1 = nested1; + this.nested2 = nested2; + this.nested3 = nested3; + this.list = list; + this.map = map; + } + } +} diff --git a/akka-bench-jmh/src/main/scala/akka/serialization/jackson/JacksonSerializationBench.scala b/akka-bench-jmh/src/main/scala/akka/serialization/jackson/JacksonSerializationBench.scala index 8ec9caa235..029464e320 100644 --- a/akka-bench-jmh/src/main/scala/akka/serialization/jackson/JacksonSerializationBench.scala +++ b/akka-bench-jmh/src/main/scala/akka/serialization/jackson/JacksonSerializationBench.scala @@ -6,6 +6,8 @@ package akka.serialization.jackson import java.time.Instant import java.time.LocalDateTime +import java.time.{ Duration => JDuration } +import java.util import java.util.concurrent.TimeUnit import scala.concurrent.Await @@ -51,11 +53,10 @@ object JacksonSerializationBench { final class TimeMessage(val duration: FiniteDuration, val date: LocalDateTime, val instant: Instant) extends TestMessage - // FIXME try with plain java classes (not case class) } @State(Scope.Benchmark) -@OutputTimeUnit(TimeUnit.MILLISECONDS) +@OutputTimeUnit(TimeUnit.SECONDS) @BenchmarkMode(Array(Mode.Throughput)) @Fork(2) @Warmup(iterations = 4) @@ -120,6 +121,66 @@ class JacksonSerializationBench { val timeMsg = new TimeMessage(5.seconds, LocalDateTime.of(2019, 4, 29, 23, 15, 3, 12345), Instant.now()) + import JavaMessages._ + val jSmallMsg1 = new JSmall("abc", 17) + val jSmallMsg2 = new JSmall("def", 18) + val jSmallMsg3 = new JSmall("ghi", 19) + val jMediumMsg1 = new JMedium( + "abc", + "def", + "ghi", + 1, + 2, + 3, + false, + true, + JDuration.ofSeconds(5), + LocalDateTime.of(2019, 4, 29, 23, 15, 3, 12345), + Instant.now(), + jSmallMsg1, + jSmallMsg2, + jSmallMsg3) + val jMediumMsg2 = new JMedium( + "ABC", + "DEF", + "GHI", + 10, + 20, + 30, + true, + false, + JDuration.ofMillis(5), + LocalDateTime.of(2019, 4, 29, 23, 15, 4, 12345), + Instant.now(), + jSmallMsg1, + jSmallMsg2, + jSmallMsg3) + val jMediumMsg3 = new JMedium( + "abcABC", + "defDEF", + "ghiGHI", + 100, + 200, + 300, + true, + true, + JDuration.ofMillis(200), + LocalDateTime.of(2019, 4, 29, 23, 15, 5, 12345), + Instant.now(), + jSmallMsg1, + jSmallMsg2, + jSmallMsg3) + val jMap = new util.HashMap[String, JMedium]() + jMap.put("a", jMediumMsg1) + jMap.put("b", jMediumMsg2) + jMap.put("c", jMediumMsg3) + val jLargeMsg = new JLarge( + jMediumMsg1, + jMediumMsg2, + jMediumMsg3, + java.util.Arrays.asList(jMediumMsg1, jMediumMsg2, jMediumMsg3), + jMap) + var system: ActorSystem = _ var serialization: Serialization = _ @@ -133,7 +194,8 @@ class JacksonSerializationBench { loglevel = WARNING actor { serialization-bindings { - "akka.serialization.jackson.JacksonSerializationBench$$TestMessage" = $serializerName + "${classOf[TestMessage].getName}" = $serializerName + "${classOf[JTestMessage].getName}" = $serializerName } } serialization.jackson { @@ -185,6 +247,21 @@ class JacksonSerializationBench { serializeDeserialize(largeMsg) } + @Benchmark + def jSmall(): JSmall = { + serializeDeserialize(jSmallMsg1) + } + + @Benchmark + def jMedium(): JMedium = { + serializeDeserialize(jMediumMsg1) + } + + @Benchmark + def jLarge(): JLarge = { + serializeDeserialize(jLargeMsg) + } + @Benchmark def timeMessage(): TimeMessage = { serializeDeserialize(timeMsg) diff --git a/akka-serialization-jackson/src/main/resources/reference.conf b/akka-serialization-jackson/src/main/resources/reference.conf index d917834b91..0da9d7ab0f 100644 --- a/akka-serialization-jackson/src/main/resources/reference.conf +++ b/akka-serialization-jackson/src/main/resources/reference.conf @@ -18,9 +18,6 @@ akka.serialization.jackson { jackson-modules += "com.fasterxml.jackson.datatype.jdk8.Jdk8Module" jackson-modules += "com.fasterxml.jackson.datatype.jsr310.JavaTimeModule" jackson-modules += "com.fasterxml.jackson.module.scala.DefaultScalaModule" - jackson-modules += "com.fasterxml.jackson.module.afterburner.AfterburnerModule" - #jackson-modules += "com.fasterxml.jackson.datatype.pcollections.PCollectionsModule" - #jackson-modules += "com.fasterxml.jackson.datatype.guava.GuavaModule" } #//#jackson-modules diff --git a/akka-serialization-jackson/src/test/java/akka/serialization/jackson/JavaTestMessages.java b/akka-serialization-jackson/src/test/java/akka/serialization/jackson/JavaTestMessages.java index 04895a1f96..3e0ddc9db9 100644 --- a/akka-serialization-jackson/src/test/java/akka/serialization/jackson/JavaTestMessages.java +++ b/akka-serialization-jackson/src/test/java/akka/serialization/jackson/JavaTestMessages.java @@ -12,6 +12,7 @@ import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; import java.time.Duration; +import java.time.Instant; import java.time.LocalDateTime; import java.util.List; import java.util.Optional; @@ -204,6 +205,39 @@ public interface JavaTestMessages { } } + public class InstantCommand implements TestMessage { + public final Instant instant; + + @JsonCreator + public InstantCommand(Instant instant) { + this.instant = instant; + } + + public Instant getInstant() { + return instant; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + InstantCommand that = (InstantCommand) o; + + return instant.equals(that.instant); + } + + @Override + public int hashCode() { + return instant.hashCode(); + } + + @Override + public String toString() { + return "InstantCommand{" + "instant=" + instant + '}'; + } + } + public class CommandWithActorRef implements TestMessage { public final String name; public final ActorRef replyTo; diff --git a/akka-serialization-jackson/src/test/scala/akka/serialization/jackson/JacksonSerializerSpec.scala b/akka-serialization-jackson/src/test/scala/akka/serialization/jackson/JacksonSerializerSpec.scala index 6507d034d8..b5049744c2 100644 --- a/akka-serialization-jackson/src/test/scala/akka/serialization/jackson/JacksonSerializerSpec.scala +++ b/akka-serialization-jackson/src/test/scala/akka/serialization/jackson/JacksonSerializerSpec.scala @@ -5,6 +5,7 @@ package akka.serialization.jackson import java.time.Duration +import java.time.Instant import java.time.LocalDateTime import java.time.temporal.ChronoUnit import java.util.Arrays @@ -39,7 +40,7 @@ import com.fasterxml.jackson.databind.SerializationFeature import com.fasterxml.jackson.databind.exc.InvalidTypeIdException import com.fasterxml.jackson.databind.node.IntNode import com.fasterxml.jackson.databind.node.ObjectNode -import com.fasterxml.jackson.module.afterburner.AfterburnerModule +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule import com.typesafe.config.ConfigFactory import org.scalatest.BeforeAndAfterAll import org.scalatest.Matchers @@ -61,6 +62,7 @@ object ScalaTestMessages { final case class OptionCommand(maybe: Option[String]) extends TestMessage final case class BooleanCommand(published: Boolean) extends TestMessage final case class TimeCommand(timestamp: LocalDateTime, duration: FiniteDuration) extends TestMessage + final case class InstantCommand(instant: Instant) extends TestMessage final case class CollectionsCommand(strings: List[String], objects: Vector[SimpleCommand]) extends TestMessage final case class CommandWithActorRef(name: String, replyTo: ActorRef) extends TestMessage final case class CommandWithTypedActorRef(name: String, replyTo: akka.actor.typed.ActorRef[String]) @@ -209,6 +211,16 @@ class JacksonJsonSerializerSpec extends JacksonSerializerSpec("jackson-json") { } } + "serialize Instant as text with ISO-8601 date format (default)" in { + val msg = new InstantCommand(Instant.ofEpochMilli(1559907792075L)) + val json = serializeToJsonString(msg) + val expected = """{"instant":"2019-06-07T11:43:12.075Z"}""" + json should ===(expected) + + // and full round trip + checkSerialization(msg) + } + // FAIL_ON_UNKNOWN_PROPERTIES = off is default in reference.conf "not fail on unknown properties" in { val json = """{"name":"abc","name2":"def","name3":"ghi"}""" @@ -251,7 +263,7 @@ class JacksonJsonSerializerSpec extends JacksonSerializerSpec("jackson-json") { bindingName: String, configuredModules: immutable.Seq[Module]): immutable.Seq[Module] = if (bindingName == "jackson-json") { - configuredModules.filterNot(_.isInstanceOf[AfterburnerModule]) + configuredModules.filterNot(_.isInstanceOf[JavaTimeModule]) } else super.overrideConfiguredModules(bindingName, configuredModules) } @@ -262,15 +274,12 @@ class JacksonJsonSerializerSpec extends JacksonSerializerSpec("jackson-json") { .withSetup(JacksonObjectMapperProviderSetup(customJacksonObjectMapperFactory)) .withSetup(BootstrapSetup(config)) withSystem(setup) { sys => - val msg = SimpleCommand2("a", "b") + val msg = InstantCommand(Instant.ofEpochMilli(1559907792075L)) val json = serializeToJsonString(msg, sys) - // using the custom ObjectMapper with pretty printing enabled - val expected = - """|{ - | "name" : "a", - | "name2" : "b" - |}""".stripMargin - json should ===(expected) + // using the custom ObjectMapper with pretty printing enabled, and no JavaTimeModule + json should include(""" "instant" : {""") + json should include(""" "seconds" : 1559907792,""") + json should include(""" "nanos" : 75000000,""") } } diff --git a/build.sbt b/build.sbt index 4e563120f5..bdee61b4f2 100644 --- a/build.sbt +++ b/build.sbt @@ -106,6 +106,7 @@ lazy val benchJmh = akkaModule("akka-bench-jmh") .dependsOn(Seq(actor, stream, streamTests, persistence, distributedData, jackson, testkit).map( _ % "compile->compile;compile->test"): _*) .settings(Dependencies.benchJmh) + .settings(javacOptions += "-parameters") // for Jackson .enablePlugins(JmhPlugin, ScaladocNoVerificationOfDiagrams, NoPublish, CopyrightHeader) .disablePlugins(MimaPlugin, WhiteSourcePlugin, ValidatePullRequest, CopyrightHeaderInPr) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 88a0f86724..0679446cf1 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -96,7 +96,6 @@ object Dependencies { val jacksonJsr310 = "com.fasterxml.jackson.datatype" % "jackson-datatype-jsr310" % jacksonVersion // ApacheV2 val jacksonScala = "com.fasterxml.jackson.module" %% "jackson-module-scala" % jacksonVersion // ApacheV2 val jacksonParameterNames = "com.fasterxml.jackson.module" % "jackson-module-parameter-names" % jacksonVersion // ApacheV2 - val jacksonAfterburner = "com.fasterxml.jackson.module" % "jackson-module-afterburner" % jacksonVersion // ApacheV2 val jacksonCbor = "com.fasterxml.jackson.dataformat" % "jackson-dataformat-cbor" % jacksonVersion // ApacheV2 object Docs { @@ -243,7 +242,6 @@ object Dependencies { jacksonJdk8, jacksonJsr310, jacksonParameterNames, - jacksonAfterburner, jacksonCbor, Test.junit, Test.scalatest.value)