Skip to content

Commit 84e0800

Browse files
authored
Add thread pool settings (#308)
* Add thread pool settings * Scalafmt
1 parent 8b79720 commit 84e0800

File tree

9 files changed

+125
-9
lines changed

9 files changed

+125
-9
lines changed

doc/20-configuration.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,18 @@ usually does not apply. However, it is expected the cache should be fast respond
227227
and thus **by default the timeout is set to 500 millis** to avoid unnecessary delays.
228228
This timeout is optional and can be disabled.
229229

230+
## ThreadPool
231+
232+
These are ResourceClient settings passed to Lettuce, a Java Redis client library. For more information see
233+
234+
https://redis.github.io/lettuce/advanced-usage/
235+
236+
```hocon
237+
play.cache.redis {
238+
io-thread-pool-size: 8 // default 8, min 3
239+
computation-thread-pool-size: 8 // default 8, min 3
240+
}
241+
```
230242
## Recovery policy
231243

232244
The intention of cache is usually to optimize the application behavior, not to provide any business logic.

src/main/scala/play/api/cache/redis/configuration/RedisSettings.scala

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ trait RedisSettings {
2626

2727
/** instance prefix */
2828
def prefix: Option[String]
29+
30+
/** threadpool */
31+
def threadPool: RedisThreadPools
2932
// $COVERAGE-OFF$
3033
/** trait-specific equals */
3134
override def equals(obj: scala.Any): Boolean = equalsAsSettings(obj)
@@ -49,6 +52,7 @@ object RedisSettings extends ConfigLoader[RedisSettings] {
4952
timeout = loadTimeouts(config, path)(RedisTimeouts.requiredDefault),
5053
source = loadSource(config, path).get,
5154
prefix = loadPrefix(config, path),
55+
threadPool = loadThreadPool(config, path)(RedisThreadPools.requiredDefault),
5256
)
5357

5458
def withFallback(fallback: RedisSettings): ConfigLoader[RedisSettings] =
@@ -60,19 +64,21 @@ object RedisSettings extends ConfigLoader[RedisSettings] {
6064
timeout = loadTimeouts(config, path)(fallback.timeout),
6165
source = loadSource(config, path) getOrElse fallback.source,
6266
prefix = loadPrefix(config, path) orElse fallback.prefix,
67+
threadPool = loadThreadPool(config, path)(fallback.threadPool),
6368
)
6469

65-
def apply(dispatcher: String, invocationPolicy: String, timeout: RedisTimeouts, recovery: String, source: String, prefix: Option[String] = None): RedisSettings =
66-
create(dispatcher, invocationPolicy, prefix, timeout, recovery, source)
70+
def apply(dispatcher: String, invocationPolicy: String, timeout: RedisTimeouts, recovery: String, source: String, prefix: Option[String] = None, threadPool: RedisThreadPools): RedisSettings =
71+
create(dispatcher, invocationPolicy, prefix, timeout, recovery, source, threadPool)
6772

6873
@inline
69-
private def create(_dispatcher: String, _invocation: String, _prefix: Option[String], _timeout: RedisTimeouts, _recovery: String, _source: String) = new RedisSettings {
74+
private def create(_dispatcher: String, _invocation: String, _prefix: Option[String], _timeout: RedisTimeouts, _recovery: String, _source: String, _threadpool: RedisThreadPools) = new RedisSettings {
7075
override val invocationContext: String = _dispatcher
7176
override val invocationPolicy: String = _invocation
7277
override val prefix: Option[String] = _prefix
7378
override val recovery: String = _recovery
7479
override val timeout: RedisTimeouts = _timeout
7580
override val source: String = _source
81+
override val threadPool: RedisThreadPools = _threadpool
7682
}
7783

7884
private def loadInvocationContext(config: Config, path: String): Option[String] =
@@ -93,6 +99,9 @@ object RedisSettings extends ConfigLoader[RedisSettings] {
9399
private def loadTimeouts(config: Config, path: String)(defaults: RedisTimeouts): RedisTimeouts =
94100
RedisTimeouts.load(config, path)(defaults)
95101

102+
private def loadThreadPool(config: Config, path: String)(defaults: RedisThreadPools): RedisThreadPools =
103+
RedisThreadPools.load(config, path)(defaults)
104+
96105
}
97106

98107
/** A helper trait delegating properties into the inner settings object */
@@ -104,4 +113,5 @@ trait RedisDelegatingSettings extends RedisSettings {
104113
override def recovery: String = settings.recovery
105114
override def invocationContext: String = settings.invocationContext
106115
override def invocationPolicy: String = settings.invocationPolicy
116+
override def threadPool: RedisThreadPools = settings.threadPool
107117
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package play.api.cache.redis.configuration
2+
3+
import com.typesafe.config.Config
4+
import play.api.cache.redis._
5+
6+
trait RedisThreadPools {
7+
def ioSize: Int
8+
def computationSize: Int
9+
}
10+
11+
final case class RedisThreadPoolsImpl(
12+
ioSize: Int,
13+
computationSize: Int,
14+
) extends RedisThreadPools {
15+
16+
// $COVERAGE-OFF$
17+
override def equals(obj: scala.Any): Boolean = obj match {
18+
case that: RedisThreadPools => this.ioSize === that.ioSize && this.computationSize === that.computationSize
19+
case _ => false
20+
}
21+
// $COVERAGE-ON$
22+
23+
}
24+
25+
object RedisThreadPools {
26+
import RedisConfigLoader._
27+
28+
def requiredDefault: RedisThreadPools = new RedisThreadPools {
29+
override def ioSize: Int = 8
30+
override def computationSize: Int = 8
31+
}
32+
33+
@inline
34+
def apply(ioSize: Int, computationSize: Int): RedisThreadPools =
35+
RedisThreadPoolsImpl(ioSize, computationSize)
36+
37+
def load(config: Config, path: String)(default: RedisThreadPools): RedisThreadPools = RedisThreadPools(
38+
ioSize = loadIoSize(config, path) getOrElse default.ioSize,
39+
computationSize = loadComputationSize(config, path) getOrElse default.computationSize,
40+
)
41+
42+
private def loadIoSize(config: Config, path: String): Option[Int] =
43+
config.getOption(path / "io-thread-pool-size", _.getInt)
44+
45+
private def loadComputationSize(config: Config, path: String): Option[Int] =
46+
config.getOption(path / "computation-thread-pool-size", _.getInt)
47+
48+
}

src/main/scala/play/api/cache/redis/connector/RedisCommands.scala

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,12 @@ abstract private[connector] class AbstractRedisCommands(
5050

5151
protected def connectionString: String
5252

53-
protected lazy val resources: ClientResources = RedisClientFactory.newClientResources()
53+
protected def configuration: RedisSettings
54+
55+
protected lazy val resources: ClientResources = RedisClientFactory.newClientResources(
56+
ioThreadPoolSize = configuration.threadPool.ioSize,
57+
computationThreadPoolSize = configuration.threadPool.computationSize,
58+
)
5459

5560
protected def client: AbstractRedisClient
5661

@@ -102,7 +107,7 @@ abstract private[connector] class AbstractRedisCommands(
102107
* configures clusters
103108
*/
104109
private[connector] class RedisCommandsStandalone(
105-
configuration: RedisStandalone,
110+
override val configuration: RedisStandalone,
106111
)(implicit
107112
executionContext: ExecutionContext,
108113
lifecycle: ApplicationLifecycle,
@@ -147,7 +152,7 @@ private[connector] class RedisCommandsStandalone(
147152
* configures clusters
148153
*/
149154
private[connector] class RedisCommandsCluster(
150-
configuration: RedisCluster,
155+
override val configuration: RedisCluster,
151156
)(implicit
152157
lifecycle: ApplicationLifecycle,
153158
executionContext: ExecutionContext,
@@ -199,7 +204,7 @@ private[connector] class RedisCommandsCluster(
199204
* configures sentinels
200205
*/
201206
private[connector] class RedisCommandsSentinel(
202-
configuration: RedisSentinel,
207+
override val configuration: RedisSentinel,
203208
)(implicit
204209
lifecycle: ApplicationLifecycle,
205210
executionContext: ExecutionContext,
@@ -248,7 +253,7 @@ private[connector] class RedisCommandsSentinel(
248253
*/
249254
//noinspection DuplicatedCode
250255
private[connector] class RedisCommandsMasterSlaves(
251-
configuration: RedisMasterSlaves,
256+
override val configuration: RedisMasterSlaves,
252257
)(implicit
253258
lifecycle: ApplicationLifecycle,
254259
executionContext: ExecutionContext,

src/test/scala/play/api/cache/redis/RedisCacheModuleSpec.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package play.api.cache.redis
22

3-
import play.api.cache.redis.configuration.{RedisHost, RedisSettings, RedisStandalone, RedisTimeouts}
3+
import play.api.cache.redis.configuration.{RedisHost, RedisSettings, RedisStandalone, RedisThreadPools, RedisTimeouts}
44
import play.api.cache.redis.test._
55
import play.api.inject._
66
import play.api.inject.guice.GuiceApplicationBuilder
@@ -138,6 +138,7 @@ class RedisCacheModuleSpec extends IntegrationSpec with RedisStandaloneContainer
138138
recovery = "log-and-default",
139139
source = "my-instance",
140140
prefix = None,
141+
threadPool = RedisThreadPools(ioSize = 1, computationSize = 1),
141142
),
142143
)
143144

src/test/scala/play/api/cache/redis/configuration/RedisInstanceManagerSpec.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ class RedisInstanceManagerSpec extends UnitSpec with ImplicitOptionMaterializati
5858
recovery = "log-and-fail",
5959
source = "standalone",
6060
prefix = "redis.",
61+
threadPool = RedisThreadPools(1, 1),
6162
)
6263

6364
manager mustEqual RedisInstanceManagerTest(defaultCacheName)(
@@ -107,6 +108,7 @@ class RedisInstanceManagerSpec extends UnitSpec with ImplicitOptionMaterializati
107108
recovery = "log-and-fail",
108109
source = "standalone",
109110
prefix = "redis.",
111+
threadPool = RedisThreadPools(1, 1),
110112
)
111113

112114
private val defaultCache: RedisInstanceProvider = RedisStandalone(defaultCacheName, RedisHost(localhost, defaultPort, database = 1), otherSettings)
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package play.api.cache.redis.configuration
2+
3+
import play.api.cache.redis.test.{Helpers, ImplicitOptionMaterialization, UnitSpec}
4+
5+
class RedisThreadPoolsSpec extends UnitSpec with ImplicitOptionMaterialization {
6+
7+
private def orDefault = RedisThreadPools(8, 8)
8+
9+
"load defined thread pools" in {
10+
val configuration = Helpers.configuration.fromHocon {
11+
"""
12+
|play.cache.redis {
13+
|
14+
| io-thread-pool-size: 5
15+
| computation-thread-pool-size: 7
16+
|}
17+
""".stripMargin
18+
}
19+
val expected = RedisThreadPools(5, 7)
20+
val actual = RedisThreadPools.load(configuration.underlying, "play.cache.redis")(RedisThreadPools.requiredDefault)
21+
actual mustEqual expected
22+
}
23+
24+
"load with default thread pools" in {
25+
val configuration = Helpers.configuration.fromHocon {
26+
"""
27+
|play.cache.redis {
28+
|}
29+
""".stripMargin
30+
}
31+
val expected = RedisThreadPools(8, 8)
32+
val actual = RedisThreadPools.load(configuration.underlying, "play.cache.redis")(orDefault)
33+
actual mustEqual expected
34+
}
35+
36+
}

src/test/scala/play/api/cache/redis/test/BaseSpec.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ trait DefaultValues {
2828
timeout = RedisTimeouts(1.second, None, Some(500.millis)),
2929
recovery = "log-and-default",
3030
source = "standalone",
31+
threadPool = RedisThreadPools(1, 1),
3132
)
3233

3334
final protected val cacheKey: String = "cache-key"

src/test/scala/play/api/cache/redis/test/RedisSettingsTest.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ final case class RedisSettingsTest(
99
recovery: String,
1010
source: String,
1111
prefix: Option[String] = None,
12+
threadPool: RedisThreadPools,
1213
) extends RedisSettings

0 commit comments

Comments
 (0)