hf-fast / src /lib /index.ts
hlarcher's picture
hlarcher HF Staff
feat: enhance analytics data handling with early event sending (after 5s)
cd5701b unverified
raw
history blame
5.13 kB
// place files you want to import through the `$lib` alias in this folder.
import { majorAirportIATAs } from '$lib/icao';
interface BandwidthCallback {
(
elapsedMs: number,
loadedBytes: number,
totalBytes: number,
bytesPerSecond: number,
done: boolean
): boolean;
}
export async function bandwidthTest(
onProgress: BandwidthCallback,
onLatency: (latency: number) => void,
onServerLocation: (location: string) => void
) {
// performance.setResourceTimingBufferSize(100);
// performance.clearResourceTimings();
const url = 'https://cdn-test-cloudfront.hf.co/5gb.safetensors';
// const url = 'https://cdn-test-cloudfront.hf.co/15mb.json';
// issue HEAD requests to estimate latency (round trip time), and average
let latencySum = 0;
const numLatencyTests = 5;
for (let i = 0; i < numLatencyTests; i++) {
const startTime = performance.now();
const response = await fetch(url, { method: 'HEAD' });
if (!response.ok) {
throw new Error(`Network response was not ok: ${response.status}`);
}
const latency = performance.now() - startTime;
latencySum += latency;
}
onLatency(latencySum / numLatencyTests);
const startTime = performance.now();
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Network response was not ok: ${response.status}`);
}
// setTimeout(() => {
// const entries = performance.getEntriesByType('resource');
// const resourceEntry = entries.find((e) => e.name === url);
// if (!resourceEntry) {
// return
// }
// console.log(resourceEntry);
// const { requestStart, responseStart } = resourceEntry;
// const latency = responseStart - requestStart;
// onLatency(latency);
// }, 2000);
// extract content-length
const contentLengthHeader = response.headers.get('content-length');
const totalBytes = contentLengthHeader ? parseInt(contentLengthHeader, 10) : 1e99;
// extract pop location
let cdnPop = response.headers.get('x-amz-cf-pop');
if (cdnPop !== null) {
cdnPop = cdnPop.toUpperCase().slice(0, 3);
// try to map to IATA
if (cdnPop in majorAirportIATAs) {
cdnPop = majorAirportIATAs[cdnPop].city + ', ' + majorAirportIATAs[cdnPop].country;
} else {
cdnPop = 'Unknown';
}
} else {
cdnPop = 'Unknown';
}
onServerLocation(cdnPop);
const reader = response.body.getReader();
let loadedBytes = 0;
let lastTimestamp = performance.now();
let lastLoaded = 0;
const REPORT_INTERVAL_MS = 500;
onProgress(0, loadedBytes, totalBytes, 0, false);
let bytesPerSecond = 0;
while (true) {
const { done, value } = await reader.read();
if (done) {
// stream is finished
const elapsedMs = performance.now() - startTime;
onProgress(elapsedMs, loadedBytes, totalBytes, bytesPerSecond, true);
// send analytics data
await sendAnalyticsData(bytesPerSecond, latencySum / numLatencyTests, cdnPop, 1.);
break;
}
// `value` is a Uint8Array for this chunk
loadedBytes += value.byteLength;
// Current time
const now = performance.now();
const deltaMs = now - lastTimestamp;
if (deltaMs >= REPORT_INTERVAL_MS) {
// compute bytes downloaded since last report
const deltaBytes = loadedBytes - lastLoaded;
// convert ms to seconds
const deltaSeconds = deltaMs / 1000;
bytesPerSecond = deltaBytes / deltaSeconds;
// Invoke callback
const elapsedMs = performance.now() - startTime;
const stop = onProgress(elapsedMs, loadedBytes, totalBytes, bytesPerSecond, false);
if (stop) {
// stop the test
console.log(`Stopping bandwidth test at ${loadedBytes} bytes after ${elapsedMs} ms`);
break;
}
// Reset our “last” markers
lastLoaded = loadedBytes;
lastTimestamp = now;
}
}
}
interface ClientInfo {
clientIp: string;
clientLocation: string;
}
export async function getClientInfo(): Promise<ClientInfo> {
let clientIp = 'Detecting...';
let clientLocation = 'Detecting...';
const res = await fetch('https://ipapi.co/json/');
clientIp = 'Not available';
clientLocation = 'Not available';
if (res.ok) {
const data = await res.json();
clientIp = data.ip || 'Unknown';
let location = '';
if (data.city) location += data.city + ', ';
if (data.region) location += data.region + ', ';
if (data.country_name) location += data.country_name;
clientLocation = location || 'Unknown';
}
return new Promise((resolve) =>
resolve({
clientIp,
clientLocation
})
);
}
export async function sendAnalyticsData(bytesPerSecond: number, latency: number, location: string, progress: number) {
// send measurements to analytics API
const measurements = {
bandwidth: bytesPerSecond,
latency,
location,
progress
};
console.log('Sending analytics data');
return new Promise((resolve) => {
fetch('/analytics', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(measurements)
})
.then((response) => {
if (!response.ok) {
throw new Error(`Network response was not ok: ${response.status}`);
}
resolve(response);
})
.catch((error) => {
console.error('Error sending bandwidth data:', error);
resolve(null);
});
});
}