mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-13 06:15:25 +00:00
The async webhook worker double-processed long-running agent calls: the 90s stale-running sweep reclaimed rows whose agent ran longer than 90s (no heartbeat), re-running the agent and duplicating MCP side-effects. Add a last_heartbeat_at column + a Heartbeat lease-renewal method (CAS on lease_token). While an agent runs, the worker renews the lease every 30s; ReclaimStale now keys off last_heartbeat_at instead of started_at, so a live run is never reclaimed while a dead worker is still recovered within the stale window. If a run loses its lease (reclaimed), the heartbeat cancels the run context so the agent stops immediately and writes no further side-effects. - migrations/000085 (PG) + SQLite schema v52 + RequiredSchemaVersion 85 - WebhookCallStore.Heartbeat (PG + SQLite impls); ClaimNext/ReclaimStale switched to last_heartbeat_at - worker heartbeat goroutine + cancel-on-lease-loss; invokeAgent honors ctx - store + worker tests
12 lines
674 B
SQL
12 lines
674 B
SQL
-- Heartbeat (lease renewal) column for async webhook call leases.
|
|
-- Lets a long-running agent renew its lease so ReclaimStale won't reclaim a live worker.
|
|
ALTER TABLE webhook_calls ADD COLUMN last_heartbeat_at timestamptz;
|
|
|
|
-- Backfill running rows so they are not reclaimed immediately after deploy
|
|
-- (their lease is still valid until staleRunningWindow elapses from started_at).
|
|
UPDATE webhook_calls SET last_heartbeat_at = started_at WHERE status = 'running';
|
|
|
|
-- Partial index: ReclaimStale scans only running rows by heartbeat.
|
|
CREATE INDEX IF NOT EXISTS idx_webhook_calls_running_heartbeat
|
|
ON webhook_calls (status, last_heartbeat_at) WHERE status = 'running';
|