Skip to content

Commit a50012b

Browse files
cacieprinsjennifer-shehaneAtofStryker
authored
perf: reduce the initial timeout for proxy connections, make configurable (#31283)
* use cached preflight response on subsequent cloud requests that require a preflight * introduce env var to skip initial proxy-api request * rename env to CYPRESS_INTERNAL prefix * change noproxy timeout default to 5 seconds * fix * rm internal * changelog * Update packages/server/test/unit/cloud/api/api_spec.js Co-authored-by: Jennifer Shehane <[email protected]> * Update packages/server/lib/cloud/api/index.ts Co-authored-by: Bill Glesias <[email protected]> * fix ts --------- Co-authored-by: Jennifer Shehane <[email protected]> Co-authored-by: Bill Glesias <[email protected]>
1 parent 2d8c224 commit a50012b

File tree

4 files changed

+124
-26
lines changed

4 files changed

+124
-26
lines changed

cli/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33

44
_Released 4/22/2025 (PENDING)_
55

6+
**Performance:**
7+
8+
- Reduced the initial timeout for the preflight API request to determine proxy conditions from sixty seconds to five, and made this timeout duration configurable with the `CYPRESS_INITIAL_PREFLIGHT_TIMEOUT` environment variable. Addresses [#28423](https://github.com/cypress-io/cypress/issues/28423). Addressed in [#31283](https://github.com/cypress-io/cypress/pull/31283).
9+
610
**Bugfixes:**
711

812
- The [`cy.press()`](http://on.cypress.io/api/press) command no longer errors when used in specs subsequent to the first spec in run mode. Fixes [#31466](https://github.com/cypress-io/cypress/issues/31466).

packages/server/lib/cloud/api/index.ts

Lines changed: 48 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,19 @@ const request = require('@cypress/request-promise')
66
const humanInterval = require('human-interval')
77

88
const RequestErrors = require('@cypress/request-promise/errors')
9-
const { agent } = require('@packages/network')
9+
1010
const pkg = require('@packages/root')
1111

1212
const machineId = require('../machine_id')
1313
const errors = require('../../errors')
14-
const { apiUrl, apiRoutes, makeRoutes } = require('../routes')
1514

1615
import Bluebird from 'bluebird'
16+
17+
import type { AfterSpecDurations } from '@packages/types'
18+
import { agent } from '@packages/network'
19+
import type { CombinedAgent } from '@packages/network/lib/agent'
20+
21+
import { apiUrl, apiRoutes, makeRoutes } from '../routes'
1722
import { getText } from '../../util/status_code'
1823
import * as enc from '../encryption'
1924
import getEnvInformationForProjectRoot from '../environment'
@@ -22,7 +27,7 @@ import type { OptionsWithUrl } from 'request-promise'
2227
import { fs } from '../../util/fs'
2328
import ProtocolManager from '../protocol'
2429
import type { ProjectBase } from '../../project-base'
25-
import type { AfterSpecDurations } from '@packages/types'
30+
2631
import { PUBLIC_KEY_VERSION } from '../constants'
2732

2833
// axios implementation disabled until proxy issues can be diagnosed/fixed
@@ -235,6 +240,16 @@ const isRetriableError = (err) => {
235240
(err.statusCode == null)
236241
}
237242

243+
function noProxyPreflightTimeout (): number {
244+
try {
245+
const timeoutFromEnv = Number(process.env.CYPRESS_INITIAL_PREFLIGHT_TIMEOUT)
246+
247+
return isNaN(timeoutFromEnv) ? 5000 : timeoutFromEnv
248+
} catch (e: unknown) {
249+
return 5000
250+
}
251+
}
252+
238253
export type CreateRunOptions = {
239254
projectRoot: string
240255
ci: {
@@ -307,8 +322,21 @@ type UpdateInstanceArtifactsOptions = {
307322
instanceId: string
308323
timeout?: number
309324
}
325+
interface DefaultPreflightResult {
326+
encrypt: true
327+
}
310328

311-
let preflightResult = {
329+
interface PreflightWarning {
330+
message: string
331+
}
332+
333+
interface CachedPreflightResult {
334+
encrypt: boolean
335+
apiUrl: string
336+
warnings?: PreflightWarning[]
337+
}
338+
339+
let preflightResult: DefaultPreflightResult | CachedPreflightResult = {
312340
encrypt: true,
313341
}
314342

@@ -598,28 +626,27 @@ export default {
598626

599627
sendPreflight (preflightInfo) {
600628
return retryWithBackoff(async (attemptIndex) => {
601-
const { timeout, projectRoot } = preflightInfo
602-
603-
preflightInfo = _.omit(preflightInfo, 'timeout', 'projectRoot')
629+
const { projectRoot, timeout, ...preflightRequestBody } = preflightInfo
604630

605631
const preflightBaseProxy = apiUrl.replace('api', 'api-proxy')
606632

607633
const envInformation = await getEnvInformationForProjectRoot(projectRoot, process.pid.toString())
608-
const makeReq = ({ baseUrl, agent }) => {
634+
635+
const makeReq = (baseUrl: string, agent: CombinedAgent | null, timeout: number) => {
609636
return rp.post({
610637
url: `${baseUrl}preflight`,
611638
body: {
612639
apiUrl,
613640
envUrl: envInformation.envUrl,
614641
dependencies: envInformation.dependencies,
615642
errors: envInformation.errors,
616-
...preflightInfo,
643+
...preflightRequestBody,
617644
},
618645
headers: {
619646
'x-route-version': '1',
620647
'x-cypress-request-attempt': attemptIndex,
621648
},
622-
timeout: timeout ?? SIXTY_SECONDS,
649+
timeout,
623650
json: true,
624651
encrypt: 'always',
625652
agent,
@@ -631,14 +658,19 @@ export default {
631658
}
632659

633660
const postReqs = async () => {
634-
return makeReq({ baseUrl: preflightBaseProxy, agent: null })
635-
.catch((err) => {
636-
if (err.statusCode === 412) {
637-
throw err
661+
const initialPreflightTimeout = noProxyPreflightTimeout()
662+
663+
if (initialPreflightTimeout >= 0) {
664+
try {
665+
return await makeReq(preflightBaseProxy, null, initialPreflightTimeout)
666+
} catch (err) {
667+
if (err.statusCode === 412) {
668+
throw err
669+
}
638670
}
671+
}
639672

640-
return makeReq({ baseUrl: apiUrl, agent })
641-
})
673+
return makeReq(apiUrl, agent, timeout)
642674
}
643675

644676
const result = await postReqs()

packages/server/lib/cloud/routes.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import _ from 'lodash'
22
import UrlParse from 'url-parse'
33

44
const app_config = require('../../config/app.json')
5-
const apiUrl = app_config[process.env.CYPRESS_CONFIG_ENV || process.env.CYPRESS_INTERNAL_ENV || 'development'].api_url
5+
6+
export const apiUrl = app_config[process.env.CYPRESS_CONFIG_ENV || process.env.CYPRESS_INTERNAL_ENV || 'development'].api_url
67

78
const CLOUD_ENDPOINTS = {
89
api: '',
@@ -40,7 +41,7 @@ const parseArgs = function (url, args: any[] = []) {
4041
return url
4142
}
4243

43-
const makeRoutes = (baseUrl: string, routes: typeof CLOUD_ENDPOINTS) => {
44+
const _makeRoutes = (baseUrl: string, routes: typeof CLOUD_ENDPOINTS) => {
4445
return _.reduce(routes, (memo, value, key) => {
4546
memo[key] = function (...args: any[]) {
4647
let url = new UrlParse(baseUrl, true)
@@ -60,10 +61,6 @@ const makeRoutes = (baseUrl: string, routes: typeof CLOUD_ENDPOINTS) => {
6061
}, {} as Record<keyof typeof CLOUD_ENDPOINTS, (...args: any[]) => string>)
6162
}
6263

63-
const apiRoutes = makeRoutes(apiUrl, CLOUD_ENDPOINTS)
64+
export const apiRoutes = _makeRoutes(apiUrl, CLOUD_ENDPOINTS)
6465

65-
module.exports = {
66-
apiUrl,
67-
apiRoutes,
68-
makeRoutes: (baseUrl) => makeRoutes(baseUrl, CLOUD_ENDPOINTS),
69-
}
66+
export const makeRoutes = (baseUrl) => _makeRoutes(baseUrl, CLOUD_ENDPOINTS)

packages/server/test/unit/cloud/api/api_spec.js

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,8 @@ describe('lib/cloud/api', () => {
245245
require('../../../../lib/cloud/encryption')
246246
}, module)
247247
}
248+
249+
prodApi.resetPreflightResult()
248250
})
249251

250252
it('POST /preflight to proxy. returns encryption', () => {
@@ -323,12 +325,75 @@ describe('lib/cloud/api', () => {
323325
})
324326
})
325327

326-
it('sets timeout to 60 seconds', () => {
328+
it('sets timeout to 5 seconds when no CYPRESS_INITIAL_PREFLIGHT_TIMEOUT env is set', () => {
327329
sinon.stub(api.rp, 'post').resolves({})
328330

329331
return api.sendPreflight({})
330332
.then(() => {
331-
expect(api.rp.post).to.be.calledWithMatch({ timeout: 60000 })
333+
expect(api.rp.post).to.be.calledWithMatch({ timeout: 5000 })
334+
})
335+
})
336+
337+
describe('when CYPRESS_INITIAL_PREFLIGHT_TIMEOUT env is set to a negative number', () => {
338+
const configuredTimeout = -1
339+
let prevEnv
340+
341+
beforeEach(() => {
342+
prevEnv = process.env.CYPRESS_INITIAL_PREFLIGHT_TIMEOUT
343+
process.env.CYPRESS_INITIAL_PREFLIGHT_TIMEOUT = configuredTimeout
344+
})
345+
346+
afterEach(() => {
347+
process.env.CYPRESS_INITIAL_PREFLIGHT_TIMEOUT = prevEnv
348+
})
349+
350+
it('skips the no-agent preflight request', () => {
351+
preflightNock(API_PROD_PROXY_BASEURL)
352+
.replyWithError('should not be called')
353+
354+
preflightNock(API_PROD_BASEURL)
355+
.reply(200, decryptReqBodyAndRespond({
356+
reqBody: {
357+
envUrl: 'https://some.server.com',
358+
dependencies: {},
359+
errors: [],
360+
apiUrl: 'https://api.cypress.io/',
361+
projectId: 'abc123',
362+
},
363+
resBody: {
364+
encrypt: true,
365+
apiUrl: `${API_PROD_BASEURL}/`,
366+
},
367+
}))
368+
369+
return prodApi.sendPreflight({ projectId: 'abc123' })
370+
.then((ret) => {
371+
expect(ret).to.deep.eq({ encrypt: true, apiUrl: `${API_PROD_BASEURL}/` })
372+
})
373+
})
374+
})
375+
376+
describe('when CYPRESS_INITIAL_PREFLIGHT_TIMEOUT env is set to a positive number', () => {
377+
const configuredTimeout = 10000
378+
let prevEnv
379+
380+
beforeEach(() => {
381+
prevEnv = process.env.CYPRESS_INITIAL_PREFLIGHT_TIMEOUT
382+
process.env.CYPRESS_INITIAL_PREFLIGHT_TIMEOUT = configuredTimeout
383+
})
384+
385+
afterEach(() => {
386+
process.env.CYPRESS_INITIAL_PREFLIGHT_TIMEOUT = prevEnv
387+
api.rp.post.restore()
388+
})
389+
390+
it('makes the initial request with the number set in the env', () => {
391+
sinon.stub(api.rp, 'post').resolves({})
392+
393+
return api.sendPreflight({})
394+
.then(() => {
395+
expect(api.rp.post).to.be.calledWithMatch({ timeout: configuredTimeout })
396+
})
332397
})
333398
})
334399

0 commit comments

Comments
 (0)