From 9716886700adef7e7874cce32cf9056a8a4200a3 Mon Sep 17 00:00:00 2001 From: jzzliu Date: Sat, 7 Nov 2026 21:14:51 -0700 Subject: [PATCH] Simplify Relay Lab dashboard and retry handling --- public/app.js | 45 ++++++++++++++++++++++++++------------------- public/index.html | 8 ++++---- queue_store.py | 6 ++++-- 3 files changed, 34 insertions(+), 25 deletions(-) diff --git a/public/app.js b/public/app.js index 356724f..28d6c65 100644 --- a/public/app.js +++ b/public/app.js @@ -1,6 +1,7 @@ 'use strict'; const states = ['queued', 'running', 'delivered', 'dead']; +const pollInterval = 750; const byId = (id) => document.getElementById(id); let lastRequest; let sending = false; @@ -15,16 +16,19 @@ function renderList(id, items, toRow, emptyMessage) { byId(id).replaceChildren(...(items.length ? items.map(toRow) : [element('li', emptyMessage)])); } +function renderPipeline({ queued = 0, running = 0, delivered = 0, dead = 0 }) { + byId('pipeline-queued').textContent = `${queued} waiting`; + byId('pipeline-running').textContent = `${running} active / 2 workers`; + byId('pipeline-outcome').textContent = `${delivered} delivered / ${dead} dead-lettered`; + byId('pipeline').dataset.active = String(running > 0); +} + function render(state) { const latest = state.events[0]; byId('backend-event').textContent = latest ? `${latest.job_id.slice(0, 8)} · ${latest.message}` : 'No queue events yet.'; - const { queued = 0, running = 0, delivered = 0, dead = 0 } = state.counts; - byId('pipeline-queued').textContent = `${queued} waiting`; - byId('pipeline-running').textContent = `${running} active / 2 workers`; - byId('pipeline-outcome').textContent = `${delivered} delivered / ${dead} dead-lettered`; - byId('pipeline').dataset.active = String(running > 0); + renderPipeline(state.counts); byId('metrics').replaceChildren(...states.map((name) => { const cell = element('div', ''); cell.dataset.state = name; @@ -66,45 +70,48 @@ async function poll() { connection.textContent = online ? 'Queue connected' : 'Queue disconnected'; connection.dataset.online = String(online); } - setTimeout(poll, 750); + setTimeout(poll, pollInterval); } } async function submit(request, button) { if (sending) return; sending = true; - byId('backend-request').textContent = 'Sending request…'; - if (!window.matchMedia('(prefers-reduced-motion: reduce)').matches) { - button.animate( - [{ transform: 'scale(.96)', boxShadow: '0 0 0 0 #bd93f9aa' }, - { transform: 'scale(1)', boxShadow: '0 0 0 8px #bd93f900' }], - { duration: 320, easing: 'ease-out' }, - ); - } + const inspector = byId('backend-request'); + inspector.textContent = 'Sending request…'; + let responseSummary = 'No API response available.'; try { + if (!window.matchMedia('(prefers-reduced-motion: reduce)').matches) { + button.animate?.([ + { transform: 'scale(.96)', boxShadow: '0 0 0 0 #bd93f9aa' }, + { transform: 'scale(1)', boxShadow: '0 0 0 8px #bd93f900' }, + ], { duration: 320, easing: 'ease-out' }); + } const response = await fetch('/api/jobs', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(request), }); + responseSummary = `HTTP ${response.status} · Response was not valid JSON.`; const result = await response.json(); - byId('backend-request').textContent = `HTTP ${response.status} · ${JSON.stringify(result)}`; + responseSummary = `HTTP ${response.status} · ${JSON.stringify(result)}`; if (!response.ok) throw new Error(result.error || 'Request failed.'); lastRequest = request; byId('repeat').disabled = false; byId('status').textContent = `${result.created ? 'Queued job' : 'Reused job'} ${result.id.slice(0, 8)}.`; } catch (error) { - if (byId('backend-request').textContent === 'Sending request…') { - byId('backend-request').textContent = 'No API response available.'; - } byId('status').textContent = error.message; } finally { + inspector.textContent = responseSummary; sending = false; } } for (const button of document.querySelectorAll('[data-scenario]')) { - button.addEventListener('click', () => submit({ key: crypto.randomUUID(), scenario: button.dataset.scenario }, button)); + button.addEventListener('click', () => { + const request = { key: crypto.randomUUID(), scenario: button.dataset.scenario }; + submit(request, button); + }); } byId('repeat').addEventListener('click', (event) => submit(lastRequest, event.currentTarget)); poll(); diff --git a/public/index.html b/public/index.html index dc11d87..ae5205f 100644 --- a/public/index.html +++ b/public/index.html @@ -12,10 +12,10 @@
-

01 / RELIABILITY ENGINEERING

-

Delivery
under failure.

-

Run an event through a durable queue. Watch two workers compete for jobs, retry failed deliveries, and recover abandoned work.

-
SQLite WAL2 workersAt-least-once attempts
+

01 / RELIABILITY ENGINEERING

+

Delivery
under failure.

+

Run an event through a durable queue. Watch two workers compete for jobs, retry failed deliveries, and recover abandoned work.

+
SQLite WAL2 workersAt-least-once attempts
FIG. 01 / DELIVERY PIPELINELIVE
diff --git a/queue_store.py b/queue_store.py index 3db7bbf..362b0bd 100644 --- a/queue_store.py +++ b/queue_store.py @@ -4,6 +4,8 @@ import sqlite3 from contextlib import contextmanager from uuid import uuid4 +MAX_ATTEMPTS = 3 + class Conflict(Exception): """An idempotency key was reused for a different request.""" @@ -77,7 +79,7 @@ class QueueStore: (now,), ).fetchall() for row in expired: - state = "dead" if row["attempts"] >= 3 else "queued" + state = "dead" if row["attempts"] >= MAX_ATTEMPTS else "queued" db.execute(""" UPDATE jobs SET state = ?, available_at = ?, lease_token = NULL, lease_until = NULL WHERE id = ? @@ -104,7 +106,7 @@ class QueueStore: delay = 2 ** attempt if success: state, message = "delivered", "Consumer acknowledged the event" - elif attempt >= 3: + elif attempt >= MAX_ATTEMPTS: state, message = "dead", "Consumer failed · attempts exhausted" else: state, message = "queued", f"Consumer failed · retry in {delay}s"