Skip to content

Commit 0f5d47d

Browse files
committed
perf: reuse singleton ObjectMapper for Redis pub/sub serialization
Constructing a new ObjectMapper on every publish is expensive and was on the websocket and batch-job hot paths. Inject the shared singleton instead, matching how RedisPubSubReceiver already consumes it.
1 parent e7b7a29 commit 0f5d47d

6 files changed

Lines changed: 18 additions & 7 deletions

File tree

backend/api/src/main/kotlin/io/tolgee/websocket/RedisWebsocketEventPublisher.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
package io.tolgee.websocket
22

33
import org.springframework.data.redis.core.StringRedisTemplate
4-
import tools.jackson.module.kotlin.jacksonObjectMapper
4+
import tools.jackson.databind.ObjectMapper
55

66
class RedisWebsocketEventPublisher(
77
private val redisTemplate: StringRedisTemplate,
8+
private val objectMapper: ObjectMapper,
89
) : WebsocketEventPublisher {
910
override operator fun invoke(
1011
destination: String,
1112
message: WebsocketEvent,
1213
) {
13-
val messageString = jacksonObjectMapper().writeValueAsString(RedisWebsocketEventWrapper(destination, message))
14+
val messageString = objectMapper.writeValueAsString(RedisWebsocketEventWrapper(destination, message))
1415
redisTemplate.convertAndSend(
1516
"websocket",
1617
messageString,

backend/api/src/main/kotlin/io/tolgee/websocket/WebsocketPublisherConfiguration.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import org.springframework.context.annotation.Bean
66
import org.springframework.context.annotation.Configuration
77
import org.springframework.data.redis.core.StringRedisTemplate
88
import org.springframework.messaging.simp.SimpMessagingTemplate
9+
import tools.jackson.databind.ObjectMapper
910

1011
@Configuration
1112
class WebsocketPublisherConfiguration(
@@ -15,7 +16,10 @@ class WebsocketPublisherConfiguration(
1516
@Bean
1617
fun websocketEventPublisher(): WebsocketEventPublisher {
1718
if (websocketProperties.useRedis) {
18-
return RedisWebsocketEventPublisher(applicationContext.getBean(StringRedisTemplate::class.java))
19+
return RedisWebsocketEventPublisher(
20+
applicationContext.getBean(StringRedisTemplate::class.java),
21+
applicationContext.getBean(ObjectMapper::class.java),
22+
)
1923
}
2024
return SimpleWebsocketEventPublisher(applicationContext.getBean(SimpMessagingTemplate::class.java))
2125
}

backend/data/src/main/kotlin/io/tolgee/batch/BatchJobCancellationManager.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import org.springframework.data.redis.core.StringRedisTemplate
2020
import org.springframework.stereotype.Component
2121
import org.springframework.transaction.PlatformTransactionManager
2222
import org.springframework.transaction.annotation.Transactional
23-
import tools.jackson.module.kotlin.jacksonObjectMapper
23+
import tools.jackson.databind.ObjectMapper
2424

2525
@Component
2626
class BatchJobCancellationManager(
@@ -36,6 +36,7 @@ class BatchJobCancellationManager(
3636
private val batchJobChunkExecutionQueue: BatchJobChunkExecutionQueue,
3737
private val concurrentExecutionLauncher: BatchJobConcurrentLauncher,
3838
private val batchProperties: io.tolgee.configuration.tolgee.BatchProperties,
39+
private val objectMapper: ObjectMapper,
3940
) : Logging {
4041
@Transactional
4142
fun cancel(id: Long) {
@@ -63,7 +64,7 @@ class BatchJobCancellationManager(
6364
if (usingRedisProvider.areWeUsingRedis) {
6465
redisTemplate.convertAndSend(
6566
RedisPubSubReceiverConfiguration.JOB_CANCEL_TOPIC,
66-
jacksonObjectMapper().writeValueAsString(id),
67+
objectMapper.writeValueAsString(id),
6768
)
6869
}
6970
cancelLocalJob(id)

backend/data/src/main/kotlin/io/tolgee/batch/BatchJobChunkExecutionQueue.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import org.springframework.data.redis.core.StringRedisTemplate
2323
import org.springframework.scheduling.annotation.Scheduled
2424
import org.springframework.stereotype.Component
2525
import org.springframework.transaction.annotation.Transactional
26-
import tools.jackson.module.kotlin.jacksonObjectMapper
26+
import tools.jackson.databind.ObjectMapper
2727
import java.util.concurrent.ConcurrentHashMap
2828
import java.util.concurrent.ConcurrentLinkedDeque
2929
import java.util.concurrent.atomic.AtomicInteger
@@ -35,6 +35,7 @@ class BatchJobChunkExecutionQueue(
3535
@Lazy
3636
private val redisTemplate: StringRedisTemplate,
3737
private val metrics: Metrics,
38+
private val objectMapper: ObjectMapper,
3839
) : Logging,
3940
InitializingBean {
4041
companion object {
@@ -222,7 +223,7 @@ class BatchJobChunkExecutionQueue(
222223
val event = JobQueueItemsEvent(batch, QueueEventType.ADD)
223224
redisTemplate.convertAndSend(
224225
RedisPubSubReceiverConfiguration.JOB_QUEUE_TOPIC,
225-
jacksonObjectMapper().writeValueAsString(event),
226+
objectMapper.writeValueAsString(event),
226227
)
227228
}
228229
return

backend/data/src/test/kotlin/io/tolgee/batch/BatchJobChunkExecutionQueuePerformanceTest.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import org.junit.jupiter.api.Timeout.ThreadMode.SEPARATE_THREAD
1414
import org.mockito.kotlin.mock
1515
import org.slf4j.LoggerFactory
1616
import org.springframework.data.redis.core.StringRedisTemplate
17+
import tools.jackson.module.kotlin.jacksonObjectMapper
1718
import java.util.concurrent.TimeUnit
1819
import kotlin.time.measureTime
1920

@@ -39,6 +40,7 @@ class BatchJobChunkExecutionQueuePerformanceTest {
3940
usingRedisProvider = mock<UsingRedisProvider>(),
4041
redisTemplate = mock<StringRedisTemplate>(),
4142
metrics = Metrics(SimpleMeterRegistry()),
43+
objectMapper = jacksonObjectMapper(),
4244
)
4345
// The internal structures live in the companion object (static), clear between tests
4446
executionQueue.clear()

backend/data/src/test/kotlin/io/tolgee/batch/BatchJobChunkExecutionQueueTest.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import org.junit.jupiter.api.BeforeEach
1313
import org.junit.jupiter.api.Test
1414
import org.mockito.kotlin.mock
1515
import org.springframework.data.redis.core.StringRedisTemplate
16+
import tools.jackson.module.kotlin.jacksonObjectMapper
1617
import java.util.concurrent.ConcurrentHashMap
1718
import java.util.concurrent.CountDownLatch
1819
import java.util.concurrent.Executors
@@ -31,6 +32,7 @@ class BatchJobChunkExecutionQueueTest {
3132
usingRedisProvider = mock<UsingRedisProvider>(),
3233
redisTemplate = mock<StringRedisTemplate>(),
3334
metrics = Metrics(SimpleMeterRegistry()),
35+
objectMapper = jacksonObjectMapper(),
3436
)
3537
queue.clear()
3638
}

0 commit comments

Comments
 (0)