Skip to content

Commit 5392f1b

Browse files
authored
AN-532 Finite DRS timeout (#7736)
1 parent 652dd7c commit 5392f1b

File tree

5 files changed

+27
-1
lines changed

5 files changed

+27
-1
lines changed

centaur/src/main/scala/centaur/test/CentaurTestException.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package centaur.test
33
import centaur.test.workflow.Workflow
44
import cromwell.api.model.{SubmittedWorkflow, WorkflowMetadata}
55

6+
import scala.util.control.NoStackTrace
7+
68
/**
79
* An exception with information about a centaur test failure.
810
*
@@ -18,6 +20,7 @@ case class CentaurTestException private (message: String,
1820
metadataJsonOption: Option[String],
1921
causeOption: Option[Exception]
2022
) extends RuntimeException(message, causeOption.orNull)
23+
with NoStackTrace
2124

2225
object CentaurTestException {
2326

cloud-nio/cloud-nio-impl-drs/src/main/scala/cloud/nio/impl/drs/DrsConfig.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import scala.concurrent.duration._
77
import scala.language.postfixOps
88

99
final case class DrsConfig(drsResolverUrl: String,
10+
requestTimeout: FiniteDuration,
1011
numRetries: Int,
1112
waitInitial: FiniteDuration,
1213
waitMaximum: FiniteDuration,
@@ -16,13 +17,15 @@ final case class DrsConfig(drsResolverUrl: String,
1617

1718
object DrsConfig {
1819
// If you update these values also update Filesystems.md!
20+
private val DefaultRequestTimeout = 60 seconds
1921
private val DefaultNumRetries = 3
2022
private val DefaultWaitInitial = 30 seconds
2123
private val DefaultWaitMaximum = 60 seconds
2224
private val DefaultWaitMultiplier = 1.25d
2325
private val DefaultWaitRandomizationFactor = 0.1
2426

2527
private val EnvDrsResolverUrl = "DRS_RESOLVER_URL"
28+
private val EnvDrsResolverRequestTimeout = "DRS_RESOLVER_REQUEST_TIMEOUT"
2629
private val EnvDrsResolverNumRetries = "DRS_RESOLVER_NUM_RETRIES"
2730
private val EnvDrsResolverWaitInitialSeconds = "DRS_RESOLVER_WAIT_INITIAL_SECONDS"
2831
private val EnvDrsResolverWaitMaximumSeconds = "DRS_RESOLVER_WAIT_MAXIMUM_SECONDS"
@@ -32,6 +35,7 @@ object DrsConfig {
3235
def fromConfig(drsResolverConfig: Config): DrsConfig =
3336
DrsConfig(
3437
drsResolverUrl = drsResolverConfig.getString("url"),
38+
requestTimeout = drsResolverConfig.getOrElse("request-timeout", DefaultRequestTimeout),
3539
numRetries = drsResolverConfig.getOrElse("num-retries", DefaultNumRetries),
3640
waitInitial = drsResolverConfig.getOrElse("wait-initial", DefaultWaitInitial),
3741
waitMaximum = drsResolverConfig.getOrElse("wait-maximum", DefaultWaitMaximum),
@@ -42,6 +46,7 @@ object DrsConfig {
4246
def fromEnv(env: Map[String, String]): DrsConfig =
4347
DrsConfig(
4448
drsResolverUrl = env(EnvDrsResolverUrl),
49+
requestTimeout = env.get(EnvDrsResolverRequestTimeout).map(_.toLong.seconds).getOrElse(DefaultRequestTimeout),
4550
numRetries = env.get(EnvDrsResolverNumRetries).map(_.toInt).getOrElse(DefaultNumRetries),
4651
waitInitial = env.get(EnvDrsResolverWaitInitialSeconds).map(_.toLong.seconds).getOrElse(DefaultWaitInitial),
4752
waitMaximum = env.get(EnvDrsResolverWaitMaximumSeconds).map(_.toLong.seconds).getOrElse(DefaultWaitMaximum),
@@ -53,6 +58,7 @@ object DrsConfig {
5358
def toEnv(drsConfig: DrsConfig): Map[String, String] =
5459
Map(
5560
EnvDrsResolverUrl -> drsConfig.drsResolverUrl,
61+
EnvDrsResolverRequestTimeout -> s"${drsConfig.requestTimeout.toSeconds}",
5662
EnvDrsResolverNumRetries -> s"${drsConfig.numRetries}",
5763
EnvDrsResolverWaitInitialSeconds -> s"${drsConfig.waitInitial.toSeconds}",
5864
EnvDrsResolverWaitMaximumSeconds -> s"${drsConfig.waitMaximum.toSeconds}",

cloud-nio/cloud-nio-impl-drs/src/main/scala/cloud/nio/impl/drs/DrsPathResolver.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import io.circe.parser.decode
1414
import io.circe.syntax._
1515
import mouse.boolean._
1616
import org.apache.commons.lang3.exception.ExceptionUtils
17+
import org.apache.http.client.config.RequestConfig
1718
import org.apache.http.client.methods.{HttpGet, HttpPost}
1819
import org.apache.http.entity.{ContentType, StringEntity}
1920
import org.apache.http.impl.client.HttpClientBuilder
@@ -61,6 +62,7 @@ class DrsPathResolver(drsConfig: DrsConfig, drsCredentials: DrsCredentials) {
6162
postRequest.setEntity(new StringEntity(requestJson, ContentType.APPLICATION_JSON))
6263
postRequest.setHeader("Authorization", s"Bearer $token")
6364
postRequest.setHeader("X-App-ID", "cromwell_drs_localizer")
65+
postRequest.setConfig(timeoutConfig)
6466
postRequest
6567
}
6668
case Invalid(errors) =>
@@ -69,6 +71,17 @@ class DrsPathResolver(drsConfig: DrsConfig, drsCredentials: DrsCredentials) {
6971
Resource.eval(io)
7072
}
7173

74+
private lazy val timeoutConfig: RequestConfig = {
75+
// Infinite timeout bad, can exhaust Akka threads. [AN-532]
76+
val requestTimeoutMillis = drsConfig.requestTimeout.toMillis.toInt
77+
RequestConfig
78+
.custom()
79+
.setConnectionRequestTimeout(requestTimeoutMillis / 4) // Obtain connection from the pool
80+
.setConnectTimeout(requestTimeoutMillis) // Max time to first byte from server
81+
.setSocketTimeout(requestTimeoutMillis / 4) // Max time between subsequent bytes
82+
.build()
83+
}
84+
7285
private def httpResponseToDrsResolverResponse(
7386
drsPathForDebugging: String
7487
)(httpResponse: HttpResponse): IO[DrsResolverResponse] = {

cloud-nio/cloud-nio-impl-drs/src/test/scala/cloud/nio/impl/drs/MockDrsPaths.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
package cloud.nio.impl.drs
22

33
import scala.concurrent.duration._
4+
import scala.language.postfixOps
45

56
object MockDrsPaths {
67
val drsResolverUrl = "http://mock.drshub"
78

8-
val mockDrsConfig: DrsConfig = DrsConfig(MockDrsPaths.drsResolverUrl, 1, 1.seconds, 1.seconds, 1d, 0d)
9+
val mockDrsConfig: DrsConfig = DrsConfig(MockDrsPaths.drsResolverUrl, 5 seconds, 1, 1 seconds, 1 seconds, 1d, 0d)
910

1011
val mockToken = "mock.token"
1112

docs/filesystems/Filesystems.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ filesystems {
2020
config {
2121
resolver {
2222
url = https://drshub-url-here"
23+
# How long to wait for the server to start responding
24+
# Default is the informal standard around the DRS ecosystem
25+
request-timeout = 60 seconds
2326
# The number of times to retry failures connecting or HTTP 429 or HTTP 5XX responses, default 3.
2427
num-retries = 3
2528
# How long to wait between retrying HTTP 429 or HTTP 5XX responses, default 10 seconds.

0 commit comments

Comments
 (0)