From 8835c07a90d274ee2c1d49b85b44285a453ef98c Mon Sep 17 00:00:00 2001 From: Matt Brown Date: Wed, 29 May 2019 20:01:14 -0400 Subject: [PATCH] perf(orca): reuse ObjectMapper instances (#2937) creating a new ObjectMapper instance for each Stage or other per-request contexts can be wasteful; ObjectMapper is safe to reuse across threads (after it is configured). See https://github.com/spinnaker/spinnaker/issues/4367 for more details. --- .../orca/kato/tasks/DeploymentDetailsAware.groovy | 2 +- .../spinnaker/orca/config/OrcaConfiguration.java | 2 +- .../spinnaker/orca/jackson/OrcaObjectMapper.java | 13 +++++++++++++ .../spinnaker/orca/pipeline/model/Stage.java | 2 +- .../orca/pipeline/util/BuildDetailExtractor.java | 2 +- .../pipeline/util/ContextParameterProcessor.java | 2 +- .../ServiceKeyExpressionFunctionProvider.java | 2 +- .../pipelinetemplate/PipelineTemplateService.java | 2 +- .../spinnaker/orca/q/handler/ExpressionAware.kt | 2 +- .../persistence/jedis/RedisExecutionRepository.java | 2 +- .../orca/web/config/WebConfiguration.groovy | 2 +- 11 files changed, 23 insertions(+), 10 deletions(-) diff --git a/orca-clouddriver/src/main/groovy/com/netflix/spinnaker/orca/kato/tasks/DeploymentDetailsAware.groovy b/orca-clouddriver/src/main/groovy/com/netflix/spinnaker/orca/kato/tasks/DeploymentDetailsAware.groovy index 0919038f4f..93c0e97144 100644 --- a/orca-clouddriver/src/main/groovy/com/netflix/spinnaker/orca/kato/tasks/DeploymentDetailsAware.groovy +++ b/orca-clouddriver/src/main/groovy/com/netflix/spinnaker/orca/kato/tasks/DeploymentDetailsAware.groovy @@ -33,7 +33,7 @@ import static com.netflix.spinnaker.orca.pipeline.model.Execution.ExecutionType. */ trait DeploymentDetailsAware { - private ObjectMapper pipelineObjectMapper = OrcaObjectMapper.newInstance() + private ObjectMapper pipelineObjectMapper = OrcaObjectMapper.getInstance() void withImageFromPrecedingStage( Stage stage, diff --git a/orca-core/src/main/java/com/netflix/spinnaker/orca/config/OrcaConfiguration.java b/orca-core/src/main/java/com/netflix/spinnaker/orca/config/OrcaConfiguration.java index b2ebfe4640..c2fe5b02d0 100644 --- a/orca-core/src/main/java/com/netflix/spinnaker/orca/config/OrcaConfiguration.java +++ b/orca-core/src/main/java/com/netflix/spinnaker/orca/config/OrcaConfiguration.java @@ -97,7 +97,7 @@ public Scheduler scheduler() { @Bean public ObjectMapper mapper() { - return OrcaObjectMapper.newInstance(); + return OrcaObjectMapper.getInstance(); } @Bean diff --git a/orca-core/src/main/java/com/netflix/spinnaker/orca/jackson/OrcaObjectMapper.java b/orca-core/src/main/java/com/netflix/spinnaker/orca/jackson/OrcaObjectMapper.java index 7153120617..1e232cd1fe 100644 --- a/orca-core/src/main/java/com/netflix/spinnaker/orca/jackson/OrcaObjectMapper.java +++ b/orca-core/src/main/java/com/netflix/spinnaker/orca/jackson/OrcaObjectMapper.java @@ -29,6 +29,8 @@ public class OrcaObjectMapper { private OrcaObjectMapper() {} + private static final ObjectMapper INSTANCE = newInstance(); + public static ObjectMapper newInstance() { ObjectMapper instance = new ObjectMapper(); instance.registerModule(new Jdk8Module()); @@ -41,4 +43,15 @@ public static ObjectMapper newInstance() { instance.setSerializationInclusion(NON_NULL); return instance; } + + /** + * Return an ObjectMapper instance that can be reused. Do not change the configuration of this + * instance as it will be shared across the entire application, use {@link #newInstance()} + * instead. + * + * @return Reusable ObjectMapper instance + */ + public static ObjectMapper getInstance() { + return INSTANCE; + } } diff --git a/orca-core/src/main/java/com/netflix/spinnaker/orca/pipeline/model/Stage.java b/orca-core/src/main/java/com/netflix/spinnaker/orca/pipeline/model/Stage.java index 76bff4dff4..973ed6c8d6 100644 --- a/orca-core/src/main/java/com/netflix/spinnaker/orca/pipeline/model/Stage.java +++ b/orca-core/src/main/java/com/netflix/spinnaker/orca/pipeline/model/Stage.java @@ -436,7 +436,7 @@ public O mapTo(Class type) { return mapTo(null, type); } - @JsonIgnore private final transient ObjectMapper objectMapper = OrcaObjectMapper.newInstance(); + @JsonIgnore private final transient ObjectMapper objectMapper = OrcaObjectMapper.getInstance(); /** * Maps the stage's context to a typed object at a provided pointer. Uses detailExtractors; - private final ObjectMapper mapper = OrcaObjectMapper.newInstance(); + private final ObjectMapper mapper = OrcaObjectMapper.getInstance(); public BuildDetailExtractor() { this.detailExtractors = diff --git a/orca-core/src/main/java/com/netflix/spinnaker/orca/pipeline/util/ContextParameterProcessor.java b/orca-core/src/main/java/com/netflix/spinnaker/orca/pipeline/util/ContextParameterProcessor.java index 4981d51d94..9ca929f545 100644 --- a/orca-core/src/main/java/com/netflix/spinnaker/orca/pipeline/util/ContextParameterProcessor.java +++ b/orca-core/src/main/java/com/netflix/spinnaker/orca/pipeline/util/ContextParameterProcessor.java @@ -43,7 +43,7 @@ public class ContextParameterProcessor { private final Logger log = LoggerFactory.getLogger(getClass()); - private static final ObjectMapper mapper = OrcaObjectMapper.newInstance(); + private static final ObjectMapper mapper = OrcaObjectMapper.getInstance(); private ExpressionEvaluator expressionEvaluator; diff --git a/orca-integrations-cloudfoundry/src/main/java/com/netflix/spinnaker/orca/cf/pipeline/expressions/functions/ServiceKeyExpressionFunctionProvider.java b/orca-integrations-cloudfoundry/src/main/java/com/netflix/spinnaker/orca/cf/pipeline/expressions/functions/ServiceKeyExpressionFunctionProvider.java index 614a0a807e..3058adbce1 100644 --- a/orca-integrations-cloudfoundry/src/main/java/com/netflix/spinnaker/orca/cf/pipeline/expressions/functions/ServiceKeyExpressionFunctionProvider.java +++ b/orca-integrations-cloudfoundry/src/main/java/com/netflix/spinnaker/orca/cf/pipeline/expressions/functions/ServiceKeyExpressionFunctionProvider.java @@ -34,7 +34,7 @@ @Component public class ServiceKeyExpressionFunctionProvider implements ExpressionFunctionProvider { private static final String CREATE_SERVICE_KEY_STAGE_NAME = "createServiceKey"; - private static final ObjectMapper objectMapper = OrcaObjectMapper.newInstance(); + private static final ObjectMapper objectMapper = OrcaObjectMapper.getInstance(); @Nullable @Override diff --git a/orca-pipelinetemplate/src/main/java/com/netflix/spinnaker/orca/pipelinetemplate/PipelineTemplateService.java b/orca-pipelinetemplate/src/main/java/com/netflix/spinnaker/orca/pipelinetemplate/PipelineTemplateService.java index 22ccecd16e..5bb3177394 100644 --- a/orca-pipelinetemplate/src/main/java/com/netflix/spinnaker/orca/pipelinetemplate/PipelineTemplateService.java +++ b/orca-pipelinetemplate/src/main/java/com/netflix/spinnaker/orca/pipelinetemplate/PipelineTemplateService.java @@ -45,7 +45,7 @@ public class PipelineTemplateService { private final Renderer renderer; - private final ObjectMapper mapper = OrcaObjectMapper.newInstance(); + private final ObjectMapper mapper = OrcaObjectMapper.getInstance(); @Autowired public PipelineTemplateService( diff --git a/orca-queue/src/main/kotlin/com/netflix/spinnaker/orca/q/handler/ExpressionAware.kt b/orca-queue/src/main/kotlin/com/netflix/spinnaker/orca/q/handler/ExpressionAware.kt index 34798855ef..11aa096459 100644 --- a/orca-queue/src/main/kotlin/com/netflix/spinnaker/orca/q/handler/ExpressionAware.kt +++ b/orca-queue/src/main/kotlin/com/netflix/spinnaker/orca/q/handler/ExpressionAware.kt @@ -38,7 +38,7 @@ interface ExpressionAware { val contextParameterProcessor: ContextParameterProcessor companion object { - val mapper: ObjectMapper = OrcaObjectMapper.newInstance() + val mapper: ObjectMapper = OrcaObjectMapper.getInstance() } val log: Logger diff --git a/orca-redis/src/main/java/com/netflix/spinnaker/orca/pipeline/persistence/jedis/RedisExecutionRepository.java b/orca-redis/src/main/java/com/netflix/spinnaker/orca/pipeline/persistence/jedis/RedisExecutionRepository.java index 8cde0f7ed6..de6469ce0e 100644 --- a/orca-redis/src/main/java/com/netflix/spinnaker/orca/pipeline/persistence/jedis/RedisExecutionRepository.java +++ b/orca-redis/src/main/java/com/netflix/spinnaker/orca/pipeline/persistence/jedis/RedisExecutionRepository.java @@ -79,7 +79,7 @@ public class RedisExecutionRepository implements ExecutionRepository { private final RedisClientDelegate redisClientDelegate; private final Optional previousRedisClientDelegate; - private final ObjectMapper mapper = OrcaObjectMapper.newInstance(); + private final ObjectMapper mapper = OrcaObjectMapper.getInstance(); private final int chunkSize; private final Scheduler queryAllScheduler; private final Scheduler queryByAppScheduler; diff --git a/orca-web/src/main/groovy/com/netflix/spinnaker/orca/web/config/WebConfiguration.groovy b/orca-web/src/main/groovy/com/netflix/spinnaker/orca/web/config/WebConfiguration.groovy index 078bddc23b..0d065559cb 100644 --- a/orca-web/src/main/groovy/com/netflix/spinnaker/orca/web/config/WebConfiguration.groovy +++ b/orca-web/src/main/groovy/com/netflix/spinnaker/orca/web/config/WebConfiguration.groovy @@ -53,7 +53,7 @@ class WebConfiguration extends WebMvcConfigurerAdapter { } @Bean(name = "objectMapper", autowire = Autowire.BY_TYPE) ObjectMapper orcaObjectMapper() { - OrcaObjectMapper.newInstance() + OrcaObjectMapper.getInstance() } @Bean