Honor Retry-After on HTTP 429 (polite rest, not a failure)
Per Codex's spec — a publisher saying "slow down" shouldn't make a feed look broken, but repeated 429s stay visible via last_success_at / stale-source. * Schema: sources.retry_after_at (nullable) + migration. * feeds.parse_retry_after: delta-seconds OR HTTP-date → UTC stamp; ignores invalid/negative/past; caps at now + MAX_BACKOFF_MINUTES. * fetch_feed raises RateLimited (carrying the parsed time) on a 429. * poll_source: on 429 set retry_after_at + last_error, status='rate_limited', and do NOT increment consecutive_failures; on success clear retry_after_at; non-429 failures unchanged. * due_source_rows requires BOTH the streak backoff elapsed AND retry_after_at passed (i.e. the later of the two). * Admin: source_health returns retry_after_at; status reads "rate-limited · rests until …" rather than "failed/resting". Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -64,6 +64,12 @@
|
||||
let sources = $derived(stats?.sources ?? []);
|
||||
// status falls back to the legacy active flag during the migration window.
|
||||
const sstatus = (s) => s.status || (s.active ? 'active' : 'paused');
|
||||
// A 429 rest window that hasn't elapsed — polite wait, not breakage.
|
||||
function rateLimited(s) {
|
||||
if (!s.retry_after_at) return false;
|
||||
const d = new Date(s.retry_after_at.replace(' ', 'T') + 'Z');
|
||||
return !isNaN(d) && d > new Date();
|
||||
}
|
||||
let healthy = $derived(sources.filter((s) => sstatus(s) === 'active' && !s.failures && !s.review_flag).length);
|
||||
let resting = $derived(sources.filter((s) => sstatus(s) === 'active' && s.failures > 0).length);
|
||||
let flagged = $derived(sources.filter((s) => s.review_flag).length);
|
||||
@@ -460,6 +466,7 @@
|
||||
<td class="status">
|
||||
{#if st === 'retired'}<span class="paustxt">retired</span>
|
||||
{:else if st === 'paused'}<span class="paustxt">paused</span>
|
||||
{:else if rateLimited(s)}<span class="bad" title={s.last_error || ''}>rate-limited · rests until {fwhen(s.retry_after_at)}{#if s.review_flag} · review{/if}</span>
|
||||
{:else if s.failures > 0}<span class="bad" title={s.last_error || ''}>⚠ resting{#if s.review_flag} · review{/if}</span>
|
||||
{:else if s.review_flag}<span class="flagtxt">⚑ review</span>
|
||||
{:else}<span class="good">✓ ok</span>{/if}
|
||||
|
||||
Reference in New Issue
Block a user