CI: give long smoke probes one capped retry

Round two of bounding the retries: timeout>300s probes previously got a
single attempt, so a transient stall in the 600s JSON-mode probes still
failed on first occurrence. Give them one retry with the attempt timeout
capped at 300s. Worst cases stay inside timeout-minutes: 240s probes
12.5 min, one 600s probe 15.25 min, the Windows JSON job's two long
probes 30.5 min against its 35 minute budget.
This commit is contained in:
Daniel Han 2026-07-10 08:50:05 +00:00
commit e739dc1cca
3 changed files with 42 additions and 30 deletions

View file

@ -467,13 +467,15 @@ jobs:
},
)
# Shared CI runners stall sporadically, so retry transport-level
# failures only; HTTP status errors surface immediately. Long
# probes (timeout > 300s) get a single attempt so the worst
# case stays inside the job's timeout-minutes budget.
attempts = 3 if timeout <= 300 else 1
# failures only; HTTP status errors surface immediately. Bounded
# to fit the job's timeout-minutes: short probes get 3 full
# attempts, long probes one retry capped at 300s (a healthy
# server answers a retry quickly; a stalled one never does).
attempts = 3 if timeout <= 300 else 2
for attempt in range(attempts):
try:
with urllib.request.urlopen(req, timeout = timeout) as resp:
t = timeout if attempt == 0 else min(timeout, 300)
with urllib.request.urlopen(req, timeout = t) as resp:
return resp.status, json.loads(resp.read().decode())
except urllib.error.HTTPError:
raise
@ -975,13 +977,15 @@ jobs:
},
)
# Shared CI runners stall sporadically, so retry transport-level
# failures only; HTTP status errors surface immediately. Long
# probes (timeout > 300s) get a single attempt so the worst
# case stays inside the job's timeout-minutes budget.
attempts = 3 if timeout <= 300 else 1
# failures only; HTTP status errors surface immediately. Bounded
# to fit the job's timeout-minutes: short probes get 3 full
# attempts, long probes one retry capped at 300s (a healthy
# server answers a retry quickly; a stalled one never does).
attempts = 3 if timeout <= 300 else 2
for attempt in range(attempts):
try:
with urllib.request.urlopen(req, timeout = timeout) as resp:
t = timeout if attempt == 0 else min(timeout, 300)
with urllib.request.urlopen(req, timeout = t) as resp:
return resp.status, json.loads(resp.read().decode())
except urllib.error.HTTPError:
raise

View file

@ -453,13 +453,15 @@ jobs:
},
)
# Shared CI runners stall sporadically, so retry transport-level
# failures only; HTTP status errors surface immediately. Long
# probes (timeout > 300s) get a single attempt so the worst
# case stays inside the job's timeout-minutes budget.
attempts = 3 if timeout <= 300 else 1
# failures only; HTTP status errors surface immediately. Bounded
# to fit the job's timeout-minutes: short probes get 3 full
# attempts, long probes one retry capped at 300s (a healthy
# server answers a retry quickly; a stalled one never does).
attempts = 3 if timeout <= 300 else 2
for attempt in range(attempts):
try:
with urllib.request.urlopen(req, timeout = timeout) as resp:
t = timeout if attempt == 0 else min(timeout, 300)
with urllib.request.urlopen(req, timeout = t) as resp:
return resp.status, json.loads(resp.read().decode())
except urllib.error.HTTPError:
raise
@ -867,13 +869,15 @@ jobs:
},
)
# Shared CI runners stall sporadically, so retry transport-level
# failures only; HTTP status errors surface immediately. Long
# probes (timeout > 300s) get a single attempt so the worst
# case stays inside the job's timeout-minutes budget.
attempts = 3 if timeout <= 300 else 1
# failures only; HTTP status errors surface immediately. Bounded
# to fit the job's timeout-minutes: short probes get 3 full
# attempts, long probes one retry capped at 300s (a healthy
# server answers a retry quickly; a stalled one never does).
attempts = 3 if timeout <= 300 else 2
for attempt in range(attempts):
try:
with urllib.request.urlopen(req, timeout = timeout) as resp:
t = timeout if attempt == 0 else min(timeout, 300)
with urllib.request.urlopen(req, timeout = t) as resp:
return resp.status, json.loads(resp.read().decode())
except urllib.error.HTTPError:
raise

View file

@ -659,13 +659,15 @@ jobs:
},
)
# Shared CI runners stall sporadically, so retry transport-level
# failures only; HTTP status errors surface immediately. Long
# probes (timeout > 300s) get a single attempt so the worst
# case stays inside the job's timeout-minutes budget.
attempts = 3 if timeout <= 300 else 1
# failures only; HTTP status errors surface immediately. Bounded
# to fit the job's timeout-minutes: short probes get 3 full
# attempts, long probes one retry capped at 300s (a healthy
# server answers a retry quickly; a stalled one never does).
attempts = 3 if timeout <= 300 else 2
for attempt in range(attempts):
try:
with urllib.request.urlopen(req, timeout = timeout) as resp:
t = timeout if attempt == 0 else min(timeout, 300)
with urllib.request.urlopen(req, timeout = t) as resp:
return resp.status, json.loads(resp.read().decode())
except urllib.error.HTTPError:
raise
@ -1101,13 +1103,15 @@ jobs:
},
)
# Shared CI runners stall sporadically, so retry transport-level
# failures only; HTTP status errors surface immediately. Long
# probes (timeout > 300s) get a single attempt so the worst
# case stays inside the job's timeout-minutes budget.
attempts = 3 if timeout <= 300 else 1
# failures only; HTTP status errors surface immediately. Bounded
# to fit the job's timeout-minutes: short probes get 3 full
# attempts, long probes one retry capped at 300s (a healthy
# server answers a retry quickly; a stalled one never does).
attempts = 3 if timeout <= 300 else 2
for attempt in range(attempts):
try:
with urllib.request.urlopen(req, timeout = timeout) as resp:
t = timeout if attempt == 0 else min(timeout, 300)
with urllib.request.urlopen(req, timeout = t) as resp:
return resp.status, json.loads(resp.read().decode())
except urllib.error.HTTPError:
raise