Retry Jitter

This commit is contained in:
Alexey
2026-10-31 14:23:01 +03:00
parent ed2139502f
commit 3df59cfa28
17 changed files with 342 additions and 5 deletions
+21 -3
View File
@@ -1,5 +1,21 @@
(()=>{'use strict';
function create(settings){
// Snapshot policy per page, including during session recovery.
const jitter=Object.freeze(Object.assign({},settings.jitter||{enabled:false}));
const randomWord=jitter.enabled?new Uint32Array(1):null;
function randomUnit(){
try{if(globalThis.crypto&&typeof globalThis.crypto.getRandomValues==='function'){
globalThis.crypto.getRandomValues(randomWord);return randomWord[0]/4294967296;
}}catch(error){}
return Math.random();
}
function jitterMs(){
const a=jitter.min,b=jitter.max;if(a===b)return a;
const u=randomUnit();
if(!jitter.triangular)return a+Math.floor(u*(b-a+1));
const c=jitter.peak,span=b-a;
return Math.round(u<(c-a)/span?a+Math.sqrt(u*span*(c-a)):b-Math.sqrt((1-u)*span*(b-c)));
}
const pause=(milliseconds,signal)=>new Promise((resolve,reject)=>{
if(signal&&signal.aborted){reject(new Error('request aborted'));return}
const timer=setTimeout(done,milliseconds);function done(){if(signal)signal.removeEventListener('abort',abort);resolve()}
@@ -15,10 +31,12 @@ function create(settings){
// Only the exact Telemt overload hint overrides its legacy whole-second fallback.
if(response.status===503&&header==='1'&&response.headers.get('X-Telemt-Retry-After-Ms')==='250')return 250;
if(!header)return 0;
// Enabled policies must never shorten a valid server minimum to the legacy 30 s cap.
if(jitter.enabled&&/^\d+$/.test(header))return Number(header)*1000;
const seconds=Number(header);
if(Number.isFinite(seconds)&&seconds>=0)return Math.min(seconds*1000,30000);
if(Number.isFinite(seconds)&&seconds>=0)return jitter.enabled?seconds*1000:Math.min(seconds*1000,30000);
const when=Date.parse(header);
if(Number.isFinite(when)){const delta=when-Date.now();return delta>0?Math.min(delta,30000):0}
if(Number.isFinite(when)){const delta=when-Date.now();return delta>0?(jitter.enabled?delta:Math.min(delta,30000)):0}
return 0;
}
function retryableStatus(status){return status===408||status===429||status===502||status===503||status===504}
@@ -65,7 +83,7 @@ function create(settings){
}finally{clearTimeout(timer);if(external)external.removeEventListener('abort',abort)}
const after=Math.min(deadline-Date.now(),remainingBudget?remainingBudget():Infinity);if(attempt>=attempts()||after<=0)break;
settings.retrying();
const backoff=wait||delay+Math.floor(Math.random()*Math.max(1,delay/4));
const backoff=jitter.enabled?(wait||delay)+jitterMs():wait||delay+Math.floor(Math.random()*Math.max(1,delay/4));
await pause(Math.min(backoff,after),external);delay=Math.min(delay*2,2000);
}
throw settings.failure(lastReason,'carrier retry limit reached');