Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support jackson buffer recycler #519

Merged
merged 4 commits into from
Mar 27, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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) {
switch (cfg.getString("buffer-recycler.pool-instance")) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As it's been used in the exceptions, how about extract to a local val?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And maybe we can assert it should be null too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is defaulted in reference.conf - virtually every config would blow up if we didn't have defaults - nowhere do we need to check for them not being set - because we have defaults in reference.conf

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: " + cfg.getString("buffer-recycler.pool-instance"));
pjfanning marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
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");
}
}
Loading