-
Notifications
You must be signed in to change notification settings - Fork 3.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix cy.visit
slowness by removing Electron timers workaround
#4385
Conversation
This it.only "completes immediately on localhost", ->
_.times 100, ->
cy.visit('http://localhost:3500/dump-method') This same test with Python's SimpleHTTPServer takes waaaay too long (60+ seconds).
And in a regular browser:
And in a regular browser: |
I happened to notice that while cypress/packages/server/lib/request.coffee Lines 444 to 448 in 768381b
EDIT: I've narrowed it down, it seems like the issue only occurs when the server sends connection: close EDIT 2: Tried setting this hack, but didn't help anything: const net = require('net')
const connect = net.Socket.prototype.connect
net.Socket.prototype.connect = function (...args) {
const s = connect.call(this, ...args)
s.setNoDelay(true)
return s
} EDIT 3: Removing the new CombinedAgent does not fix this, leading me to think the issue could be in the request retries code. |
Noticed that a few factors appear to influence this slowdown:
*The visits with Conclusions:
EDIT: The following workaround fixes the issue with blocking on stderr/stdout writes: const _ = require('lodash')
const debug = require('debug')
const fs = require('fs')
const util = require('util')
process.stdout.write = function write (str) {
fs.write(1, str, _.noop)
}
process.stderr.write = function write (str) {
fs.write(2, str, _.noop)
}
debug.log = function log (...args) {
return process.stderr.write(`${util.format(...args)}\n`)
} I am not sure if this will have detrimental effects like out-of-order output, have to do some testing. Not sure if this complexity is worth the marginal benefit - users usually don't have tons of output spitting out during a run. All tests pass with the async stdio stuff: https://circleci.com/workflow-run/9150993a-cecc-450b-8229-583035a45cb5 Which makes sense, I don't see how interleaving could be possible since .writes will be queued in order in the Node event loop. |
Notes on trying to figure out what caused this issue:
Wrote a small script that creates 10000 setTimeouts and then measures the extremes of how different the actual execution time is from the specified execution time and tested it against Node.js and Electron and our timers fix. const _ = require('lodash')
const percentile = require('percentile')
// require('./timers/parent').fix()
const N = 10000
let differences = []
_.times(N, () => {
const ms = Math.random() * 60 * 1000 // between 0 and 1 minute
const started = new Date
setTimeout(() => {
const elapsed = new Date - started
const difference = elapsed - ms
differences.push(difference)
//console.log(`Expected: ${ms}ms\tActual: ${elapsed}ms\tDifference: ${difference}ms`)
if (differences.length === N) {
printSummary()
}
}, ms)
})
function printSummary () {
console.log(`Node version: ${process.versions.node}\tElectron version: ${process.versions.electron}`)
;[10, 20, 30, 40, 50, 60, 70, 80, 90, 95, 98, 99, 99.5, 99.7, 100].forEach((p) => {
const q = percentile(p, differences)
console.log(`${p}%\t of setTimeout calls finished with less than ${q}ms\t of difference`)
})
} ResultsWithout timer fixDetailsNode
Electron
|
cy.visit
slowness by removing Electron timers workaround; making stdio operations asynchronous
This reverts commit 93f15fc.
This reverts commit 9c8337e.
cy.visit
slowness by removing Electron timers workaround; making stdio operations asynchronouscy.visit
slowness by removing Electron timers workaround
So do we still want to remove the |
@brian-mann It's a hop-by-hop header, so I think the proxy should remove it. The browser's connection to the proxy is still keep-alive - check out the Connection IDs in the network log I produced using this branch: |
It looks like we're only stripping it on the incoming response from the upstream server - before handing it back to the browser. Are we still sending it when we make the initial proxied request to the upstream server from the cypress proxy? Does the http agent modify the http header and add it? NOTE: i'm not referring to the TCP keepalive mechanism, just the actual HTTP connection header. Can't remember if we have tests around this or not. |
Yeah, this is still there: cypress/packages/server/lib/request.coffee Lines 444 to 448 in 8e63476
If I remove it, then a new socket is opened for each request (net profiler output):
With it kept in, there is only one connection established for these requests. Looks like it's still good to have. |
Fixes #4313
Connection
header through proxy to the browser -Connection
is a hop by hop headerMake calls to stdout.write and stderr.write asynchronous to prevent slowdown when TTY is unresponsive