[web] increase timeout for downloads from clsi (#31840)

* [web] increase timeout for downloads from clsi

* [web] detach request timeout from streaming timeout

* [web] align timeout for clsi-cache downloads with clsi downloads

* [web] destroy stream when bailing out early from aborted requests

GitOrigin-RevId: cc4cad9d684214b2eee0f5fc43513e430ceb0977
This commit is contained in:
Jakob Ackermann
2026-02-27 08:00:43 +01:00
committed by Copybot
parent b6c38ef5d0
commit 624a351aac
2 changed files with 32 additions and 6 deletions

View File

@@ -53,7 +53,8 @@ async function _downloadFromCacheWithParams(
filename
) {
const userId = CompileController._getUserIdForCompile(req)
const signal = AbortSignal.timeout(60 * 1000)
const ac = new AbortController()
let timer = setTimeout(() => ac.abort(), 10_000)
let location, projectName
try {
;[{ location }, { name: projectName }] = await Promise.all([
@@ -62,11 +63,12 @@ async function _downloadFromCacheWithParams(
userId,
buildId,
filename,
signal
ac.signal
),
ProjectGetter.promises.getProject(projectId, { name: 1 }),
])
} catch (err) {
clearTimeout(timer)
if (err instanceof NotFoundError) {
// res.sendStatus() sends a description of the status as body.
// Using res.status().end() avoids sending that fake body.
@@ -76,11 +78,17 @@ async function _downloadFromCacheWithParams(
}
}
const { stream, response } = await fetchStreamWithResponse(location, {
signal,
})
let stream, response
try {
;({ stream, response } = await fetchStreamWithResponse(location, {
signal: ac.signal,
}))
} finally {
clearTimeout(timer)
}
if (req.destroyed) {
// The client has disconnected already, avoid trying to write into the broken connection.
stream.destroy(new Error('user aborted the request'))
return
}
@@ -93,6 +101,10 @@ async function _downloadFromCacheWithParams(
? `${CompileController._getSafeProjectName({ name: projectName })}.pdf`
: filename
)
// Downloads can take a while on a slow connection, increase timeouts to 10min
const TEN_MINUTES_IN_MS = 10 * 60 * 1000
res.setTimeout(TEN_MINUTES_IN_MS)
timer = setTimeout(() => ac.abort(), TEN_MINUTES_IN_MS)
try {
res.writeHead(response.status)
await pipeline(
@@ -132,6 +144,8 @@ async function _downloadFromCacheWithParams(
},
'CLSI-cache proxy error'
)
} finally {
clearTimeout(timer)
}
}

View File

@@ -598,10 +598,12 @@ const _CompileController = {
[0, 100, 1000, 2000, 5000, 10000, 15000, 20000, 30000, 45000, 60000]
)
Metrics.inc('proxy_to_clsi', 1, { path: action, status: 'start' })
const ac = new AbortController()
let timeout = setTimeout(() => ac.abort(), 10_000)
try {
const { stream, response } = await fetchStreamWithResponse(url.href, {
method: req.method,
signal: AbortSignal.timeout(60 * 1000),
signal: ac.signal,
headers: persistenceOptions.headers,
})
if (req.destroyed) {
@@ -610,6 +612,7 @@ const _CompileController = {
path: action,
status: 'req-aborted',
})
stream.destroy(new Error('user aborted the request'))
return
}
Metrics.inc('proxy_to_clsi', 1, {
@@ -622,6 +625,13 @@ const _CompileController = {
res.setHeader(key, response.headers.get(key))
}
}
// Downloads can take a while on a slow connection, increase timeouts to 10min
const TEN_MINUTES_IN_MS = 10 * 60 * 1000
res.setTimeout(TEN_MINUTES_IN_MS)
clearTimeout(timeout)
timeout = setTimeout(() => ac.abort(), TEN_MINUTES_IN_MS)
res.writeHead(response.status)
await pipeline(stream, res)
timer.labels.status = 'success'
@@ -679,6 +689,8 @@ const _CompileController = {
},
'CLSI proxy error'
)
} finally {
clearTimeout(timeout)
}
},