Skip to content

Commit

Permalink
support jackson buffer recycler (#519)
Browse files Browse the repository at this point in the history
* support jackson buffer recycler

* javafmt

* add tests

* Update Jackson.java
  • Loading branch information
pjfanning authored Mar 27, 2024
1 parent 34be8ce commit 8eb3a50
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<BufferRecycler> 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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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());
Expand All @@ -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<BufferRecycler> 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<BufferRecycler> 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");
}
}

0 comments on commit 8eb3a50

Please sign in to comment.