A tap's span ends with its own requests' answers (#379)
traceparent keeps one trace for any request within a minute of the last, and the tap span ran to the last answer in that trace, so a live update's refresh 13 s later made a tap answered in 0.9 s read as 15 s (trace a895551d20626fb6dc071d336262b772). Now a request counts towards the tap only when one of the tap's own is in flight or it begins within two seconds of the tap or its last answer; it still joins the trace. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -105,8 +105,11 @@ function spanText(secs: number){
|
||||
/// trace, which then lasted nine hours (#219). A play's position saves, every ten seconds, keep it.
|
||||
/// A tap or a key is the trace's root span (#376): its requests name it as their parent, and once
|
||||
/// the next one begins, or the page is hidden, the page reports it, from the tap to its last
|
||||
/// answer, for ipx to send on to Tempo with these ids.
|
||||
let traceId='', traceAt=0, rootId='', rootAt=0, rootKind='', answeredAt=0;
|
||||
/// answer, for ipx to send on to Tempo with these ids. Only requests begun while the tap is still
|
||||
/// at work count towards it: one of its own in flight, or within two seconds of the tap or its last
|
||||
/// answer, as a request an answer sets off is. A live update's refresh thirteen seconds later joined
|
||||
/// the trace and made a tap answered in 0.9 s read as 15 (#379).
|
||||
let traceId='', traceAt=0, rootId='', rootAt=0, rootKind='', answeredAt=0, tapBusy=0;
|
||||
const hex=(n: number)=>Array.from(crypto.getRandomValues(new Uint8Array(n)),b=>b.toString(16).padStart(2,'0')).join('');
|
||||
function endTap(){
|
||||
const kind=rootKind;
|
||||
@@ -118,7 +121,7 @@ function endTap(){
|
||||
}
|
||||
function newTrace(kind: string){
|
||||
endTap();
|
||||
traceId=hex(16); rootId=hex(8); rootAt=Date.now(); rootKind=kind; answeredAt=0;
|
||||
traceId=hex(16); rootId=hex(8); rootAt=Date.now(); rootKind=kind; answeredAt=0; tapBusy=0;
|
||||
}
|
||||
for(const t of ['pointerdown','keydown']) window.addEventListener(t,()=>{ newTrace(t); traceAt=Date.now(); },{capture:true,passive:true});
|
||||
window.addEventListener('pagehide',endTap);
|
||||
@@ -149,10 +152,13 @@ window.addEventListener('unhandledrejection',e=>report('rejection',{msg:String(e
|
||||
async function api(url: string, opts?: RequestInit): Promise<any>{
|
||||
// The browser's "Failed to fetch" said nothing anyone could act on (#304).
|
||||
const tp=traceparent(), mine=traceId;
|
||||
const taps=!!rootKind&&(tapBusy>0||Date.now()-Math.max(rootAt,answeredAt)<2000);
|
||||
if(taps) tapBusy++;
|
||||
const r = await fetch(url,{headers:{'Content-Type':'application/json',traceparent:tp},...opts})
|
||||
.catch(()=>{ throw new Error('Unable to reach iPX. Check your connection and try again.'); });
|
||||
// The tap lasts until its last answer.
|
||||
if(mine===traceId) answeredAt=Date.now();
|
||||
.catch(()=>{ throw new Error('Unable to reach iPX. Check your connection and try again.'); })
|
||||
.finally(()=>{ if(taps&&mine===traceId) tapBusy--; });
|
||||
// The tap lasts until the last answer to what it set off.
|
||||
if(taps&&mine===traceId) answeredAt=Date.now();
|
||||
if(r.status===401){ location.href='/login'; throw new Error('signed out'); }
|
||||
if(!r.ok){
|
||||
// A page in place of ipx's words is Cloudflare or Caddy answering for it: the message became a
|
||||
|
||||
Reference in New Issue
Block a user