feat(zulip): v3 resilience rewrite — circuit breaker, retry, watchdog, model fix
Root cause: Workers hung because default model was DeepSeek v4-pro (reasoning model that produces empty content with normal token limits). pi RPC workers waited forever for content that never arrived. Changes: - Circuit breaker (CLOSED→OPEN→HALF_OPEN) around Zulip API calls - Retry with exponential backoff + jitter for transient errors - Queue lifecycle management (idle_queue_timeout, auto re-register on BAD_EVENT_QUEUE_ID) - External supervisor watchdog (restarts router after 3 health check failures) - Busy worker timeout (SIGKILL after 5 min + error DM) - PM2 hardening (max_restarts=100, max_memory_restart=500M) - Crash handlers (uncaughtException + unhandledRejection → reconnect, not die) - Fixed default model: deepseek-v4-pro → syslog-harness/syslog-auto - Enhanced health endpoint with circuit breaker stats Architecture: Research-backed from Zulip event system docs + Node.js resilience patterns. Inline circuit breaker (no dependency). Separate watchdog process (Hermes pattern). Verified: Circuit breaker trips on outage, recovers gracefully. End-to-end DM processed in 4 seconds. Watchdog monitoring every 30s.
This commit is contained in:
@@ -1,26 +1,76 @@
|
||||
/**
|
||||
* PM2 Ecosystem Config — Zulip Gateway v3 (Resilience)
|
||||
*
|
||||
* Deploy: pm2 start /root/.pm2/ecosystem.config.cjs
|
||||
* Status: pm2 status
|
||||
* Logs: pm2 logs abiba-zulip
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
apps: [
|
||||
{
|
||||
// ── Router (main Zulip gateway) ──
|
||||
name: "abiba-zulip",
|
||||
script: "/usr/bin/pi",
|
||||
script: "/bin/pi",
|
||||
args: "--mode rpc --session-id zulip-service",
|
||||
cwd: "/root",
|
||||
|
||||
// Resilience hardening — up from pi defaults
|
||||
max_restarts: 100, // Crash loops won't exhaust PM2 (was default 10)
|
||||
min_uptime: "10s", // Must survive 10s to count as "alive"
|
||||
max_memory_restart: "500M", // OOM protection — restart before swap thrash
|
||||
restart_delay: 5000, // 5s cooldown between restarts
|
||||
kill_timeout: 15000, // 15s SIGTERM grace before SIGKILL
|
||||
listen_timeout: 30000, // 30s to bind health port
|
||||
|
||||
// Logging
|
||||
log_date_format: "YYYY-MM-DD HH:mm:ss Z",
|
||||
error_file: "/root/.pm2/logs/abiba-zulip-error.log",
|
||||
out_file: "/root/.pm2/logs/abiba-zulip-out.log",
|
||||
merge_logs: true,
|
||||
log_type: "json",
|
||||
|
||||
// Process management
|
||||
autorestart: true,
|
||||
watch: false,
|
||||
instances: 1,
|
||||
exec_mode: "fork",
|
||||
|
||||
// Environment
|
||||
env: {
|
||||
ZULIP_ROLE: "router",
|
||||
ZULIP_EXTENSION_ACTIVE: "true",
|
||||
NODE_OPTIONS: "--max-old-space-size=512",
|
||||
ZULIP_SITE: "https://chat.sysloggh.net",
|
||||
ZULIP_EMAIL: "abiba-bot@chat.sysloggh.net",
|
||||
ZULIP_API_KEY: "cKTDMZAPW08dk3zl05sStzO7HRztzyn8",
|
||||
AGENT_NAME: "abiba",
|
||||
AGENT_OWNER_EMAIL: "jerome@sysloggh.com",
|
||||
NODE_ENV: "production",
|
||||
},
|
||||
// Prevent rapid crash-looping: restart with backoff, limit retries
|
||||
min_uptime: "30s",
|
||||
max_restarts: 15,
|
||||
restart_delay: 10000,
|
||||
kill_timeout: 5000,
|
||||
autorestart: true,
|
||||
},
|
||||
{
|
||||
name: "abiba-telegram",
|
||||
script: "/usr/bin/pitg",
|
||||
// ── Supervisor (external watchdog) ──
|
||||
name: "zulip-watchdog",
|
||||
script: "/root/.pi/agent/extensions/zulip/watchdog.js",
|
||||
cwd: "/root",
|
||||
|
||||
max_restarts: 10,
|
||||
min_uptime: "3s",
|
||||
restart_delay: 3000,
|
||||
kill_timeout: 5000,
|
||||
|
||||
log_date_format: "YYYY-MM-DD HH:mm:ss Z",
|
||||
error_file: "/root/.pm2/logs/zulip-watchdog-error.log",
|
||||
out_file: "/root/.pm2/logs/zulip-watchdog-out.log",
|
||||
merge_logs: true,
|
||||
|
||||
autorestart: true,
|
||||
watch: false,
|
||||
instances: 1,
|
||||
exec_mode: "fork",
|
||||
|
||||
env: {
|
||||
NODE_ENV: "production",
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user