f01c7dc7ea48d00510033d7c9448b20bd9c68e19 braney Mon Aug 31 13:44:24 2026 -0700 hgTracks: keep the fast png deliveries in the timing sample, refs #38109 Two fixes from a review of the timing beacon. The first threw away a real measurement. A download whose responseEnd and responseStart fall in the same clock tick was treated the same as an image that never crossed the wire. Firefox rounds resource timing to a millisecond, so a small track image on a fast link lands there. Dropping those samples would leave only the slow connections in the record, which is the wrong population to pick a compression level from. The beacon now sends them and reports x=0. Take d as the denominator when x is zero. The second was the beacon image itself. An Image with no reference to it can be collected before the request goes out. It now lives in a variable that outlives the function. diff --git src/hg/js/hgTracks.js src/hg/js/hgTracks.js index 9dda73c521b..4b20c81b4b0 100644 --- src/hg/js/hgTracks.js +++ src/hg/js/hgTracks.js @@ -7909,61 +7909,72 @@ myVariants.showDialog(); }); } } if (typeof showMouseovers !== 'undefined' && showMouseovers) { convertTitleTagsToMouseovers(); } if (typeof pngTimingSampleRate !== 'undefined' && pngTimingSampleRate > 0) { reportPngTiming(pngTimingSampleRate); } }); +// hold the beacon image in a variable that outlives reportPngTiming. An +// Image with no reference to it can be collected before the request goes out. +var pngTimingBeacon; + function reportPngTiming(sampleRate) { /* Report how long the track image took to reach this reader, on one page * load in sampleRate. The browser keeps a timing record for every image it * loads, holding the bytes it took off the wire and the time it waited. We * cannot get that from our own logs: apache stops timing once the kernel has * the bytes. Bytes divided by time gives the reader's throughput, which is * what decides whether a lower png compression level helps them or hurts * them. The two numbers ride on the query string of a 43 byte image, so the * apache log line is the whole record and no process has to start. */ if (Math.random() * sampleRate >= 1) return; if (!window.performance || !window.performance.getEntriesByType) return; var sendTiming = function () { var entries = window.performance.getEntriesByType("resource"); for (var i = 0; i < entries.length; i++) { var entry = entries[i]; // the track image is ../trash/hgt/hgt_<host>_<user>_<hex>.png. The // guidelines and the side label images are named differently. if (entry.name.indexOf("/hgt/hgt_") < 0) continue; if (entry.name.indexOf(".png") < 0) continue; var bytes = entry.transferSize; var download = entry.responseEnd - entry.responseStart; - // an image answered from the browser cache has no transfer to time - if (!bytes || !download) + // no bytes means the browser never took the image off the wire, or + // it does not report the size. Either way there is nothing to time. + if (!bytes) return; + // a download of zero is a real delivery that finished inside one + // clock tick, not a missing measurement. Firefox rounds resource + // timing to a millisecond, so a small image on a fast link lands + // there. Report it and let the reader of the log fall back to d. + // Dropping it would leave only the slow connections in the sample. // duration covers the whole fetch, download only the bytes arriving - new Image().src = "../images/DOT.gif?hgtPng=1" + + pngTimingBeacon = new Image(); + pngTimingBeacon.src = "../images/DOT.gif?hgtPng=1" + "&ts=" + Math.round(bytes) + "&d=" + Math.round(entry.duration) + "&x=" + Math.round(download); return; } }; // the timing record only exists once the image has finished loading if (document.readyState === "complete") sendTiming(); else window.addEventListener("load", sendTiming); } function hgtWarnTiming(maxSeconds) {