{"id":50248743,"url":"https://github.com/shinagawa-web/pgincident","last_synced_at":"2026-05-27T00:06:26.835Z","repository":{"id":355795665,"uuid":"1229393677","full_name":"shinagawa-web/pgincident","owner":"shinagawa-web","description":"A live terminal dashboard for the first 30 seconds of a PostgreSQL incident — connections, locks, long queries, and idle transactions at a glance.","archived":false,"fork":false,"pushed_at":"2026-05-25T09:56:30.000Z","size":6908,"stargazers_count":0,"open_issues_count":9,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-25T11:24:51.297Z","etag":null,"topics":["bubbletea","cli","devops","go","incident-response","postgres","postgresql","sre","terminal","tui"],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/shinagawa-web.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-05T02:04:23.000Z","updated_at":"2026-05-25T09:38:17.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/shinagawa-web/pgincident","commit_stats":null,"previous_names":["shinagawa-web/pgincident"],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/shinagawa-web/pgincident","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shinagawa-web%2Fpgincident","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shinagawa-web%2Fpgincident/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shinagawa-web%2Fpgincident/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shinagawa-web%2Fpgincident/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/shinagawa-web","download_url":"https://codeload.github.com/shinagawa-web/pgincident/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shinagawa-web%2Fpgincident/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33544023,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"ssl_error","status_checked_at":"2026-05-26T15:22:15.568Z","response_time":63,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bubbletea","cli","devops","go","incident-response","postgres","postgresql","sre","terminal","tui"],"created_at":"2026-05-27T00:06:25.958Z","updated_at":"2026-05-27T00:06:26.829Z","avatar_url":"https://github.com/shinagawa-web.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# pgincident\n\n[![CI](https://github.com/shinagawa-web/pgincident/actions/workflows/ci.yml/badge.svg)](https://github.com/shinagawa-web/pgincident/actions/workflows/ci.yml)\n[![codecov](https://codecov.io/gh/shinagawa-web/pgincident/branch/main/graph/badge.svg)](https://codecov.io/gh/shinagawa-web/pgincident)\n\n\u003e \"The first 30 seconds of a Postgres incident — in one terminal.\"\n\nProduction Postgres is slow. You open psql and start firing queries — `pg_stat_activity`, `pg_locks`, `pg_stat_statements` — each in a separate window, refreshed by hand. By the time you've pieced together what's happening, the incident is already a minute old.\n\n**pgincident** collapses that into a live TUI: a global health overview to spot the problem, then a per-category incident dashboard to dig in.\n\n→ **[Usage guide](docs/usage.md)** — startup, screens, key bindings\n\n## 1. Positioning\n\nTargets SREs and Web engineers who reach for `psql -c \"SELECT * FROM pg_stat_activity\"` when production gets slow. Opens with a global health overview screen, then lets you drill into per-category incident views — replacing a sequence of manual queries with a two-screen live TUI.\n\n## 2. v0.1 Feature Scope\n\n### 2.1 Single-screen Incident Dashboard\n\n```\npgincident v0.1.0              connected: 10.0.1.42:5432 (PG 16.1)  interval: 5.0s\nConnections: 142/200 (71%)   TPS: 2340   Cache hit: 99.2%\n─────────────────────────────────────────────────────────────────────────────────\nLong-running queries (\u003e 5s)                                         [12 active]\n  PID     USER           DURATION      STATE        QUERY\n▸ 12345  app_user    00:02:14.32  active   SELECT u.* FROM users u JOIN…\n  12346  worker      00:00:18.04  active   UPDATE jobs SET status=...\n─────────────────────────────────────────────────────────────────────────────────\nLocks (waiting)                                                      [3 waiting]\n  BLOCKED  BLOCKING   WAIT TIME     RELATION             MODE\n  12350    12345     00:01:23.10  public.users   ShareLock\n─────────────────────────────────────────────────────────────────────────────────\nIdle in transaction (\u003e 30s)                                            [2 idle]\n  PID     USER           IDLE TIME     LAST QUERY\n  12348  worker      00:01:45.22  UPDATE jobs SET status=...\n─────────────────────────────────────────────────────────────────────────────────\n[q]uit  [Tab]section  [↑↓/jk]cursor  [+/-]interval  [?]help\n```\n\n### 2.2 Five core elements\n\n| # | Element | Source | Notes |\n|---|---|---|---|\n| 1 | Header (connections / TPS / cache hit) | `pg_stat_database`, `pg_stat_activity` | TPS = delta of xact_commit + xact_rollback per interval |\n| 2 | Long-running queries | `pg_stat_activity` | filter: state='active' AND duration \u003e threshold (default 5s) |\n| 3 | Locks | `pg_locks` JOIN `pg_stat_activity` | blocked-blocking pairs |\n| 4 | Idle in transaction | `pg_stat_activity` | filter: state='idle in transaction' AND duration \u003e threshold (default 30s) |\n| 5 | Key bindings | (in-app) | `q`, `Tab`, `↑↓/jk`, `+/-`, `?` |\n\n### 2.3 Out of scope for v0.1 (deferred)\n\n- `pg_stat_statements` integration (v0.2)\n- Investigate mode / drill-down (v0.3)\n- Replication monitoring, log tailing (v0.4)\n- Snapshot recording (v0.5)\n- Autovacuum / wraparound detection (v0.6)\n- Post-mortem export (v0.7)\n- Snapshot replay, Azure/Neon/Supabase (v1.0)\n- Web UI (v2.0)\n\n## 3. Non-goals\n\n- Replacing pgAdmin / DBeaver (no schema browsing, no query editor)\n- Long-term metrics storage (Prometheus, Grafana already do this)\n- Replication monitoring (later version)\n- System stats (CPU/IO/mem) — pgcenter does this; we focus on Postgres internals\n- Multi-instance dashboard (one connection at a time)\n\n## 4. SQL Catalog\n\nSee `SQL_CATALOG.md` for the candidate SQL per metric, version notes, and verification status (✅ tested / ⚠️ untested / ❌ broken on PG X).\n\n## 5. Update Loop\n\n- Default interval: 5 seconds. Adjustable with `+` / `-` (minimum 1s).\n- Poller runs in a background goroutine, sends `PollResult` to TUI via channel. TUI never blocks on DB.\n- Uses `time.NewTimer` (not `time.After`) to avoid timer leaks.\n- TPS skipped when `XactTotal` goes backward (server restart / `pg_stat_reset`).\n\n### DB load\n\nAll polled views (`pg_stat_activity`, `pg_locks`, `pg_stat_database`) read from shared memory with no disk I/O. Each query typically completes in \u003c 1ms; total overhead is a few ms/s with negligible CPU impact (\u003c 0.1%). A single persistent connection is reused — no per-poll connection cost.\n\nNote: `pg_stat_statements` (v0.2) can be heavier on systems with many unique queries. Consider polling it at a longer interval or making it opt-in.\n\n## 6. Error Handling\n\n| Category | Example | UX |\n|---|---|---|\n| Startup error | wrong DSN, can't connect | print to stderr, exit 1 |\n| Permission error | not member of `pg_monitor` | print explanation + grant command, exit 1 |\n| Transient runtime error | lost connection mid-poll | error banner in status bar |\n\n`pg_monitor` membership is checked at startup. If the user is not a member, the tool exits with an actionable message.\n\n## 7. Testing\n\n- **Unit tests** (`internal/core/`, `internal/tui/`) — pure Go logic: formatters, poller math, TUI rendering (golden files + interaction tests with stub data). No DB required. 100% statement coverage enforced by the pre-push hook.\n- **Integration tests** (`internal/core/integration_test.go`) — real Postgres via `DATABASE_URL`.\n- **CI** — GitHub Actions jobs:\n  - *Unit tests*: `go test -race -coverprofile` on every push/PR — covers core logic and TUI rendering (golden files + interaction tests); coverage uploaded to Codecov.\n  - *Integration tests*: Postgres 14 / 15 / 16 / 17 matrix.\n\n## 8. UX Details\n\n### 8.1 Three-level design (target architecture)\n\n\u003e v0.1 ships a single dashboard screen (Level 2 entry point). Level 1 overview shipped in v0.1.3; full Level 3 investigation planned for v0.3.\n\n- **Level 1 — Overview** *(shipped v0.1.3)* — Global DB health at a glance. Key metrics with status colors (normal / warning / critical). If something is red, drill into Level 2.\n- **Level 2 — Category view** — Per-category lists: Activity / Locks / I/O / Statements / Tables / Vacuum / Replication / Connections. *(v0.1 ships Activity, Locks, Idle in transaction)*\n- **Level 3 — Process view** (v0.3+) — Extends `Enter` into a full investigation: wait events, lock chain, cancel/kill. Currently `Enter` opens the query detail overlay (v0.1.2+).\n\n### 8.2 Layout constraints\n\n- **Minimum supported size**: 80 columns × 24 rows.\n- Below minimum: warning screen instead of broken layout.\n- Above minimum: each section gets roughly 1/3 of the body area.\n\n### 8.3 Key bindings\n\nSee [docs/usage.md](docs/usage.md) for the full key binding reference per screen.\n\n## Why pgincident?\n\n| | pgcenter | pg_activity | pgincident |\n|---|---|---|---|\n| Language | Go | Python | Go |\n| OS | Linux only | Linux/Mac | Linux + Mac |\n| Required privilege | SUPERUSER | SUPERUSER | **`pg_monitor`** |\n| Managed DB (RDS, Cloud SQL) | partial | partial | **first-class** |\n| Focus | comprehensive stats | top-style activity | **incident response + investigation** |\n| Post-mortem export | no | no | planned (v0.7) |\n\nThree key decisions behind this tool:\n\n1. **`pg_monitor` instead of SUPERUSER** — unlocks managed PostgreSQL (RDS, Cloud SQL, Aurora).\n2. **Incident-response framing** — not \"show me everything\", but \"what's broken right now.\"\n3. **overview → category → process flow** — global health first, drill into the problem area, then individual session investigation.\n\n## 9. Roadmap\n\nSee [issue #40](https://github.com/shinagawa-web/pgincident/issues/40) for the full roadmap.\n\n## Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for development setup, architecture, and how to simulate incident scenarios.\n\n## PostgreSQL configuration\n\npgincident reads the full query text from `pg_stat_activity.query`. PostgreSQL truncates this column at `track_activity_query_size` bytes (default: **1024**). With the default, long queries are cut off before they overflow the detail overlay, making the scroll feature useless in practice.\n\nRaise the limit to get the most out of the query detail overlay:\n\n```sql\n-- Check the current value\nSHOW track_activity_query_size;\n\n-- Apply permanently (requires superuser + server restart)\nALTER SYSTEM SET track_activity_query_size = 65536;\nSELECT pg_reload_conf(); -- not enough alone; a restart is required\n```\n\nFor the local dev container, the `docker-compose.yml` already sets `track_activity_query_size=65536`. On managed databases (RDS, Cloud SQL), set the parameter in the parameter group and reboot the instance.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshinagawa-web%2Fpgincident","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fshinagawa-web%2Fpgincident","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshinagawa-web%2Fpgincident/lists"}