From 8eb3a5092eb72ad1dc6e96e8a1533d6a2ac5b980 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Wed, 27 Mar 2024 20:13:59 +0100 Subject: [PATCH] support jackson buffer recycler (#519) * support jackson buffer recycler * javafmt * add tests * Update Jackson.java --- .../javadsl/marshallers/jackson/Jackson.java | 24 ++++++++++++ .../src/main/resources/reference.conf | 16 +++++++- .../marshallers/jackson/JacksonTest.java | 38 ++++++++++++++++--- 3 files changed, 70 insertions(+), 8 deletions(-) diff --git a/http-marshallers-java/http-jackson/src/main/java/org/apache/pekko/http/javadsl/marshallers/jackson/Jackson.java b/http-marshallers-java/http-jackson/src/main/java/org/apache/pekko/http/javadsl/marshallers/jackson/Jackson.java index 54c5e8f28..401b60604 100644 --- a/http-marshallers-java/http-jackson/src/main/java/org/apache/pekko/http/javadsl/marshallers/jackson/Jackson.java +++ b/http-marshallers-java/http-jackson/src/main/java/org/apache/pekko/http/javadsl/marshallers/jackson/Jackson.java @@ -30,6 +30,9 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.StreamReadConstraints; import com.fasterxml.jackson.core.StreamWriteConstraints; +import com.fasterxml.jackson.core.util.BufferRecycler; +import com.fasterxml.jackson.core.util.JsonRecyclerPools; +import com.fasterxml.jackson.core.util.RecyclerPool; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.json.JsonMapper; @@ -113,7 +116,28 @@ static ObjectMapper createMapper(final Config config) { JsonFactory.builder() .streamReadConstraints(streamReadConstraints) .streamWriteConstraints(streamWriteConstraints) + .recyclerPool(getBufferRecyclerPool(config)) .build(); return new JsonMapper(jsonFactory); } + + private static RecyclerPool getBufferRecyclerPool(final Config cfg) { + final String poolType = cfg.getString("buffer-recycler.pool-instance"); + switch (poolType) { + case "thread-local": + return JsonRecyclerPools.threadLocalPool(); + case "lock-free": + return JsonRecyclerPools.newLockFreePool(); + case "shared-lock-free": + return JsonRecyclerPools.sharedLockFreePool(); + case "concurrent-deque": + return JsonRecyclerPools.newConcurrentDequePool(); + case "shared-concurrent-deque": + return JsonRecyclerPools.sharedConcurrentDequePool(); + case "bounded": + return JsonRecyclerPools.newBoundedPool(cfg.getInt("buffer-recycler.bounded-pool-size")); + default: + throw new IllegalArgumentException("Unknown recycler-pool: " + poolType); + } + } } diff --git a/http-marshallers-java/http-jackson/src/main/resources/reference.conf b/http-marshallers-java/http-jackson/src/main/resources/reference.conf index 3ffbbba55..c645d86fd 100644 --- a/http-marshallers-java/http-jackson/src/main/resources/reference.conf +++ b/http-marshallers-java/http-jackson/src/main/resources/reference.conf @@ -9,7 +9,7 @@ pekko.http.marshallers.jackson { read { - # see https://www.javadoc.io/static/com.fasterxml.jackson.core/jackson-core/2.16.1/com/fasterxml/jackson/core/StreamReadConstraints.html + # see https://www.javadoc.io/static/com.fasterxml.jackson.core/jackson-core/2.16.2/com/fasterxml/jackson/core/StreamReadConstraints.html # these defaults are the same as the defaults in `StreamReadConstraints` max-nesting-depth = 1000 max-number-length = 1000 @@ -20,8 +20,20 @@ pekko.http.marshallers.jackson { } write { - # see https://www.javadoc.io/static/com.fasterxml.jackson.core/jackson-core/2.16.1/com/fasterxml/jackson/core/StreamWriteConstraints.html + # see https://www.javadoc.io/static/com.fasterxml.jackson.core/jackson-core/2.16.2/com/fasterxml/jackson/core/StreamWriteConstraints.html # these defaults are the same as the defaults in `StreamWriteConstraints` max-nesting-depth = 1000 } + + # Controls the Buffer Recycler Pool implementation used by Jackson. + # https://javadoc.io/static/com.fasterxml.jackson.core/jackson-core/2.16.2/com/fasterxml/jackson/core/util/JsonRecyclerPools.html + # The default is "thread-local" which is the same as the default in Jackson 2.16. + buffer-recycler { + # the supported values are "thread-local", "lock-free", "shared-lock-free", "concurrent-deque", + # "shared-concurrent-deque", "bounded" + pool-instance = "thread-local" + # the maximum size of bounded recycler pools - must be >=1 or an IllegalArgumentException will occur + # only applies to pool-instance type "bounded" + bounded-pool-size = 100 + } } diff --git a/http-marshallers-java/http-jackson/src/test/java/org/apache/pekko/http/javadsl/marshallers/jackson/JacksonTest.java b/http-marshallers-java/http-jackson/src/test/java/org/apache/pekko/http/javadsl/marshallers/jackson/JacksonTest.java index 7a4ebb122..d14efe738 100644 --- a/http-marshallers-java/http-jackson/src/test/java/org/apache/pekko/http/javadsl/marshallers/jackson/JacksonTest.java +++ b/http-marshallers-java/http-jackson/src/test/java/org/apache/pekko/http/javadsl/marshallers/jackson/JacksonTest.java @@ -17,6 +17,9 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.StreamReadConstraints; import com.fasterxml.jackson.core.StreamWriteConstraints; +import com.fasterxml.jackson.core.util.BufferRecycler; +import com.fasterxml.jackson.core.util.JsonRecyclerPools.BoundedPool; +import com.fasterxml.jackson.core.util.RecyclerPool; import com.fasterxml.jackson.databind.ObjectMapper; import com.typesafe.config.Config; import com.typesafe.config.ConfigFactory; @@ -107,9 +110,7 @@ public void configStreamReadsConstraints() throws Exception { + "\n" + "read.max-nesting-depth=" + maxNestingDepth; - Config config = - ConfigFactory.parseString(configText) - .withFallback(ConfigFactory.load().getConfig("pekko.http.marshallers.jackson")); + Config config = ConfigFactory.parseString(configText).withFallback(getDefaultConfig()); ObjectMapper mapper = Jackson.createMapper(config); StreamReadConstraints constraints = mapper.getFactory().streamReadConstraints(); assertEquals(maxNumLen, constraints.getMaxNumberLength()); @@ -123,11 +124,36 @@ public void configStreamReadsConstraints() throws Exception { public void configStreamWritesConstraints() throws Exception { final int maxNestingDepth = 5; String configText = "write.max-nesting-depth=" + maxNestingDepth; - Config config = - ConfigFactory.parseString(configText) - .withFallback(ConfigFactory.load().getConfig("pekko.http.marshallers.jackson")); + Config config = ConfigFactory.parseString(configText).withFallback(getDefaultConfig()); ObjectMapper mapper = Jackson.createMapper(config); StreamWriteConstraints constraints = mapper.getFactory().streamWriteConstraints(); assertEquals(maxNestingDepth, constraints.getMaxNestingDepth()); } + + @Test + public void testDefaultFactory() throws Exception { + ObjectMapper mapper = Jackson.createMapper(getDefaultConfig()); + RecyclerPool recyclerPool = mapper.getFactory()._getRecyclerPool(); + assertEquals("ThreadLocalPool", recyclerPool.getClass().getSimpleName()); + } + + @Test + public void testFactoryWithBufferRecyclerSetting() throws Exception { + final String poolType = "bounded"; + final int poolSize = 10; + String configText = + "buffer-recycler.pool-instance=" + + poolType + + "\nbuffer-recycler.bounded-pool-size=" + + poolSize; + Config config = ConfigFactory.parseString(configText).withFallback(getDefaultConfig()); + ObjectMapper mapper = Jackson.createMapper(config); + RecyclerPool recyclerPool = mapper.getFactory()._getRecyclerPool(); + assertEquals("BoundedPool", recyclerPool.getClass().getSimpleName()); + assertEquals(poolSize, ((BoundedPool) recyclerPool).capacity()); + } + + private static Config getDefaultConfig() { + return ConfigFactory.load().getConfig("pekko.http.marshallers.jackson"); + } }